模拟位置的测试提供程序不适用于 Android 10

fel*_*rce 6 android mocking android-location

模拟位置在 Android 10 上不起作用,调用 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$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
Run Code Online (Sandbox Code Playgroud)

代码:

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)
    }
Run Code Online (Sandbox Code Playgroud)

崩溃:

mLocationManager?.addTestProvider(
                provider,
                false,
                false,
                false,
                false,
                false,
                true,
                true,
                android.location.Criteria.POWER_LOW,
                android.location.Criteria.ACCURACY_FINE
        )
Run Code Online (Sandbox Code Playgroud)

它仅在 Android 10 上崩溃,我尝试更改提供程序名称,但不起作用,所有名称都崩溃了。我认为这不是权限问题,因为我在启用 ACCESS_MOCK_LOCATION、ACCESS_FINE_LOCATION 和 INTERNET 的情况下进行了测试

bla*_*ara 4

首先,当您添加测试提供程序(即“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);
    }
}
Run Code Online (Sandbox Code Playgroud)