6

Mock location are not working on Android 10, crash when addTestProvider are called:

2020-11-30 00:25:16.855 13189-13256/br.com.tupinikimtecnologia.fakegpslocation E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-2
    Process: br.com.tupinikimtecnologia.fakegpslocation, PID: 13189
    java.lang.IllegalArgumentException: Provider "gps" already exists
        at android.os.Parcel.createException(Parcel.java:2075)
        at android.os.Parcel.readException(Parcel.java:2039)
        at android.os.Parcel.readException(Parcel.java:1987)
        at android.location.ILocationManager$Stub$Proxy.addTestProvider(ILocationManager.java:2022)
        at android.location.LocationManager.addTestProvider(LocationManager.java:1461)
        at br.com.tupinikimtecnologia.fakegpslocation.feature.mock.view.MapsActivity.setMock(MapsActivity.kt:100)
        at br.com.tupinikimtecnologia.fakegpslocation.feature.mock.view.MapsActivity.access$setMock(MapsActivity.kt:34)
        at br.com.tupinikimtecnologia.fakegpslocation.feature.mock.view.MapsActivity$onCreate$1.invokeSuspend(MapsActivity.kt:65)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
     Caused by: android.os.RemoteException: Remote stack trace:
        at com.android.server.LocationManagerService.addTestProvider(LocationManagerService.java:3536)
        at android.location.ILocationManager$Stub.onTransact(ILocationManager.java:958)
        at android.os.Binder.execTransactInternal(Binder.java:1021)
        at android.os.Binder.execTransact(Binder.java:994)
2020-11-30 00:25:16.857 13189-13256/br.com.tupinikimtecnologia.fakegpslocation I/Process: Sending signal. PID: 13189 SIG: 9

Code:

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_maps)

        val mapFragment = supportFragmentManager
                .findFragmentById(R.id.map) as SupportMapFragment
        mapFragment.getMapAsync(this)

        ActivityCompat.requestPermissions(this, arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION), ACCESS_LOCATION_CODE)


        mLocationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager

        if (isMockLocationEnabled()) {
            GlobalScope.launch {
                while (true) {
                    setMock(LocationManager.GPS_PROVIDER, 39.0293211, 125.6020307);
                    setMock(LocationManager.NETWORK_PROVIDER, 39.0293211, 125.6020307);
                }
            }
        } else {
            AlertDialog.Builder(this)
                    .setTitle(R.string.dev_settings_title_dialog)
                    .setMessage(R.string.dev_settings_msg_dialog)
                    .setPositiveButton(android.R.string.ok) { dialog, which ->
                        Toast.makeText(this, R.string.dev_settings_msg_toast, Toast.LENGTH_LONG).show()
                        startActivity(Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS))
                    }
                    .show()

        }

    }
    

    private fun isMockLocationEnabled(): Boolean {
        val isMockLocation: Boolean
        isMockLocation = try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                val opsManager = getSystemService(APP_OPS_SERVICE) as AppOpsManager
                Objects.requireNonNull(opsManager).checkOp(AppOpsManager.OPSTR_MOCK_LOCATION, Process.myUid(), BuildConfig.APPLICATION_ID) === AppOpsManager.MODE_ALLOWED
            } else {
                Settings.Secure.getString(contentResolver, "mock_location") != "0"
            }
        } catch (e: Exception) {
            return false
        }
        return isMockLocation
    }

    private fun setMock(provider: String, latitude: Double, longitude: Double) {
        mLocationManager?.addTestProvider(
                provider,
                false,
                false,
                false,
                false,
                false,
                true,
                true,
                android.location.Criteria.POWER_LOW,
                android.location.Criteria.ACCURACY_FINE
        )

        val newLocation = Location(provider)
        newLocation.latitude = latitude
        newLocation.longitude = longitude
        newLocation.altitude = 3.0
        newLocation.time = System.currentTimeMillis()
        newLocation.speed = 0.01f
        newLocation.bearing = 1f
        newLocation.accuracy = 3f
        newLocation.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            newLocation.bearingAccuracyDegrees = 0.1f
            newLocation.verticalAccuracyMeters = 0.1f
            newLocation.speedAccuracyMetersPerSecond = 0.01f
        }
        mLocationManager?.setTestProviderEnabled(provider, true)
        mLocationManager?.setTestProviderLocation(provider, newLocation)
    }

