前言:大家都知道,手机关机以后,就是一个冰冷的砖头,只能用来做防身的利器,但是开机后,点击桌面上的任何一个图片,都能开启一个APP,这说明在开机过程中,系统把已经安装好的APP加载到内存中,这到底是怎么做的?
所以我们可以推断,在安卓系统中肯定存在这么一块区域,用于存放已经安装的APP的信息,在开机的时候,通过系统扫描,这块区域,把对应的内容加载到内存中去。
其次,我们知道了在Android系统中存在这样一块区域,在开机的的时候,加载这块区域的信息,从而实现加载在内存中去。那么我们继续反推断,那这块区域的信息,是怎么来的?
应该在安装这个APK的时候,把这个APK的信息写入到该区域的。这样就可以实现了在安卓系统一次安装后,在删除APK文件后,还可以运行APP了。
该篇文章是讲述apk的安装流程,想要了解apk打包、app启动 流程的同学可以点击下面的传送门:
apk打包流程
app启动流程详解
回到apk的安装话题上来,上面说的Android区域其实就是:“/data目录”下的system目录,这个目录用来保存很多系统文件。主要工作是创建了5个位于目录/data/system的File对象,分别是:
这5个文件中pakcages-back.xml和pakcages-stoped-backup.xml是备份文件。当Android对文件packages.xml和pakcages-stoped.xml写之前,会先把它们备份,如果写文件成功了,再把备份文件删除。如果写的时候,系统出问题了,重启后在需要读取这两个文件时,如果发现备份文件存在,会使用备份文件的内容,因为源文件可能已经损坏了。其中packages.xml是PackageManagerServcie启动时,需要用到的文件。
为了更直观的看一下,下面copy一张手机Root后,在/data/system目录下 截图如下:
把packages.xml导出来,因为文件内容太大,所以展示局部内容如下:
.....
对上面的标签的含义,下面做一个简单的介绍:
在上面知道了apk的信息在系统的存储的位置和方式,有人就会有新的疑问:系统是怎么获取到apk的信息的?接下来就是要梳理apk的安装流程了,看了apk安装流程的解析,你就会知道apk的信息是怎么被系统获取到的了。
先介绍一下Apk安装的四种方式:
1. 系统应用安装:没有安装界面,在开机时自动完成。
2. 网络下载应用安装: 没有安装界面,在应用市场完成。
3. ADB命令安装: 没有安装界面,通过命令直接安装。
4. 外部设备安装: 有安装界面,通过SD卡等外部设备安装,由packageInstaller处理安装逻辑。
接下来介绍一下APK安装涉及到的几个常用目录:
1.system/app : 系统自带的应用程序,获得root权限才能删除。
2.data/app : 用户程序安装目录,安装时会把apk文件复制到此目录下。
3.data/data : 存放应用程序的数据。
4.data/dalvik-cache : 将apk中的dex文件安装到该目录下(dex文件是dalvik虚拟机的可执行文件,大小约为原始apk的四分之一)。
还有APK安装的预备知识点也说一下吧:
(1)PackageManagerService是由SystemServer启动,PMS负责应用的安装、卸载、权限检查等工作;
(2)在/system/app和/data/app目录下的apk文件,PMS在启动过程中,都会扫描安装;
(3)每次开机时,PMS都会在构造函数中对指定目录下的apk进行扫描,没有安装的apk就会触发安装;
在梳理源码之前先大概说一下apk安装的四大步骤:
(1)拷贝apk到指定的目录:默认情况下,用户安装的apk首先会拷贝到/data/app下,用户有访问/data/app目录的权限,但系统出厂的apk文件会被放到/system分区下,包括/system/app,/system/vendor/app,以及/system/priv-app等,该分区需要root权限的用户才能访问。
(2)加载apk、拷贝文件、创建应用的数据目录:为了加快APP的启动速度,apk在安装的时候,会首先将APP的可执行文件(dex)拷贝到/data/dalvik-cache目录下,缓存起来。再在/data/data/目录下创建应用程序的数据目录(以应用包名命令),用来存放应用的数据库、xml文件、cache、二进制的so动态库等。
(3)解析apk的AndroidManifest.xml文件:在安装apk的过程中,会解析apk的AndroidManifest.xml文件,将apk的权限、应用包名、apk的安装位置、版本、userID等重要信息保存在/data/system/packages.xml文件中。这些操作都是在PackageManagerService中完成
的。
(4)显示icon图标:应用程序经过PMS中的逻辑处理后,相当于已经注册好了,如果想要在Android桌面上看到icon图标,则需要Launcher将系统中已经安装的程序展现在桌面上。
概念说的差不多了,接下来就对不同的安装方式做源码的过程梳理。
系统在创建PackageManagerService实例时,会在PMS的构造函数中开始执行安装应用程序的逻辑。
在PMS的构造函数中做了如下几点重要操作:
1.创建Settings对象,添加shareUserId。
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
Settings是Android的包的全局管理者,用于协助PackageManagerService保存所有的安装包信息,同时用于存储系统执行过程中的一些设置,PackageManagerService和Settings之间的类图关系如下:
2.通过PackageManagerService构造函数参数获取应用安装器Installer。
mInstaller = installer;
3.获取SystemConfig实例,读取“/system/etc/permissions/*.xml”资源文件,从资源文件中获取mGlobalsGids(Group-ids)、mSystemPermissions(系统权限)、mAvailableFeatures(系统支持的features)属性。
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
4.创建系统消息处理线程。
mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
5.执行com.android.server.pm.Settings中的readLPw方法,读取安装包中的信息,并解析成对应的数据结构。
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
// Propagate permission configuration in to package manager.
ArrayMap permConfig
= systemConfig.getPermissions();
for (int i=0; i libConfig = systemConfig.getSharedLibraries();
for (int i=0; i
其中,读取的重要文件就是之前已经说过的保存apk信息的那几个:packages.xml、packages-backup.xml、packages.list、packages-stopped.xml、packages-stopped-backup.xml。并且这几个目录在创建Settings对象时就已经被封装成了对应的File文件。
6.执行PMS中的scanDirLI方法扫描系统安装目录和非系统apk信息。
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
// Find base frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
其中,系统安装目录有:
/system/framework 系统库;
/system/app 默认的系统应用;
/vendor/app 厂商定制的应用;
非系统apk信息目录有:
/data/app/;
/system/priv-app/;
/data/app-private/;
到此,PMS构造函数中主要的逻辑操作就介绍完了,接下来继续探究扫描安装过程:
(1)深入到PackageManagerService—>scanDirLI方法中:
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + dir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
try {
scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);//注释1
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
// Delete invalid userdata apps
if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
if (file.isDirectory()) {
mInstaller.rmPackageDir(file.getAbsolutePath());
} else {
file.delete();
}
}
}
}
}
在注释1处,对于目录中的每一个文件,如果是已apk作为后缀名的,就会调用PackageManagerService—>scanPackageLI方法进行扫描解析。
(2)深入到PackageManagerService—>scanPackageLI方法中:
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser();//注释1
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
final PackageParser.Package pkg;
try {
pkg = pp.parsePackage(scanFile, parseFlags);//注释2
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
}
......省略......
// reader
synchronized (mPackages) {//注释3
// Look to see if we already know about this package.
String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
// This package has been renamed to its original name. Let's
// use that.
ps = mSettings.peekPackageLPr(oldName);
}
// If there was no original package, see one for the real package name.
if (ps == null) {
ps = mSettings.peekPackageLPr(pkg.packageName);
}
// Check to see if this package could be hiding/updating a system
// package. Must look for it either under the original or real
// package name depending on our state.
updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);
}
......省略......
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);//注释4
/*
* If the system app should be overridden by a previously installed
* data, hide the system app now and let the /data/app scan pick it up
* again.
*/
if (shouldHideSystemApp) {
synchronized (mPackages) {
mSettings.disableSystemPackageLPw(pkg.packageName);
}
}
return scannedPkg;
}
在注释1处:创建一个PackageParser实例。
在注释2处:调用PackageParser的parsePackage函数来对apk安装包的AndroidManifest.xml文件扫描和提取证书信息,然后构建一个PackageParser.Package对象,并将其返回。
在注释3处:将解析返回的PackageParser对象中的信息保存到PMS中。
在注释4处:调用另一个重载的scanPackageLI方法来构建一个PackageSetting对象,这个对象保存的信息最后会通过writeLPr写入到/data/system/packages.xml文件中去。
(3)根据(2)中注释2深入到PackageParser—>parsePackage方法中:
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
这里是根据packageFile是否是目录分别调用parseClusterPackage和parseMonolithicPackage去解析。
其中parseClusterPackage方法如下:
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
final PackageLite lite = parseClusterPackageLite(packageDir, 0);
if (mOnlyCoreApps && !lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + packageDir);
}
final AssetManager assets = new AssetManager();
try {
...省略...
final File baseApk = new File(lite.baseCodePath);
final Package pkg = parseBaseApk(baseApk, assets, flags);//注释1
if (pkg == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse base APK: " + baseApk);
}
...省略...
pkg.codePath = packageDir.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
parseMonolithicPackage方法如下:
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
...省略...
final AssetManager assets = new AssetManager();
try {
final Package pkg = parseBaseApk(apkFile, assets, flags);//注释2
pkg.codePath = apkFile.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
从parseClusterPackage的注释1和parseMonolithicPackage的注释2可以看出都是调用parseBaseApk去解析。
往下继续探究parseBaseApk方法:
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
...省略...
try {
res = new Resources(assets, mMetrics, null);
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
final Package pkg = parseBaseApk(res, parser, flags, outError);//注释1
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
...省略...
} catch (PackageParserException e) {
throw e;
} catch (Exception e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
}
}
在注释1处可以看到是调用另一个重载的parseBaseApk方法对apk进行解析。
(4) 先回到(2)中注释4处深入到PackageManagerService—>另一个scanPackageLI方法中:
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
boolean success = false;
try {
final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
currentTime, user);//注释1
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
removeDataDirsLI(pkg.volumeUuid, pkg.packageName);
}
}
}
接着由注释1继续深入PackageManagerService—>scanPackageDirtyLI方法中:
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
final File scanFile = new File(pkg.codePath);
......省略......
// And now re-install the app.
ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
HasSystemUidErrors = true;//注释1
......省略......
//invoke installer to do the actual installation
int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.seinfo);//注释2
if (ret < 0) {
// Error from installer
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Unable to create data dirs [errorCode=" + ret + "]");
}
......省略......
}
从注释1和注释2处可以看出调用了PMS中的createDataDirsLI方法,给installed发送消息,为应用程序创建对应的数据目录,如果目录已经存在,也会重新创建一遍。
继续深入到PMS的createDataDirsLI方法中:
private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
int[] users = sUserManager.getUserIds();
int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);//注释1
if (res < 0) {
return res;
}
for (int user : users) {
if (user != 0) {
res = mInstaller.createUserData(volumeUuid, packageName,//注释2
UserHandle.getUid(user, uid), user, seinfo);
if (res < 0) {
return res;
}
}
}
return res;
}
从上面代码可知最后是调用mInstaller.install()函数完成APK安装。到此为止,系统应用的安装流程差不多就完成了。
在网络应用下载完成后,会自动调用PackageManagerService中的installPackage方法:
@Override
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride) {
installPackageAsUser(originPath, observer, installFlags, installerPackageName,
verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
}
在上面的代码里可以看到主要是调用了PMS—>installPackageAsUser方法:
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
......省略......
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, null, observer, params.installFlags,
installerPackageName, params.volumeUuid, verifParams, user, params.abiOverride,
params.grantedRuntimePermissions);
mHandler.sendMessage(msg);
}
这里主要是获取用户安装位置,将InstallParams对象封装在Message里,然后发一个Handler消息。
接下来深入到处理INIT_COPY的地方:PMS—>doHandleMessage方法中:
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
......省略......
if (!mBound) {
// If this is the only one pending we might
// have to bind to the service again.
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
params.serviceError();
return;
} else {
// Once we bind to the service, the first
// pending request will be processed.
mPendingInstalls.add(idx, params);
}
} else {
mPendingInstalls.add(idx, params);
// Already bound to the service. Just make
// sure we trigger off processing the first request.
if (idx == 0) {
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {
......省略......
} else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
if (params.startCopy()) {//注释1
// We are done... look for more work or to
// go idle.
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Checking for more work or unbind...");
// Delete pending install
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) {
if (mBound) {
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting delayed MCS_UNBIND");
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
// Unbind after a little delay, to avoid
// continual thrashing.
sendMessageDelayed(ubmsg, 10000);
}
} else {
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting MCS_BOUND for next work");
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
上面的逻辑是:如果msg.what是INIT_COPY:则连接DefaultContainerService服务,将我们要安装的信息方法HandlerParams的mPendingInstalls中,然后再发送MCS_BOUND消息。
如果msg.what是MCS_BOUND:则通过HandlerParams params = mPendingInstalls.get(0)获取要安装包的信息,然后清除包信息,如果还有其他包,则继续发MCS_BOUND消息,一直循环,直到安装包都安装完。
然后再调用PackageManagerService.HandlerParams—>startCopy方法:
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();//注释1
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();//注释2
return res;
}
其中startCopy中有两个重要的方法handleStartCopy和handleReturnCode。handleStartCopy中会检查应用是否能安装成功,如果不能安装成功,则会返回failed的CODE;返回res标识,判断是否安装成功。handleReturnCode方法有两处重载了,一个是删除时调用的,一个是安装时调用的,下面列出安装的地方:
@Override
void handleReturnCode() {
// If mArgs is null, then MCS couldn't be reached. When it
// reconnects, it will try again to install. At that point, this
// will succeed.
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
可以看到调用了processPendingInstall方法:
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
// Result object to be returned
PackageInstalledInfo res = new PackageInstalledInfo();
res.returnCode = currentStatus;
res.uid = -1;
res.pkg = null;
res.removedInfo = new PackageRemovedInfo();
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageLI(args, res);//注释1
}
args.doPostInstall(res.returnCode, res.uid);
}
......省略......
}
从注释1处可以看到,调用的是installPackageLI方法:
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
......省略......
if (replace) {
replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
installerPackageName, volumeUuid, res);
} else {
installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res);
}
......省略......
}
如果是替换安装,则走replacePackageLI的逻辑。追踪replacePackageLI的逻辑流程:
如果是第一次安装,则走installNewPackageLI。追踪installNewPackageLI的逻辑流程:
到了调用PackageParser.Package的scanPackageLI方法就到了系统应用安装过程中的“继续探究扫描安装过程”逻辑,后面的安装逻辑就与系统应用安装的逻辑一样了。
命令安装应用的入口是:framework/base/cmds/pm/src/com/android/commands/pm/Pm.java
应用安装的时候就会调用runInstall()方法:
private int runInstall() {
......省略......
try {
VerificationParams verificationParams = new VerificationParams(verificationURI,
originatingURI, referrerURI, VerificationParams.NO_UID, null);
mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
installerPackageName, verificationParams, abi, userId);//注释1
......省略......
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
}
}
从注释1处可以看到安装逻辑调用的是PackageManagerService—>installPackageAsUser方法。后面的调用逻辑就与“网络下载应用安装”的逻辑一样了。
通过外部设备安装应用,调用的是Android内部的PackageInstaller去完成的,其本身也是一个apk。
代码位置在:/packages/apps/PackageInstaller/。 用于显示安装apk,但其最终的本质还是调用PackageManagerService去完成安装的。
当点击文件管理器中的apk时,文件管理器就会启动PackageInstaller中的PackageInstallerActivity,并将apk的信息通过intent传递给PackageInstallerActivity。
(1)深入到PackageInstallerActivity—>onCreate方法:
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getPackageManager();
mInstaller = mPm.getPackageInstaller();//注释1
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
//注释2
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
finish();
return;
}
mSessionId = sessionId;
mPackageURI = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
mSessionId = -1;
mPackageURI = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
final boolean unknownSourcesAllowedByAdmin = isUnknownSourcesAllowedByAdmin();
final boolean unknownSourcesAllowedByUser = isUnknownSourcesEnabled();
//注释3
boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
......省略.....
// Block the install attempt on the Unknown Sources setting if necessary.
if (!requestFromUnknownSource) {
initiateInstall();//注释4
return;
}
......省略.....
if (!unknownSourcesAllowedByAdmin
|| (!unknownSourcesAllowedByUser && isManagedProfile)) {
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else if (!unknownSourcesAllowedByUser) {
// Ask user to enable setting first
showDialogInner(DLG_UNKNOWN_SOURCES);
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING);
} else {
initiateInstall();//注释5
}
}
注释1:获取PackageInstaller对象;
注释2:PackageInstaller检查权限;
注释3:PackageInstaller检查是否开启未知来源;
注释4、注释5:调用initiateInstall方法;
(2)继续深入到PackageInstallerActivity—>initiateInstall方法:
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
pkgName = oldName[0];
mPkgInfo.packageName = pkgName;
mPkgInfo.applicationInfo.packageName = pkgName;
}
// Check if package is already installed. display confirmation dialog if replacing pkg
try {
// This is a little convoluted because we want to get all uninstalled
// apps, but this may include apps with just data, and if it is just
// data we still want to count it as "installed".
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
mInstallFlowAnalytics.setReplace(mAppInfo != null);
mInstallFlowAnalytics.setSystemApp(
(mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));
startInstallConfirm();//调用
}
initiateInstall方法主要负责检查是否已经安装过,是否是系统应用等。然后继续调用了startInstallConfirm方法。
(3) 接着深入到PackageInstallerActivity—>startInstallConfirm方法:
private void startInstallConfirm() {
TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
tabHost.setup();
ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
adapter.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
@Override
public void onTabChanged(String tabId) {
if (TAB_ID_ALL.equals(tabId)) {
mInstallFlowAnalytics.setAllPermissionsDisplayed(true);
} else if (TAB_ID_NEW.equals(tabId)) {
mInstallFlowAnalytics.setNewPermissionsDisplayed(true);
}
}
});
// If the app supports runtime permissions the new permissions will
// be requested at runtime, hence we do not show them at install.
boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
>= Build.VERSION_CODES.M;
boolean permVisible = false;
mScrollView = null;
mOkCanInstall = false;
int msg = 0;
AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
if (mAppInfo != null) {
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system
: R.string.install_confirm_question_update;
mScrollView = new CaffeinatedScrollView(this);
mScrollView.setFillViewport(true);
boolean newPermissionsFound = false;
if (!supportsRuntimePermissions) {
newPermissionsFound =
(perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
mInstallFlowAnalytics.setNewPermissionsFound(newPermissionsFound);
if (newPermissionsFound) {
permVisible = true;
mScrollView.addView(perms.getPermissionsView(
AppSecurityPermissions.WHICH_NEW));
}
}
if (!supportsRuntimePermissions && !newPermissionsFound) {
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
TextView label = (TextView)inflater.inflate(R.layout.label, null);
label.setText(R.string.no_new_perms);
mScrollView.addView(label);
}
adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(
getText(R.string.newPerms)), mScrollView);
} else {
findViewById(R.id.tabscontainer).setVisibility(View.GONE);
findViewById(R.id.divider).setVisibility(View.VISIBLE);
}
if (!supportsRuntimePermissions && N > 0) {
permVisible = true;
LayoutInflater inflater = (LayoutInflater)getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View root = inflater.inflate(R.layout.permissions_list, null);
if (mScrollView == null) {
mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
}
((ViewGroup)root.findViewById(R.id.permission_list)).addView(
perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(
getText(R.string.allPerms)), root);
}
mInstallFlowAnalytics.setPermissionsDisplayed(permVisible);
if (!permVisible) {
if (mAppInfo != null) {
// This is an update to an application, but there are no
// permissions at all.
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? R.string.install_confirm_question_update_system_no_perms
: R.string.install_confirm_question_update_no_perms;
} else {
// This is a new application with no permissions.
msg = R.string.install_confirm_question_no_perms;
}
tabHost.setVisibility(View.GONE);
mInstallFlowAnalytics.setAllPermissionsDisplayed(false);
mInstallFlowAnalytics.setNewPermissionsDisplayed(false);
findViewById(R.id.filler).setVisibility(View.VISIBLE);
findViewById(R.id.divider).setVisibility(View.GONE);
mScrollView = null;
}
if (msg != 0) {
((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
}
mInstallConfirm.setVisibility(View.VISIBLE);
mOk = (Button)findViewById(R.id.ok_button);
mOk.requestFocus();
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this);
mCancel.setOnClickListener(this);
//add by wangqi begin{@
TabWidget tabWidget = tabHost.getTabWidget();
int childCount = tabWidget.getChildCount();
if (childCount == 2) {
final View left = tabWidget.getChildAt(0);
final View right = tabWidget.getChildAt(1);
left.setId(tabWidget.getId() + 1);
right.setId(tabWidget.getId() + 2);
right.setNextFocusLeftId(left.getId());
right.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && event.getAction() == KeyEvent.ACTION_DOWN) {
left.requestFocus();
}
return true;
}
});
}
// end @}
if (mScrollView == null) {
// There is nothing to scroll view, so the ok button is immediately
// set to install.
//mOk.setText(R.string.install);
mOkCanInstall = true;
} else {
mScrollView.setFullScrollAction(new Runnable() {
@Override
public void run() {
//mOk.setText(R.string.install);
mOkCanInstall = true;
}
});
}
}
从上面代码可以看到startInstallConfirm主要负责界面初始化,显示权限信息等。
界面初始化完成后,安装界面就会呈现在用户面前,如果用户想要安装这个应用程序,可以直接点击确认按钮,此时就会调用PackageInstallerActivity中的onClick方法:
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
mInstallFlowAnalytics.setInstallButtonClicked();
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
// We're only confirming permissions, so we don't really know how the
// story ends; assume success.
mInstallFlowAnalytics.setFlowFinishedWithPackageManagerResult(
PackageManager.INSTALL_SUCCEEDED);
finish();
} else {
startInstall();//注释1
}
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
} else if(v == mCancel) {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
mInstallFlowAnalytics.setFlowFinished(
InstallFlowAnalytics.RESULT_CANCELLED_BY_USER);
finish();
}
}
onClick方法中分别会对取消和确定按钮做处理,如果是确定按钮,就会调用注释1处的startInstall方法。
(4) 下面到PackageInstallerActivity—>startInstall方法:
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);//跳转的Activity
newIntent.putExtra(InstallAppProgress.EXTRA_MANIFEST_DIGEST, mPkgDigest);
newIntent.putExtra(
InstallAppProgress.EXTRA_INSTALL_FLOW_ANALYTICS, mInstallFlowAnalytics);
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
......省略.....
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);//注释1
finish();
}
startInstall方法启动后,将会跳转到注释1处的InstallAppProgress界面,并关掉当前的PackageInstallerActivity。
(5) 接着深入到InstallAppProgress.java文件中:
当InstallAppProgress.java启动时,会先调用InstallAppProgress.java—>onCreate方法:
@Override
public void onCreate(Bundle icicle) {
......省略.....
initView();
}
public void initView() {
......省略.....
if ("package".equals(mPackageURI.getScheme())) {
try {
pm.installExistingPackage(mAppInfo.packageName);
observer.packageInstalled(mAppInfo.packageName,
PackageManager.INSTALL_SUCCEEDED);
} catch (PackageManager.NameNotFoundException e) {
observer.packageInstalled(mAppInfo.packageName,
PackageManager.INSTALL_FAILED_INVALID_APK);
}
} else {
pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
installerPackageName, verificationParams, null);
}
}
可以看到有两条安装逻辑,我这里只探索else中的逻辑。else中实际上是调用了ApplicationPackageManager的installPackageWithVerificationAndEncryption方法来安装。
(6) 再到ApplicationPackageManager—>installPackageWithVerificationAndEncryption方法:
@Override
public void installPackageWithVerificationAndEncryption(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
encryptionParams);//注释1
}
注意installPackageWithVerificationAndEncryption方法上有个Override,说明继承于父类的PackageManager中。
从注释1中可以看到调用的是installCommon方法:
private void installCommon(Uri packageURI,
PackageInstallObserver observer, int flags, String installerPackageName,
VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
......省略.....
try {
mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
verificationParams, null);//注释1
} catch (RemoteException ignored) {
}
}
从注释1中可以看到调用的是PMS中的installPackage方法,到这里后续的逻辑就与前面的“网络下载应用安装”中的逻辑一样了。
结论:看到这里咱们就可以对apk的安装过程有清晰的感知了,在四种安装方式中每种安装方式的入口部分是特殊的流程,但是后面的流程都是一致的。
并且咱们对之前的疑惑也算是有了答案:apk的信息就是在安装的过程中通过对apk解析得到其信息并保存在对应的文件中的。
总结APK的安装流程如下:
复制APK安装包到/data/app目录下,解压缩并扫描安装包,向资源管理器注入APK资源,解析AndroidManifest文件,并在/data/data目录下创建对应的应用数据目录,然后针对Dalvik/ART环境优化dex文件,保存到dalvik-cache目录,将AndroidManifest文件解析出的组件、权限注册到PackageManagerService并发送广播。