Android package管理之应用程序安装详解

系统中的应用程序分两部分,系统预装和后期安装,系统预装的apk主要是编译时,预置在以下几个目录
/system/app、/system/priv-app、/vendor/app、/vendor/priv-app、后期安装的通过以下几种途径:

APK安装方式
方法核心文件及函数解释
adb pushPMS、AppDirObserver、ADD_EVENTS将apk 文件直接推送到/data 目录下,系统监听并安装
adb installcommandline.cpp/adb_commandline->install_app:exec:cmd package//cmd.cpp onShellCommand ->PMS->PackageManagerShellCommand通过adb 调用cmd 命令,执行cmd package,最终通过PackageManagerShellCommand 调用runinstall 函数执行安装
pm installpm、cmd package在串口或者adb shell 进入终端后,通过pm 命令安装,cmd package后续步骤与adb install 一致
应用市场安装Android 4.4:PackageManager->installPackage Android 9.0 通过PackageManager->getPackageInstaller->createSession/session.commitAndroid 4.4 可以直接调用PackageManager installPackage (反射的方式)安装apk Android 9.0已经没有installPackage 接口,通过获取PackageInstaller 安装apk,下面可以给出实例代码
其他第三方安装PackageInstall APP网络上下载的apk,或者U盘安装等,通过PackageInstall APP 统一安装

通过命令行安装adb push 、adb install ,pm install ,cmd package 等,这里就不给出实例了,很简单

第三方安装code实例
public static boolean installNormal(Context context, String filePath) {
    	Intent i = new Intent(Intent.ACTION_VIEW);
        File file = new File(filePath);
        if (file == null || !file.exists() || !file.isFile() || file.length() <= 0) {
            return false;
        }
        file.setReadable(true, false);
        i.setDataAndType(Uri.parse("file://" + filePath), "application/vnd.android.package-archive");
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(i);
        
        return true;
    }

packages/apps/PackageInstaller/AndroidManifest.xml

 <activity android:name=".InstallStart"
                android:exported="true"
                android:excludeFromRecents="true">
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="file" />
                <data android:scheme="content" />
                <data android:mimeType="application/vnd.android.package-archive" />
            </intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.intent.action.INSTALL_PACKAGE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="file" />
                <data android:scheme="package" />
                <data android:scheme="content" />
            </intent-filter>
            <intent-filter android:priority="1">
                <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
应用市场安装code实例

应用市场(厂商自己定制的)安装一般是没有界面的,直接调用底层的packageinstall 或者pm install
实例1:

StringBuilder command = new StringBuilder().append("LD_LIBRARY_PATH=/vendor/lib:/system/lib pm install ")
                .append(pmParams == null ? "" : pmParams).append(" ").append(filePath.replace(" ", "\\ "));
        CommandResult commandResult = ShellUtils.execCommand(command.toString(), !isSystemApplication(context), true);
        if (commandResult.successMsg != null
                && (commandResult.successMsg.contains("Success") || commandResult.successMsg.contains("success"))) {
        	return INSTALL_SUCCEEDED;
        }

实例2:
PackageInstallerSession.commit ==> commitLocked(); ==> PMS.installStage()

