Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test provider for mock location not working on Android 10

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

like image 277
felipe.rce Avatar asked Nov 30 '20 03:11

felipe.rce


People also ask

Is location from mock provider?

On Android 18 (JellyBean MR2) and above mock locations are detected using Location. isFromMockProvider() for each location. The app can detect that the location came from a mock provider when the API returns true.

How to allow mock locations on Android?

You can go to its Settings > Software Information and tap on the Build Number 7 times to turn on Developer Options. Afterward, you can go to Settings > Developer Options > Mock Location App and select any fake GPS application from here. There you go! After reading this guide, you would be able to allow mock locations on Android pretty easily.

How do I mock a location on my Xiaomi Phone?

To mock location on Xiaomi Most of the Xiaomi devices have a layer of the company’s interface over Android, which is known as MIUI. Instead of the Build Number, you need to tap on the MIUI version under Settings > About Phone to unlock Developer Options.

How can I prevent mock locations from reaching the HyperTrack API?

Solution: The HyperTrack SDK filters out mock locations on the device in order to prevent them from reaching the HyperTrack API server. HyperTrack removes Mock Locations by default.

How to allow mock locations on MIUI devices?

Instead of the Build Number, you need to tap on the MIUI version under Settings > About Phone to unlock Developer Options. Later, you can go to the Developer Options settings and turn on the feature for “Allow Mock Locations”.


Video Answer


2 Answers

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);
    }
}
like image 82
blackkara Avatar answered Oct 25 '22 13:10

blackkara


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.

like image 3
Martin Zeitler Avatar answered Oct 25 '22 12:10

Martin Zeitler