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开发 Kotlin
Android经典实战之SurfaceView原理和实践
本文介绍了 `SurfaceView` 这一强大的 UI 组件,尤其适合高性能绘制任务,如视频播放和游戏。文章详细讲解了 `SurfaceView` 的原理、与 `Surface` 类的关系及其实现示例,并强调了使用时需注意的线程安全、生命周期管理和性能优化等问题。
160 8
|
21天前
|
缓存 Java 数据库
Android的ANR原理
【10月更文挑战第18天】了解 ANR 的原理对于开发高质量的 Android 应用至关重要。通过合理的设计和优化,可以有效避免 ANR 的发生,提升应用的性能和用户体验。
49 8
|
1月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
35 3
|
2月前
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
45 2
|
2月前
|
编解码 前端开发 Android开发
Android经典实战之TextureView原理和高级用法
本文介绍了 `TextureView` 的原理和特点,包括其硬件加速渲染的优势及与其他视图叠加使用的灵活性,并提供了视频播放和自定义绘制的示例代码。通过合理管理生命周期和资源,`TextureView` 可实现高效流畅的图形和视频渲染。
224 12
|
1月前
|
Java 调度 Android开发
Android面试题之Kotlin中async 和 await实现并发的原理和面试总结
本文首发于公众号“AntDream”,详细解析了Kotlin协程中`async`与`await`的原理及其非阻塞特性,并提供了相关面试题及答案。协程作为轻量级线程,由Kotlin运行时库管理,`async`用于启动协程并返回`Deferred`对象,`await`则用于等待该对象完成并获取结果。文章还探讨了协程与传统线程的区别,并展示了如何取消协程任务及正确释放资源。
24 0
|
3月前
|
Java Android开发 Windows
使用keytool查看Android APK签名
本文介绍了如何使用Windows命令行工具和keytool查看APK的签名信息,并提供了使用AOSP环境中的signapk.jar工具对APK进行系统签名的方法。
354 0
使用keytool查看Android APK签名
|
3月前
|
Android开发
将AAB(Android App Bundle)转换为APK
将AAB(Android App Bundle)转换为APK
239 1
|
3月前
|
Android开发 开发者
Android、Flutter为不同的CPU架构包打包APK(v7a、v8a、x86)
Android、Flutter为不同的CPU架构包打包APK(v7a、v8a、x86)
262 1
|
3月前
|
Android开发
解决android apk安装后出现2个相同的应用图标
解决android apk安装后出现2个相同的应用图标
339 2