private void install(PackageManager pm, File targetFile) {
	    final long length = targetFile.length();
	    SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
	    params.setSize(length);
	
	    android.content.pm.PackageInstaller installer = pm.getPackageInstaller();
	    try {
	        final int sessionID = installer.createSession(params);
	        if (sessionID > 0) {
	            registerInstallReceiver();
	            Session session = installer.openSession(sessionID);
	            copyFile(session, targetFile, length);
	
	            Intent intent = new Intent(ACTION_INSTALLED);
	            PendingIntent pi = PendingIntent.getBroadcast(FunApplication.getInstance(), 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
	            session.commit(pi.getIntentSender());
	
	            waitForInstall();
	            session.close();
	        }
	    } catch (Exception e) {
	        unregisterInstallReceiver();
	        Log.e("SDKManagerPackageInstaller", "Install failed - " + targetFile, e);
	    }
    }

实例3(Android 4.4):

public int install(Uri src, PackageManager pm, String installFrom) {
        mRet = 0;
        int flag = 0x00000002;  // PackageManager.INSTALL_REPLACE_EXISTING
        try {
         Method method = pm.getClass().getMethod("installPackage",
                          Uri.class, IPackageInstallObserver.class,
                          int.class, String.class);
          Log.d("SDKManagerPackageInstaller", "start install");
          method.invoke(pm, src, this, flag, installFrom);
       } catch(Exception e) {
               e.printStackTrace();
       }
       waitForInstall();
        return mRet;
    }

以上,不管是什么方式,最终都会调用到PMS,通过PMS->installStage/installPackageAsUser执行后续的安装流程

PMS安装apk流程

1、installStage
PMS.installStage()会调用sendMessage将”INIT_COPY”发送给PackageHandler:

final PackageHandler mHandler;
void installStage(String packageName, File stagedDir,
            IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
            String installerPackageName, int installerUid, UserHandle user,
            PackageParser.SigningDetails signingDetails) {
       	//方便查看,简化了很多代码
        final VerificationInfo verificationInfo = new VerificationInfo();
        final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir);
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        final int installReason = fixUpInstallReason();
        final InstallParams params = new InstallParams();
        params.setTraceMethod();
        msg.obj = params;
        mHandler.sendMessage(msg);
    }

PMS.installPackageAsUser 与installStage 类似:

public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, int userId) {
    ...
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    final VerificationInfo verificationInfo = new VerificationInfo(
            null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);
    final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
            installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
            null /*packageAbiOverride*/, null /*grantedPermissions*/,
            null /*certificates*/);
    params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
    msg.obj = params;
    mHandler.sendMessage(msg);
    ....
}

PackageHandler用于处理apk的安装请求等消息

2、INIT_COPY
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 内部类 PackageHandler

class PackageHandler extends Handler {
		...
        void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    HandlerParams params = (HandlerParams) msg.obj;
                    int idx = mPendingInstalls.size();
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
                    if (!mBound) {
                        if (!connectToService()) {
                            Slog.e(TAG, "Failed to bind to media container service");
                            params.serviceError();
                            if (params.traceMethod != null) {
                               params.traceCookie);
                            }
                            return;
                        } else {
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
                        mPendingInstalls.add(idx, params);
                        if (idx == 0) {
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
                }
                case MCS_BOUND: {
                    //这里后续分析
                    break;
                }
               
            }
        }
    }

INIT_COPY主要是将新的请求加入到mPendingIntalls中,等待MCS_BOUND阶段处理。加入到mPendingIntalls 中的是一个InstallParams 对象(在installStage 中构造)

3、MCS_BOUND
不管是哪种情况,INIT_COPY之后,一定会再发一个MSC_BOUND的消息
这里开始调用InstallParams类的startCopy()方法

void doHandleMessage(Message msg) {
    .......
    case MCS_BOUND: {
        ........
        if (msg.obj != null) {
            mContainerService = (IMediaContainerService) msg.obj;
            .......
        }
        if (mContainerService == null) {
            if (!mBound) {
                ............            
            } else {
                Slog.w(TAG, "Waiting to connect to media container service");
            }
        // 请求队列mPendingInstalls不为空
        } else if (mPendingInstalls.size() > 0) {
            HandlerParams params = mPendingInstalls.get(0);
            if (params != null) {
                ........
                //调用参数的startCopy函数处理安装请求
                if (params.startCopy()) {
                    ........
                    // Delete pending install
                    if (mPendingInstalls.size() > 0) {
                        mPendingInstalls.remove(0);
                    }
                    if (mPendingInstalls.size() == 0) {
                        if (mBound) {
                            ..........
                            removeMessages(MCS_UNBIND);
                            Message ubmsg = obtainMessage(MCS_UNBIND);
                            sendMessageDelayed(ubmsg, 10000);
                        }
                    } else {
                        ......
                        mHandler.sendEmptyMessage(MCS_BOUND);
                    }
                }
                .........
            }
        } else {
            // Should never happen ideally.
            Slog.w(TAG, "Empty queue");
        }
        break;
    }
.......
}

