Android システム アプリケーションのアンインストールと復元 (非データ パーティション)

Android システム アプリケーションのアンインストールと復元 (非データ パーティション)

Android システム アプリのアンインストールと復元 (非データ パーティション)

インターネット上には既にプリインストールされたアプリケーションをアンインストールする方法がたくさんありますが、それらは基本的に初回起動時にデータ パーティションにインストールしようとします。

この記事で達成する必要がある目標は、システム パーティションにプリインストールされているアプリケーションをアンインストールして、インストールを復元できるようにすることです。

コマンドライン方式

Android 5.0 はユーザーのシステム アプリをアンインストールできます

システムアプリをアンインストールする

adb shell pm uninstall -k --user 0 app包名
复制代码

-k はデータを保持する必要があるかどうかで、追加することはできません。

システムアプリを復元する

Android 7 はコマンドでのみ復元できます

adb shell cmd package install-existing app包名
复制代码

コード方式

コード分​​析 (現在の aosp マスター ブランチの最新コードに基づく)

アンインストール

com/android/server/pm/PackageManagerShellCommand.java

private int runUninstall() throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        int flags = 0;
        int userId = UserHandle.USER_ALL;
        long versionCode = PackageManager.VERSION_CODE_HIGHEST;
​
        String opt;
        while ((opt = getNextOption()) != null) {
            switch (opt) {
                case "-k":
                    flags |= PackageManager.DELETE_KEEP_DATA;
                    break;
                case "--user":
                    userId = UserHandle.parseUserArg(getNextArgRequired());
                    break;
                case "--versionCode":
                    versionCode = Long.parseLong(getNextArgRequired());
                    break;
                default:
                    pw.println("Error: Unknown option: " + opt);
                    return 1;
            }
        }
​
        final String packageName = getNextArg();
        if (packageName == null) {
            pw.println("Error: package name not specified");
            return 1;
        }
​
        / if a split is specified, just remove it and not the whole package
        ArrayList<String> splitNames = getRemainingArgs();
        if (!splitNames.isEmpty()) {
            return runRemoveSplits(packageName, splitNames);
        }
        // 不加--user是卸载所有用户的应用
        if (userId == UserHandle.USER_ALL) {
            flags |= PackageManager.DELETE_ALL_USERS;
        }
        final int translatedUserId =
                translateUserId(userId, UserHandle.USER_SYSTEM, "runUninstall");
        final LocalIntentReceiver receiver = new LocalIntentReceiver();
        final PackageManagerInternal internal =
                LocalServices.getService(PackageManagerInternal.class);
​
        if (internal.isApexPackage(packageName)) {
            internal.uninstallApex(
                    packageName, versionCode, translatedUserId, receiver.getIntentSender(), flags);
        } else {
            //如果删除某一个用户的应用,走这里
            if ((flags & PackageManager.DELETE_ALL_USERS) == 0) {
                final PackageInfo info = mInterface.getPackageInfo(packageName,
                        PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES, translatedUserId);
                if (info == null) {
                    pw.println("Failure [not installed for " + translatedUserId + "]");
                    return 1;
                }
                final boolean isSystem =
                        (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
                // If we are being asked to delete a system app for just one
                // user set flag so it disables rather than reverting to system
                // version of the app.
                if (isSystem) {
                    flags |= PackageManager.DELETE_SYSTEM_APP;
                }
            }
            mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
                            versionCode), null /*callerPackageName*/, flags,
                    receiver.getIntentSender(), translatedUserId);
        }
​
        final Intent result = receiver.getResult();
        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                PackageInstaller.STATUS_FAILURE);
        if (status == PackageInstaller.STATUS_SUCCESS) {
            pw.println("Success");
            return 0;
        } else {
            pw.println("Failure ["
                    + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
            return 1;
        }
    }
复制代码

アンドロイド/コンテンツ/午後/PackageInstaller.java

