Android PackageManagerService源码分析和APK安装原理详解

简介: Android PackageManagerService源码分析和APK安装原理详解

一、PackageManagerService简称PMS:PackageManagerService是Android系统中核心的

服务之一,负责应用程序的查询,卸载和应用信息查询,相当于应用程序的大管家。

try {
            //调用系统的方法,获取应用程序信息
            context.getPackageManager().getPackageInfo(null, 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

1、首先是在抽象类PackageManager中定义了抽象方法getPackageInfo()

public abstract PackageInfo getPackageInfo(@NonNull String packageName,
            @PackageInfoFlags int flags)
            throws NameNotFoundException;

2、类ApplicationPackageManager继承PackageManager类,重写了getPackageInfo()方法

@Override
    public PackageInfo getPackageInfo(String packageName, int flags)
            throws NameNotFoundException {
        return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
    }

实际调用了getPackageInfoAsUser()方法

@Override
    public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
            throws NameNotFoundException {
        try {
            PackageInfo pi = mPM.getPackageInfo(packageName, flags, userId);
            if (pi != null) {
                return pi;
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        throw new NameNotFoundException(packageName);
    }

mPM是系统中写的AIDL文件,通过mPM.getPackageInfo(),调用到代理对象Proxy类中重写的getPackageInfo()方法,代理对象又调用到PackageManagerService中的getPackageInfo()方法。

二、APK安装原理

1.APK安装的两种方式

1.静默安装,又叫无界面的安装,从各大手机厂商应用商店下载的APK,便是无界面的安装。

2.有界面的安装,从三方托管平台下载一个APK包,需要一步一步来操作的。

下面分析有界面的安装

1、点击安装后,会跳转到系统提供的PackageInstallerActivity和其对应的布局

install_start.xml.

2、之后通过PackageUtil类获取APK包里面的信息

PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);

3、如果用户点击安装按钮进行安装,或者取消,两种情况。

public void onClick(View v) {
        if (v == mOk) { //点击安装
            if (mOkCanInstall || mScrollView == null) {
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, true);
                    clearCachedApkIfNeededAndFinish();
                } else {
                    startInstall(); //跳转到一个新的页面,显示正在安装
                }
            } else {
                mScrollView.pageScroll(View.FOCUS_DOWN);
            }
        } else if (v == mCancel) { //点击取消
            // Cancel and finish
            setResult(RESULT_CANCELED);
            if (mSessionId != -1) {
                mInstaller.setPermissionsResult(mSessionId, false);
            }
            clearCachedApkIfNeededAndFinish();
        }
    }

4、正在安装中的Activity类名为:InstallAppProgress 继承Activity。

以上是有界面的安装方式的简单分析。

下面分析无界面的安装方式:

1、没有界面的安装方式,最开始是从C代码开始执行的。

首先通过adb install 输入包名后,一敲回车,会执行到commandline.c文件下的,adb_commandline()方法。

2.最终会调用到install_app()方法

char* apk_file = argv[last_apk];
    char apk_dest[PATH_MAX]; //APK安装路径
    snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
    int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);//把APK,push到手机存储里面。
    if (err) {
        goto cleanup_apk;
    } else {
        argv[last_apk] = apk_dest; /* destination name, not source location */
    }
    pm_command(transport, serial, argc, argv); //这个方法是为了执行shell:pm以命名的方式去安装

3、pm_command()方法如下

static int pm_command(transport_type transport, char* serial,
                      int argc, char** argv)
{
    char buf[4096];
    snprintf(buf, sizeof(buf), "shell:pm"); //借助pm的脚本文件,实现安装,通过pm命名的方式实现安装操作
    while(argc-- > 0) {
        char *quoted = escape_arg(*argv++);
        strncat(buf, " ", sizeof(buf) - 1);
        strncat(buf, quoted, sizeof(buf) - 1);
        free(quoted);
    }
    send_shellcommand(transport, serial, buf);
    return 0;
}