如果mPendingInstalls不为空,调用InstallParams.startCopy函数处理安装请求。
接着如果mPendingInstalls不为空,发送MCS_BOUND继续处理下一个,直到队列为空。
如果队列为空,则等待一段时间后,发送MCS_UNBIND消息断开与安装服务的绑定。

这里总结一下PMS已经完成的工作:
1.构造一个InstallParams对象(installStage /installPackageAsUser),这个对象里包含了apk文件的信息和一些安装相关的参数

2.发送INIT_COPY消息,这时如果没有连接DefaultContainerService, 先连接这个service;还有就是将apk安装请求放入mPendingInstalls里

3.再发送MCS_BOUND消息,调用InstallParams对象的startCopy()方法开始安装app;如果有多个app等待安装,循环发MSC_BOUND消息,执行安装操作

4、startCopy
InstallParams继承HandlerParams,会首先调用HandlerParams.startCopy:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private abstract class HandlerParams {
       	...
        final boolean startCopy() {
            boolean res;
            try {
                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

                if (++mRetries > MAX_RETRIES) {
                    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) {
            }
            handleReturnCode();//注意这里
            return res;
        }
        ...
    }

5、handleStartCopy
handleStartCopy函数在HandleParams抽象类定义,在其子类InstallParams来实现,我们看看与实际安装相关的handleStartCopy函数:

public void handleStartCopy() throws RemoteException {
    int ret = PackageManager.INSTALL_SUCCEEDED;
    
    // 决定是安装在手机内还是sdcard中,设置对应标志位
    if (origin.staged) {
        if (origin.file != null) {
            installFlags |= PackageManager.INSTALL_INTERNAL;
            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
        } else if (origin.cid != null) {
            installFlags |= PackageManager.INSTALL_EXTERNAL;
            installFlags &= ~PackageManager.INSTALL_INTERNAL;
        } else {
            throw new IllegalStateException("Invalid stage location");
        }
    }
    ...
    // 检查APK的安装位置是否正确
    if (onInt && onSd) {
        // Check if both bits are set.
        Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
    } else if (onSd && ephemeral) {
        Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");
        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
    } else {
        ...
    }
    ...
    // createInstallArgs用于创建一个安装参数对象
    final InstallArgs args = createInstallArgs(this);
    
    if (ret == PackageManager.INSTALL_SUCCEEDED) {
        ...
            // 调用InstallArgs的copyApk函数
            ret = args.copyApk(mContainerService, true);
        }
    }
    mRet = ret;
}

InstallParams$handleStartCopy()主要功能是获取安装位置信息以及复制apk到指定位置。
copyApk()方法完成将需要安装的apk文件拷贝到要安装的分区的一个零时文件夹里,并重命名为base.apk,比如/data/app/vmdl1309479077.tmp/base.apk下。同时还会将.apk包里的.so库拷贝到/data/app/vmdl1309479077.tmp/libs下

6、handleReturnCode
handleReturnCode里会完成app安装的剩余动作,并发送安装成功的广播
handleReturnCode
7、installPackageLI
上图第4步,installPackageLI中完成app 安装的核心工作,后面再单独介绍一下
8、POST_INSTALL
1.发送ACTION_PACKAGE_ADDED广播
2.如果在调用installPackageAsUser()方法时有传入IPackageInstallObserver2作为回调,则调用回调方法报告安装状态
至此一个APK 安装流程完毕



