Reputation: 237
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
Upvotes: 6
Views: 3109
Reputation: 5052
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);
}
}
Upvotes: 4
Reputation: 76639
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.
Upvotes: 3