模拟位置的测试提供商无法在 Android 10 上工作
Test provider for mock location not working on Android 10
模拟位置在 Android10 上不起作用,调用 addTestProvider 时崩溃:
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.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
代码:
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)
}
崩溃时间:
mLocationManager?.addTestProvider(
provider,
false,
false,
false,
false,
false,
true,
true,
android.location.Criteria.POWER_LOW,
android.location.Criteria.ACCURACY_FINE
)
它仅在 Android 10 时崩溃,我尝试更改提供商名称,但没有用,所有名称都在崩溃。
我认为这不是权限问题,因为我在 ACCESS_MOCK_LOCATION、ACCESS_FINE_LOCATION 和启用 INTERNET 的情况下进行了测试
这并不是真正来自模拟,而是 LocationManager
的 .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)
而不是尝试添加具有重复名称的模拟提供者;或者至少删除现有的,然后再尝试添加另一个同名的。
首先,当您添加测试提供者(即“my_provider”)时,它实际上会将真实提供者解释为“gps”,但用于测试目的。因此,如果您向 android 询问位置来源,那么它可以告诉您;
"gps"
、"network"
、"some_provider"
那么,为什么会出现异常?
实际上你已经倾向于得到异常,但它是由系统处理的。如果您尝试添加像 "gps"
这样的已知提供程序作为测试提供程序,那么系统会尝试将其添加到位置源中。所以最好用 try catch
包装已知提供者的模拟操作
要无一例外地模拟 gps 提供商,试试这个
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);
}
}
模拟位置在 Android10 上不起作用,调用 addTestProvider 时崩溃:
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.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
代码:
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)
}
崩溃时间:
mLocationManager?.addTestProvider(
provider,
false,
false,
false,
false,
false,
true,
true,
android.location.Criteria.POWER_LOW,
android.location.Criteria.ACCURACY_FINE
)
它仅在 Android 10 时崩溃,我尝试更改提供商名称,但没有用,所有名称都在崩溃。 我认为这不是权限问题,因为我在 ACCESS_MOCK_LOCATION、ACCESS_FINE_LOCATION 和启用 INTERNET 的情况下进行了测试
这并不是真正来自模拟,而是 LocationManager
的 .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)
而不是尝试添加具有重复名称的模拟提供者;或者至少删除现有的,然后再尝试添加另一个同名的。
首先,当您添加测试提供者(即“my_provider”)时,它实际上会将真实提供者解释为“gps”,但用于测试目的。因此,如果您向 android 询问位置来源,那么它可以告诉您;
"gps"
、"network"
、"some_provider"
那么,为什么会出现异常?
实际上你已经倾向于得到异常,但它是由系统处理的。如果您尝试添加像 "gps"
这样的已知提供程序作为测试提供程序,那么系统会尝试将其添加到位置源中。所以最好用 try catch
要无一例外地模拟 gps 提供商,试试这个
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);
}
}