关于上面提到的installPackageLI()是app 安装的的核心函数,下面再重点讲解一下

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
    
    // PackageParser对象
    PackageParser pp = new PackageParser();
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setDisplayMetrics(mMetrics);
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final PackageParser.Package pkg;
    try {
        if (DEBUG_INSTALL) Slog.i(TAG, "Start parsing apk: " + installerPackageName);
        // 1.开始解析我们的package
        pkg = pp.parsePackage(tmpPackageFile, parseFlags);
        if (DEBUG_INSTALL) Slog.i(TAG, "Parsing done for apk: " + installerPackageName);
    } catch (PackageParserException e) {
        res.setError("Failed parse during installPackageLI", e);
        return;
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
    ...
    //2. 加载证书,获取签名信息
    try {
        // either use what we've been given or parse directly from the APK
        if (args.certificates != null) {
            try {
                PackageParser.populateCertificates(pkg, args.certificates);
            } catch (PackageParserException e) {
                PackageParser.collectCertificates(pkg, parseFlags);
            }
        } else {
            PackageParser.collectCertificates(pkg, parseFlags);
        }
    } catch (PackageParserException e) {
        res.setError("Failed collect during installPackageLI", e);
        return;
    }
    ...
    synchronized (mPackages) {
        // 3.检测packages是否存在
        if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                ...
                replace = true;
                
            } else if (mPackages.containsKey(pkgName)) {
                ...
                replace = true;
                if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
            }
            ...           
        }
    }    
    ...
    try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
            "installPackageLI")) {
        if (replace) {
            // 4.更新已经存在的packages
            replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                    installerPackageName, res);
        } else {
            // 5.安装新的packages
            installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                    args.user, installerPackageName, volumeUuid, res);
        }
    }
...
}

PackageParser$parsePackage,主要是解析APK的AndroidManifest.xml,将每个标签对应的信息添加到Package的相关列表中,如将下的信息添加到Package的activities列表等。
加载apk证书,获取签名信息
检查目前安装的APK是否在系统中已存在:
已存在,则调用replacePackageLIF进行替换安装。
不存在,否则调用installNewPackageLIF进行安装。
install
第2步parsePackage()通过解析apk包和apk包里的AndroidManifest.xml,解析出所有apk的信息,放到Package对象中。

第3步collectCertificates()方法对apk文件进行签收校验,确保apk文件没有被更改过以及签名正确。第4步通过collectManifestDigest()方法获取AndroidManifest.xml的摘要值。

第5步derivePackageAbi()方法通过apk包里使用的so库确定app进程的abi,关于abi,可以查看之前的一篇文章: Android系统设置app进程abi

第6步performDexOpt()通过socket和installd通信,将apk包里的classes.dex转化为虚拟机可以直接运行的文件格式,关于dex优化,可以查看之前的一篇文章:Android编译时odex优化

第7步doRename()方法将前面handleStartCopy()里的零时文件夹/data/app/vmdl1309479077更改成/data/app/$(package_name),这个新的文件夹将是app安装后的最终目录

第8步installNewPackageLI()将完成一个新的app的安装,如果是安装之前已经存在的apk, 则这里调用的是replacePackageLI()


replacePackageLIF
如果需要替换的是系统APP,则调用Settings$disableSystemPackageLPw来disable旧的APK;如果替换的是非系统APP,则调用deletePackageLI删除旧的APK。

replacePackageLIF
    replaceSystemPackageLIF  // 系统 pkg
        removePackageLI
        disableSystemPackageLPw
        clearAppDataLIF
        scanPackageTracedLI  //安装apk
            scanPackageLI
                scanPackageDirtyLI  
        updateSettingsLI
        updatePermissionsLPw
        mSettings.writeLPr();
    replaceNonSystemPackageLIF  // 非系统 pkg
        deletePackageLIF
        clearAppDataLIF
        clearAppProfilesLIF
        scanPackageTracedLI    // 安装apk
            scanPackageLI
                scanPackageDirtyLI  
        updateSettingsLI
        updatePermissionsLPw
        mSettings.writeLPr();

不管是更新系统还是非系统apk,都会先清除之前的packages信息,然后通过scanPackageTracedLI去安装apk,安装完后更新permissions和setting,最后通过writeLPr更新packages.xml


installNewPackageLIF