/**
     * Uninstall the given package with a specific version code, removing it
     * completely from the device. If the version code of the package
     * does not match the one passed in the versioned package argument this
     * method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
     * uninstall the latest version of the package.
     * <p>
     * This method is available to:
     * <ul>
     * <li>the current "installer of record" for the package
     * <li>the device owner
     * <li>the affiliated profile owner
     * </ul>
     *
     * @param versionedPackage The versioned package to uninstall.
     * @param statusReceiver Where to deliver the result.
     *
     * @see android.app.admin.DevicePolicyManager
     */
    @RequiresPermission(anyOf = {
            Manifest.permission.DELETE_PACKAGES,
            Manifest.permission.REQUEST_DELETE_PACKAGES})
    public void uninstall(@NonNull VersionedPackage versionedPackage,
            @NonNull IntentSender statusReceiver) {
        uninstall(versionedPackage, 0 /*flags*/, statusReceiver);
    }
​
    /**
     * Uninstall the given package with a specific version code, removing it
     * completely from the device. This method is only available to the current
     * "installer of record" for the package. If the version code of the package
     * does not match the one passed in the versioned package argument this
     * method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
     * uninstall the latest version of the package.
     *
     * @param versionedPackage The versioned package to uninstall.
     * @param flags Flags for uninstall.
     * @param statusReceiver Where to deliver the result.
     *
     * @hide
     */
    @RequiresPermission(anyOf = {
            Manifest.permission.DELETE_PACKAGES,
            Manifest.permission.REQUEST_DELETE_PACKAGES})
    public void uninstall(@NonNull VersionedPackage versionedPackage, @DeleteFlags int flags,
            @NonNull IntentSender statusReceiver) {
        Objects.requireNonNull(versionedPackage, "versionedPackage cannot be null");
        try {
            //binder调用
            mInstaller.uninstall(versionedPackage, mInstallerPackageName,
                    flags, statusReceiver, mUserId);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
复制代码

com/android/server/pm/PackageInstallerService.java

@Override
    public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
                IntentSender statusReceiver, int userId) {
        final Computer snapshot = mPm.snapshotComputer();
        final int callingUid = Binder.getCallingUid();
        snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
        if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
            mAppOps.checkPackage(callingUid, callerPackageName);
        }
​
        // Check whether the caller is device owner or affiliated profile owner, in which case we do
        // it silently.
        DevicePolicyManagerInternal dpmi =
                LocalServices.getService(DevicePolicyManagerInternal.class);
        final boolean canSilentlyInstallPackage =
                dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid);
​
        final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
                statusReceiver, versionedPackage.getPackageName(),
                canSilentlyInstallPackage, userId);
        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
                    == PackageManager.PERMISSION_GRANTED) {
            // Sweet, call straight through!
            mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
        } else if (canSilentlyInstallPackage) {
            // Allow the device owner and affiliated profile owner to silently delete packages
            // Need to clear the calling identity to get DELETE_PACKAGES permission
            final long ident = Binder.clearCallingIdentity();
            try {
                mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
            DevicePolicyEventLogger
                    .createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE)
                    .setAdmin(callerPackageName)
                    .write();
        } else {
            ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId);
            if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
                mContext.enforceCallingOrSelfPermission(Manifest.permission.REQUEST_DELETE_PACKAGES,
                        null);
            }
​
            // Take a short detour to confirm with user
            final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
            intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
            intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
            adapter.onUserActionRequired(intent);
        }
    }
复制代码

com/android/server/pm/IPackageManagerBase.java

@Override
    @Deprecated
    public final void deletePackageAsUser(String packageName, int versionCode,
                                          IPackageDeleteObserver observer, int userId, int flags) {
        deletePackageVersioned(new VersionedPackage(packageName, versionCode),
                               new PackageManager.LegacyPackageDeleteObserver(observer).getBinder(), userId,
                               flags);
    }
复制代码
インストールの復元

com/android/server/pm/PackageManagerShellCommand.java

