如何利用virtualApp实现手机虚拟定位

最近遇到一个需求,是需要做一个手机模拟位置的功能,功能场景不复杂,主要如下:
1、用户可以通过本软件进行位置设置
2、位置设置成功以后,手机中的其他软件当前的位置应该都是设置之后的经纬度
3、需要针对部分考勤软件做防检测
经过不断的努力,功能已完成,安装包和代码已上传至github,下载地址:https://github.com/MockLocation/dingdian
您也可以直接下载安装:http://app.yuyanda.com/amc2
核心代码如下:
1、添加应用分身

private InstallResult installPackageImpl(String path, InstallOptions options) {
        long installTime = System.currentTimeMillis();
        if (path == null) {
            return InstallResult.makeFailure("path = NULL");
        }
        File packageFile = new File(path);
        if (!packageFile.exists() || !packageFile.isFile()) {
            return InstallResult.makeFailure("Package File is not exist.");
        }
        VPackage pkg = null;
        try {
            pkg = PackageParserEx.parsePackage(packageFile);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        if (pkg == null || pkg.packageName == null) {
            return InstallResult.makeFailure("Unable to parse the package.");
        }
        InstallResult res = new InstallResult();
        res.packageName = pkg.packageName;
        // PackageCache holds all packages, try to check if we need to update.
        VPackage existOne = PackageCacheManager.get(pkg.packageName);
        PackageSetting existSetting = existOne != null ? (PackageSetting) existOne.mExtras : null;
        if (existOne != null) {
            if (options.updateStrategy == InstallOptions.UpdateStrategy.IGNORE_NEW_VERSION) {
                res.isUpdate = true;
                return res;
            }
            if (!isAllowedUpdate(existOne, pkg, options.updateStrategy)) {
                return InstallResult.makeFailure("Not allowed to update the package.");
            }
            res.isUpdate = true;
            VActivityManagerService.get().killAppByPkg(res.packageName, VUserHandle.USER_ALL);
        }
        boolean useSourceLocationApk = options.useSourceLocationApk;
        if (existOne != null) {
            PackageCacheManager.remove(pkg.packageName);
        }
        PackageSetting ps;
        if (existSetting != null) {
            ps = existSetting;
        } else {
            ps = new PackageSetting();
        }
        boolean support64bit = false, support32bit = false;
        boolean checkSupportAbi = true;
        Set<String> abiList = NativeLibraryHelperCompat.getSupportAbiList(packageFile.getPath());
        if (abiList.isEmpty()) {
            support64bit = true;
            support32bit = true;
        } else {
            if (NativeLibraryHelperCompat.contain64bitAbi(abiList)) {
                support64bit = true;
            }
            if (NativeLibraryHelperCompat.contain32bitAbi(abiList)) {
                support32bit = true;
            }
        }
        if (support32bit) {
            if (support64bit) {
                ps.flag = PackageSetting.FLAG_RUN_BOTH_32BIT_64BIT;
            } else {
                ps.flag = PackageSetting.FLAG_RUN_32BIT;
            }
        } else {
            ps.flag = PackageSetting.FLAG_RUN_64BIT;
        }
        NativeLibraryHelperCompat.copyNativeBinaries(packageFile, VEnvironment.getAppLibDirectory(pkg.packageName));

        if (!useSourceLocationApk) {
            File privatePackageFile = VEnvironment.getPackageResourcePath(pkg.packageName);
            try {
                FileUtils.copyFile(packageFile, privatePackageFile);
            } catch (IOException e) {
                privatePackageFile.delete();
                return InstallResult.makeFailure("Unable to copy the package file.");
            }
            packageFile = privatePackageFile;
            VEnvironment.chmodPackageDictionary(packageFile);
        }

        if (support64bit && !useSourceLocationApk) {
//            V64BitHelper.copyPackage64(packageFile.getPath(), pkg.packageName);
        }

        ps.appMode = useSourceLocationApk ? MODE_APP_USE_OUTSIDE_APK : MODE_APP_COPY_APK;
        ps.packageName = pkg.packageName;
        ps.appId = VUserHandle.getAppId(mUidSystem.getOrCreateUid(pkg));
        if (res.isUpdate) {
            ps.lastUpdateTime = installTime;
        } else {
            ps.firstInstallTime = installTime;
            ps.lastUpdateTime = installTime;
            for (int userId : VUserManagerService.get().getUserIds()) {
                boolean installed = userId == 0;
                ps.setUserState(userId, false/*launched*/, false/*hidden*/, installed);
            }
        }
        PackageParserEx.savePackageCache(pkg);
        PackageCacheManager.put(pkg, ps);
        mPersistenceLayer.save();
        if (support32bit && !useSourceLocationApk) {
            try {
                DexOptimizer.optimizeDex(packageFile.getPath(), VEnvironment.getOdexFile(ps.packageName).getPath());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (options.notify) {
            notifyAppInstalled(ps, -1);
        }
        res.isSuccess = true;
        return res;
    }

2、启动应用

public boolean launchApp(final int userId, String packageName, boolean preview) {

        Context context = VirtualCore.get().getContext();
        VPackageManager pm = VPackageManager.get();
        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
        intentToResolve.addCategory(Intent.CATEGORY_INFO);
        intentToResolve.setPackage(packageName);
        List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, intentToResolve.resolveType(context), 0, userId);

        // Otherwise, try to find a main launcher activity.
        if (ris == null || ris.size() <= 0) {
            // reuse the intent instance
            intentToResolve.removeCategory(Intent.CATEGORY_INFO);
            intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
            intentToResolve.setPackage(packageName);
            ris = pm.queryIntentActivities(intentToResolve, intentToResolve.resolveType(context), 0, userId);
        }
        if (ris == null || ris.size() <= 0) {
            return false;
        }
        ActivityInfo info = ris.get(0).activityInfo;
        final Intent intent = new Intent(intentToResolve);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setClassName(info.packageName, info.name);

        if (!preview || VActivityManager.get().isAppRunning(info.packageName, userId, true)) {
            VActivityManager.get().startActivity(intent, userId);
        } else {
            intent.putExtra("_VA_|no_animation", true);
            WindowPreviewActivity.previewActivity(userId, info);
            VirtualRuntime.getUIHandler().postDelayed(new Runnable() {
                @Override
                public void run() {VActivityManager.get().startActivity(intent, userId);
                }
            }, 400L);
        }
        return true;
    }

3、对requestLocationUpdates这个关键函数进行拦截

@SkipInject
    static class RequestLocationUpdates extends ReplaceLastPkgMethodProxy {

        public RequestLocationUpdates() {
            super("requestLocationUpdates");
        }

        public RequestLocationUpdates(String name) {
            super(name);
        }

        @Override
        public Object call(final Object who, Method method, Object... args) throws Throwable {
            if (isFakeLocationEnable()) {
                VLocationManager.get().requestLocationUpdates(args);
                return 0;
            } else {
                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
                    LocationRequest request = (LocationRequest) args[0];
                    fixLocationRequest(request);
                }
            }
            return super.call(who, method, args);
        }
    }

4、提供虚拟位置

    private boolean notifyLocation(final Object ListenerTransport, final Location location, boolean post) {
        checkWork();
        if (ListenerTransport == null) {
            return false;
        }
        if (!post) {
            try {
                mirror.android.location.LocationManager.ListenerTransport.mListener.get(ListenerTransport).onLocationChanged(location);
                return true;
            } catch (Throwable e) {
            }
            return false;
        }
        mWorkHandler.post(new Runnable() {
            @Override
            public void run() {
                try {
                    mirror.android.location.LocationManager.ListenerTransport.mListener.get(ListenerTransport).onLocationChanged(location);
                } catch (Throwable e) {
                }
            }
        });
        return true;
    }

目前还存在不少问题,比如安卓10的适配不是很好,后来使用epic框架,勉强可以使用,但是效率不是很好,低配置的手机使用起来会有卡顿的情况,还有部分手机不能兼容,启动分身的时候会崩溃

猜你喜欢

转载自blog.csdn.net/qq_44983722/article/details/100072624