private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
            int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
            PackageInstalledInfo res) {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
        // Remember this for later, in case we need to rollback this install
        String pkgName = pkg.packageName;
        if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
        // package已经存在
        synchronized(mPackages) {
            if (mSettings.mRenamedPackages.containsKey(pkgName)) {
                // A package with the same name is already installed, though
                // it has been renamed to an older name.  The package we
                // are trying to install should be installed as an update to
                // the existing one, but that has not been requested, so bail.
                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
                        + " without first uninstalling package running as "
                        + mSettings.mRenamedPackages.get(pkgName));
                return;
            }
            if (mPackages.containsKey(pkgName)) {
                // Don't allow installation over an existing package with the same name.
                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
                        + " without first uninstalling.");
                return;
            }
        }
        try {
            // 1. 安装apk
            PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
                    System.currentTimeMillis(), user);
            // 2. 更新setting
            updateSettingsLI(newPackage, installerPackageName, null, res, user);
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                prepareAppDataAfterInstallLIF(newPackage);
            } else {
                // Remove package from internal structures, but keep around any
                // data that might have already existed
                deletePackageLIF(pkgName, UserHandle.ALL, false, null,
                        PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
            }
        } catch (PackageManagerException e) {
            res.setError("Package couldn't be installed in " + pkg.codePath, e);
        }
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

installNewPackageLIF会调用scanPackageTracedLI去安装apk,最终会调用scanPackageLI->scanPackageDirtyLI实际去安装apk。

installNewPackageLI
verifySignaturesLP()使用上一步中collectCertificates()里获取的签名信息进行验证,主要分两步:一是如果是安装一个系统中已经存在的同名app, 则和已经存在的app签名进行对比,保证两者签名相同;二是如果系统中已经存在和正在安装的app有相同UID的app
则需要保证安装app的签名和已经存在的使用相同UID的app签名相同。

createDataDirsLI()方法创建app的数据目录,例如: /data/data/${package_name}/

adjustCpuAbisForSharedUserLPw()方法保证如果新安装的app使用了”android:sharedUID”, 它会将它的abi调整成和系统中使用相同uid的其它app一样的abi

接下来就会在mSettings中写入正在安装app的信息,比如安装路径,数据目录路径,签名信息,abi信息等。将这些信息在/data/system/packages.xml中。同时还会将AndroidManifest.xml中的四大组件以及权限等信息,存到PackageManagerService的对应数组里,这样以后通过startActivity之类的方法打开相应的组件时,就可以找到相应的组件是否存在。需要注意的是,这些四大组件以及权限信息,只是保存在内存中,每次系统开机时,都会重新扫描apk文件,然后将它们再次存到内存中使用。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android源码中,应用程序安装是由PackageManagerService (PMS) 负责实现的。PackageManagerService是Android系统中的一个系统服务,负责管理应用程序安装、卸载、更新和权限控制等操作。 具体而言,应用程序安装过程涉及到以下几个关键的类和组件: 1. PackageManagerService (PMS):PMS是系统服务的实现类,负责处理应用程序安装、卸载、更新等操作。它通过解析APK文件获取应用程序的信息,然后将相关信息记录在系统的包管理器中。 2. PackageInstaller:PackageInstaller是一个应用程序安装器,用于处理用户通过界面(如Google Play商店或系统文件管理器)安装应用程序的请求。它与PMS进行通信,并将安装请求传递给PMS进行处理。 3. PackageParser:PackageParser是一个APK解析器,用于解析APK文件并提取其中的信息,如应用程序的包名、版本号、权限等。PMS使用PackageParser来解析APK文件并获取相关信息。 4. PackageInstallerActivity:PackageInstallerActivity是一个系统界面组件,用于显示应用程序安装的界面。当用户通过界面触发应用程序安装时,PackageInstallerActivity会调用PackageInstaller来处理安装请求。 总结起来,应用程序安装是由PackageManagerService负责实现的,它通过与PackageInstaller和PackageParser等组件的协作,完成解析APK文件、记录应用程序信息和处理安装请求等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值