Crash on:

mLocationManager?.addTestProvider(
                provider,
                false,
                false,
                false,
                false,
                false,
                true,
                true,
                android.location.Criteria.POWER_LOW,
                android.location.Criteria.ACCURACY_FINE
        )

It is crashing only on Android 10, I tried to change the provider name, but it don't work, all names are crashing. I think it is not a permission issue, because I tested with ACCESS_MOCK_LOCATION, ACCESS_FINE_LOCATION and INTERNET enabled

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
felipe.rce
  • 237
  • 2
  • 8
  • 35

2 Answers2

4

The first thing, when you add test provider (i.e "my_provider"), it's actually interpreted real-like provider as "gps", but for test purposes. So if you ask to android, for locations sources, then it could tell you;

"gps", "network", "some_provider"

So, why you got exception ?

Actually you are already tend to get exceptions, but it's handled by the system. If you try to add already known provider like "gps" as test provider, then the system tries to add it into location sources. So it's better to wrap mocking operations for known providers with try catch

To mock gps provider without exception, try this

  public void mockGps(Location location) throws SecurityException {
    location.setProvider(GPS_PROVIDER);
    try{
        // @throws IllegalArgumentException if a provider with the given name already exists
        mLocationManager.addTestProvider(GPS_PROVIDER, false, false, false, false, false, true, true, 0, 5);
    }  catch (IllegalArgumentException ignored){}

    try{
        // @throws IllegalArgumentException if no provider with the given name exists
        mLocationManager.setTestProviderEnabled(GPS_PROVIDER, true);
    } catch (IllegalArgumentException ignored){
        mLocationManager.addTestProvider(GPS_PROVIDER, false, false, false, false, false, true, true, 0, 5);
    }

    try{
        // @throws IllegalArgumentException if no provider with the given name exists
        mLocationManager.setTestProviderLocation(GPS_PROVIDER, location);
    } catch (IllegalArgumentException ignored){
        mLocationManager.addTestProvider(GPS_PROVIDER, false, false, false, false, false, true, true, 0, 5);
        mLocationManager.setTestProviderEnabled(GPS_PROVIDER, true);
        mLocationManager.setTestProviderLocation(GPS_PROVIDER, location);
    }
}
blackkara
  • 4,900
  • 4
  • 28
  • 58
  • It worked, but I changed the catch by adding mLocationManager?.removeTestProvider(provider) – felipe.rce Jan 18 '21 at 23:59
  • Adding mLocationManager?.removeTestProvider(provider) on Catch and call mock location again are working, I don't understand why it is not working if I try to removeTestProvider before setProvider – felipe.rce Jan 19 '21 at 00:00
3

This is not really coming from the mocking, but the LocationManager's .addTestProvider(). Adding the same one test-provider twice will in every case not work out, according to the source code. For later API levels, just use .setTestProviderLocation(String provider, Location loc) instead of trying to add that mocked provider with the duplicate name; or at least remove the existing one, before trying to add another one with the same name.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • clearTestProviderLocation are deprecated, I tested but it won't work. Location from .setTestProviderLocation(String provider, Location loc) are not working because there is not loc.isComplete() and makeComplete(), I think it is too old and is removed – felipe.rce Jan 15 '21 at 02:07
  • You linked some googlesource code, I just say that this method are deprecated. I don't understand why try to ofend me about my knowlegment... – felipe.rce Jan 18 '21 at 23:54
  • Yes, it is possible to remove a provider before adding it. But also in try-catch block, because it can be absent in location. – CoolMind Feb 14 '23 at 14:45