模拟位置的测试提供商无法在 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);
    }
}