4、pm脚本文件

# Script to start "pm" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/pm.jar //重点是找到pm.jar
exec app_process $base/bin com.android.commands.pm.Pm "$@"

5、Android源码frameworks/base/cmds/pm/src/com/android/commands/pm.java 源码中有一个main方法如下

public static void main(String[] args) {
        int exitCode = 1;
        try {
            exitCode = new Pm().run(args); //重点关注run()方法
        } catch (Exception e) {
            Log.e(TAG, "Error", e);
            System.err.println("Error: " + e);
            if (e instanceof RemoteException) {
                System.err.println(PM_NOT_RUNNING_ERR);
            }
        }
        System.exit(exitCode);
    }

6、run方法代码如下

public int run(String[] args) throws IOException, RemoteException {
        boolean validCommand = false;
        if (args.length < 1) {
            return showUsage();
        }
        mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
        if (mPm == null) {
            System.err.println(PM_NOT_RUNNING_ERR);
            return 1;
        }
        mInstaller = mPm.getPackageInstaller();
        mArgs = args;
        String op = args[0];
        mNextArg = 1;
        if ("list".equals(op)) {
            return runList();
        }
        if ("path".equals(op)) {
            return runPath();
        }
        if ("dump".equals(op)) {
            return runDump();
        }
        if ("install".equals(op)) { //安装
            return runInstall();
        }
        if ("install-create".equals(op)) {
            return runInstallCreate();
        }
        if ("install-write".equals(op)) {
            return runInstallWrite();
        }
        if ("install-commit".equals(op)) {
            return runInstallCommit();
        }
        if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
            return runInstallAbandon();
        }
        if ("set-installer".equals(op)) {
            return runSetInstaller();
        }
        if ("uninstall".equals(op)) { //卸载
            return runUninstall();
        }
        if ("clear".equals(op)) {
            return runClear();
        }
        if ("enable".equals(op)) {
            return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
        }
        if ("disable".equals(op)) {
            return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
        }
        if ("disable-user".equals(op)) {
            return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
        }
        if ("disable-until-used".equals(op)) {
            return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
        }
        if ("hide".equals(op)) {
            return runSetHiddenSetting(true);
        }
        if ("unhide".equals(op)) {
            return runSetHiddenSetting(false);
        }
        if ("grant".equals(op)) {
            return runGrantRevokePermission(true);
        }
        if ("revoke".equals(op)) {
            return runGrantRevokePermission(false);
        }
        if ("set-permission-enforced".equals(op)) {
            return runSetPermissionEnforced();
        }
        if ("set-install-location".equals(op)) {
            return runSetInstallLocation();
        }
        if ("get-install-location".equals(op)) {
            return runGetInstallLocation();
        }
        if ("trim-caches".equals(op)) {
            return runTrimCaches();
        }
        if ("create-user".equals(op)) {
            return runCreateUser();
        }
        if ("remove-user".equals(op)) {
            return runRemoveUser();
        }
        if ("get-max-users".equals(op)) {
            return runGetMaxUsers();
        }
        if ("force-dex-opt".equals(op)) {
            return runForceDexOpt();
        }
        try {
            if (args.length == 1) {
                if (args[0].equalsIgnoreCase("-l")) {
                    validCommand = true;
                    return runListPackages(false);
                } else if (args[0].equalsIgnoreCase("-lf")){
                    validCommand = true;
                    return runListPackages(true);
                }
            } else if (args.length == 2) {
                if (args[0].equalsIgnoreCase("-p")) {
                    validCommand = true;
                    return displayPackageFilePath(args[1]);
                }
            }
            return 1;
        } finally {
            if (validCommand == false) {
                if (op != null) {
                    System.err.println("Error: unknown command '" + op + "'");
                }
                showUsage();
            }
        }
    }

7、在runInstall()方法中,主要是调用了该方法

mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
//实际调用的是PackageManagerService中的installPackageAsUser()方法
mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
                    installerPackageName, verificationParams, abi, userId);

8、实际调用的是PackageManagerService中的installPackageAsUser()方法

/**
     * 1、originPath:代表应用程序文件的路径。这是一个字符串类型的参数,表示应用程序安装包的位置。
     *
     * 2、observer:代表应用程序安装的观察者。它是IPackageInstallObserver2接口的一个实例,通过观察者模式来监听安装过程的状态和结果。
     *
     * 3、installFlags:代表安装标志。这是一个整型参数,用于指定安装时的特定选项。例如,可以通过设置INSTALL_REPLACE_EXISTING标志来替换已存在的应用。
     *
     * 4、installerPackageName:代表安装程序的名称。这是一个字符串类型的参数,表示执行安装的应用程序的包名。
     *
     * 5、userId:代表要安装应用的用户ID。这是一个整型参数,用于指定要安装应用的用户。在多用户系统中,每个用户拥有自己的应用安装目录。
     */
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
            int installFlags, String installerPackageName, int userId)

9.最后通过Handler发送消息,最后执行到startCopy()方法

final boolean startCopy() {
            boolean res;
            try {
                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
                if (++mRetries > MAX_RETRIES) { //安装次数大于4次,安装失败
                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                    mHandler.sendEmptyMessage(MCS_GIVE_UP);
                    handleServiceError();
                    return false;
                } else {
                    handleStartCopy();
                    res = true;
                }
            } catch (RemoteException e) {
                if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                mHandler.sendEmptyMessage(MCS_RECONNECT);
                res = false;
            }
            handleReturnCode();
            return res;
        }

9、最后执行安装操作的方法handleReturnCode()

@Override
        void handleReturnCode() {
            if (mObserver != null) {
                try {
                    mObserver.onGetStatsCompleted(mStats, mSuccess);
                } catch (RemoteException e) {
                    Slog.i(TAG, "Observer no longer exists.");
                }
            }
        }

APK无界面安装流程图

APK安装原理如下:


目录
相关文章
|
2月前
|
Android开发 开发者
Android Split APK介绍
【2月更文挑战第5天】
|
5天前
|
存储 Java Android开发
Android系统 设置第三方应用为默认Launcher实现和原理分析
Android系统 设置第三方应用为默认Launcher实现和原理分析
19 0
|
5天前
|
网络协议 Shell Android开发
Android 深入学习ADB调试原理(1)
Android 深入学习ADB调试原理(1)
20 1
|
5天前
|
存储 Java Linux
Android系统获取event事件回调等几种实现和原理分析
Android系统获取event事件回调等几种实现和原理分析
26 0
|
5天前
|
Linux 开发工具 Android开发
Docker系列(1)安装Linux系统编译Android源码
Docker系列(1)安装Linux系统编译Android源码
7 0
|
1月前
|
Shell 开发工具 Android开发
ADB 下载、安装及使用教程:让你更好地管理 Android 设备
ADB 下载、安装及使用教程:让你更好地管理 Android 设备
523 2
|
1月前
|
网络协议 关系型数据库 MySQL
安卓手机termux上安装MariaDB数据库并实现公网环境下的远程连接
安卓手机termux上安装MariaDB数据库并实现公网环境下的远程连接
|
2月前
|
算法 数据库 Android开发
安卓逆向 -- APK文件结构
安卓逆向 -- APK文件结构
22 0
|
2月前
|
算法 Java Android开发
安卓逆向 -- 调用其他APK的SO文件
安卓逆向 -- 调用其他APK的SO文件
17 0
|
2月前
|
安全 Android开发
如何在Android手机上安装第三方应用?
【2月更文挑战第4天】在Android系统中,安装第三方应用是一个常见的需求。本文将介绍如何在Android手机上安装第三方应用,并提供详细的步骤和注意事项。