系统中的应用程序分两部分,系统预装和后期安装,系统预装的apk主要是编译时,预置在以下几个目录 /system/app、/system/priv-app、/vendor/app、/vendor/priv-app、后期安装的通过以下几种途径:
APK安装方式
方法 | 核心文件及函数 | 解释 |
---|
adb push | PMS、AppDirObserver、ADD_EVENTS | 将apk 文件直接推送到/data 目录下,系统监听并安装 | adb install | commandline.cpp/adb_commandline->install_app:exec:cmd package//cmd.cpp onShellCommand ->PMS->PackageManagerShellCommand | 通过adb 调用cmd 命令,执行cmd package,最终通过PackageManagerShellCommand 调用runinstall 函数执行安装 | pm install | pm、cmd package | 在串口或者adb shell 进入终端后,通过pm 命令安装,cmd package后续步骤与adb install 一致 | 应用市场安装 | Android 4.4:PackageManager->installPackage Android 9.0 通过PackageManager->getPackageInstaller->createSession/session.commit | Android 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;
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 , null , -1 , callingUid);
final InstallParams params = new InstallParams(origin, null , observer,
installFlags, installerPackageName, null , verificationInfo, user,
null , null ,
null );
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");
}
} else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
........
if (params.startCopy()) {
........
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 {
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;
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");
}
}
...
if (onInt && onSd) {
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 {
...
}
...
final InstallArgs args = createInstallArgs(this);
if (ret == PackageManager.INSTALL_SUCCEEDED) {
...
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安装的剩余动作,并发送安装成功的广播 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 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);
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);
}
...
try {
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) {
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) {
replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, res);
} else {
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进行安装。 第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");
String pkgName = pkg.packageName;
if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
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)) {
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
+ " without first uninstalling.");
return;
}
}
try {
PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
System.currentTimeMillis(), user);
updateSettingsLI(newPackage, installerPackageName, null, res, user);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
prepareAppDataAfterInstallLIF(newPackage);
} else {
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。
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文件,然后将它们再次存到内存中使用。
|