private int runInstallExisting() throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        int userId = UserHandle.USER_CURRENT;
        int installFlags = PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
        String opt;
        boolean waitTillComplete = false;
        while ((opt = getNextOption()) != null) {
            switch (opt) {
                case "--user":
                    userId = UserHandle.parseUserArg(getNextArgRequired());
                    break;
                case "--ephemeral":
                case "--instant":
                    installFlags |= PackageManager.INSTALL_INSTANT_APP;
                    installFlags &= ~PackageManager.INSTALL_FULL_APP;
                    break;
                case "--full":
                    installFlags &= ~PackageManager.INSTALL_INSTANT_APP;
                    installFlags |= PackageManager.INSTALL_FULL_APP;
                    break;
                case "--wait":
                    waitTillComplete = true;
                    break;
                case "--restrict-permissions":
                    installFlags &= ~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
                    break;
                default:
                    pw.println("Error: Unknown option: " + opt);
                    return 1;
            }
        }
​
        final String packageName = getNextArg();
        if (packageName == null) {
            pw.println("Error: package name not specified");
            return 1;
        }
        final int translatedUserId =
                translateUserId(userId, UserHandle.USER_NULL, "runInstallExisting");
​
        int installReason = PackageManager.INSTALL_REASON_UNKNOWN;
        try {
            if (waitTillComplete) {
                final LocalIntentReceiver receiver = new LocalIntentReceiver();
                final IPackageInstaller installer = mInterface.getPackageInstaller();
                pw.println("Installing package " + packageName + " for user: " + translatedUserId);
                installer.installExistingPackage(packageName, installFlags, installReason,
                        receiver.getIntentSender(), translatedUserId, null);
                final Intent result = receiver.getResult();
                final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                        PackageInstaller.STATUS_FAILURE);
                pw.println("Received intent for package install");
                return status == PackageInstaller.STATUS_SUCCESS ? 0 : 1;
            }
         //走这里
            final int res = mInterface.installExistingPackageAsUser(packageName, translatedUserId,
                    installFlags, installReason, null);
            if (res == PackageManager.INSTALL_FAILED_INVALID_URI) {
                throw new NameNotFoundException("Package " + packageName + " doesn't exist");
            }
            pw.println("Package " + packageName + " installed for user: " + translatedUserId);
            return 0;
        } catch (RemoteException | NameNotFoundException e) {
            pw.println(e.toString());
            return 1;
        }
    }
复制代码

アンロードとリカバリのためのシステム アプリケーション リフレクション コール インターフェイス

このコマンドは Android 5.0 からのみサポートされていますが、インターフェイスは Android 4.2 からサポートされている必要があります (複数のユーザーをサポートしています)。

//安卓8以前
public void deletePackageAsUser(final String packageName,
                                    final IPackageDeleteObserver observer,
                                    final int userId, final int flags) 
 
public int installExistingPackageAsUser(String packageName, int userId) 
 
//安卓8开始
public void deletePackageAsUser(String packageName, int versionCode, IPackageDeleteObserver observer, int userId, int flags)public int installExistingPackageAsUser(String packageName, int userId, int installFlags,int installReason)
复制代码

どうやってするの

システム アプリケーションは、android.content.pm.IPackageManager$Stub リフレクションを介して deletePackageAsUser と installExistingPackageAsUser を取得できます。前者はアンインストールでき、後者は復元できます。インストールを復元する前に、packageManager.getApplicationInfo を使用して、インストール パッケージがまだシステムに存在するかどうかを判断できます。存在しない場合は、アプリ ストアにジャンプしてインストールできます。

知らせ:

1. deletePackageAsUser はデータを消去しますが、アップデートはアンインストールしません。つまり、システム アプリケーションが更新されると、システム パーティションに 1 つ、データ パーティションに 1 つ、合計 2 つの apk が作成されます。

データの下の更新を削除する場合は、最初にアンインストール インターフェイスを呼び出してから、deletePackageAsUser を呼び出します。

2、/data/dalvik-cache/下面会残留dex文件,可以扫描删除。

おすすめ

転載: juejin.im/post/7228928343176986661
おすすめ