Android10 mockLocation 模拟定位

劳豪
2023-12-01

网上搜了半天,试了很多方法,大同小异,当然报错也是大同小异……

最经典的报错就是 IllegalArgumentException: Provider "gps" already exists 

这里介绍一下如何解决这个问题,并且给出正确的模拟定位方法。

首先当然是先在AndroidManifest里申请权限了

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
        tools:ignore="MockLocation,ProtectedPermissions"/>

在Android 10里定位是危险权限,如果不是在编译系统的时候打包的apk,就需要动态申请权限:

private void initPermissions(Context context) {
        RequestPermissions(context, "android.permission.ACCESS_FINE_LOCATION");
        RequestPermissions(context, "android.permission.ACCESS_COARSE_LOCATION");
        RequestPermissions(context,"android.permission.ACCESS_MOCK_LOCATION");
    }

    public static boolean RequestPermissions(Context context, String permission) {
        if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
            Log.i("requestMyPermissions", ": 【 " + permission + " 】没有授权,申请权限");
            ActivityCompat.requestPermissions((Activity) context, new String[]{permission}, 100);
            return false;
        } else {
            Log.i("requestMyPermissions", ": 【 " + permission + " 】有权限");
            return true;
        }
    }

然后就是获取LocationManager、构建location 注意location参数必须设满,否则会报不完整错误:

java.lang.IllegalArgumentException: Incomplete location object

mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        Location mockLocation = new Location(GPS_PROVIDER);
        mockLocation.setLatitude(116.397128);
        mockLocation.setLongitude(39.916527);
        mockLocation.setAltitude(0);
        mockLocation.setTime(System.currentTimeMillis());
        mockLocation.setSpeed(0.01f);
        mockLocation.setBearing(1f);
        mockLocation.setAccuracy(3f);
        mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            mockLocation.setBearingAccuracyDegrees(0.1f);
            mockLocation.setVerticalAccuracyMeters(0.1f);
            mockLocation.setSpeedAccuracyMetersPerSecond(0.01f);
        }

下一步就是直接mock location了:

这里需要在addTestProvider之前remove一次,否则会报 Provider "gps" already exists 

源码:frameworks/base/services/core/java/com/android/server/LocationManagerService.java

    @Override
    public void addTestProvider(String name, ProviderProperties properties, String opPackageName) {
        if (!canCallerAccessMockLocation(opPackageName)) {
            return;
        }

        if (PASSIVE_PROVIDER.equals(name)) {
            throw new IllegalArgumentException("Cannot mock the passive location provider");
        }

        synchronized (mLock) {
            long identity = Binder.clearCallingIdentity();
            try {
                LocationProvider oldProvider = getLocationProviderLocked(name);
                if (oldProvider != null) {
                    if (oldProvider.isMock()) {
                        throw new IllegalArgumentException(
                                "Provider \"" + name + "\" already exists");
                    }

                    removeProviderLocked(oldProvider);
                }

mock代码:

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){
            // IllegalArgumentException: Provider "gps" already exists
            mLocationManager.removeTestProvider(GPS_PROVIDER);
            mLocationManager.addTestProvider(GPS_PROVIDER, false, false, false, false, false, true, true, 0, 5);
            mLocationManager.setTestProviderEnabled(GPS_PROVIDER, true);
            mLocationManager.setTestProviderLocation(GPS_PROVIDER, location);
        }
    }

如此一来就模拟成功了,当然安装apk成功后,记得在开发者模式里设置 “选择模拟位置信息位置 ”为当前app。

 类似资料: