Android的应用管理主要是通过PackageManagerService来完成的,PackageManagerService服务负责各种APK包的安装,卸载,优化和查询.
PackageManagerService在系统启动时会扫描所有APK文件和jar包,然后把它们的信息读取出来,保存在内存中,这样系统运行时就能迅速找到各种应用和组件的信息.扫描过程中如果遇到没有优化的文件,还要执行转换工作,将APP文件中的dex格式转换成oat格式(Android5.0之前是转换成odex格式);启动后,将提供安装包的信息查询服务及应用的安装和卸载服务.
system/priv-app ---- 存放一些系统底层应用(Settings,SystemUI等,拥有的权限最高)
system/app ---- 存放系统级的应用(phone,Contacts等)
data/app ---- 存放用户安装的应用
data/data ---- 存放安装应用程序的数据
data/dalvik-cache ---- 存放大部分的apk文件和jar包的odex版本,odex是一种优化过的格式,执行速度比apk文件中的dex格式更快,如果系统运行在art模式下,这里保存的是oat格式的文件,这种文件格式是Linux的ELF格式的一种私有形式
data/system/packages.xml ---- 记录系统中所有安装的应用信息,包括基本信息、签名和权限
data/system/packages.list ---- 保存普通应用的数据目录和uid等信息
PackageManagerService的构造中会扫描对应目录下的apk,完成安装
调用PackageManager的installPackage方法执行安装
调用:msm8976/repo/system/core/adb/commandline.cpp中install_app方法,该方法调用pm_command通过send_shell_command方法将数据发送到手机端的adbd守护进程中,adbd在收到PC中Console发来的数据之后启动一个Shell,然后执行pm脚本(pm位于/system/bin目录下).
pm脚本通过app_process执行pm.jar包的main函数(\system\framework\pm.jar)
对应源码: msm8976/repo/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
private int runInstall() {
...
...
LocalPackageInstallObserver obs = new LocalPackageInstallObserver();
try {
VerificationParams verificationParams = new VerificationParams(verificationURI,
originatingURI, referrerURI, VerificationParams.NO_UID, null);
mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
installerPackageName, verificationParams, abi, userId);
synchronized (obs) {
while (!obs.finished) {
try {
obs.wait();
} catch (InterruptedException e) {
}
}
if (obs.result == PackageManager.INSTALL_SUCCEEDED) {
System.out.println("Success");
return 0;
} else {
System.err.println("Failure ["
+ installFailureToString(obs)
+ "]");
return 1;
}
}
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
}
}
调用PMS的installPackageAsUser完成安装
安装本地apk,有安装界面,由package/app/PackageInstaller应用处理安装及卸载过程的界面
以上方式都有PackageInstallObserver来监听安装是否成功
复制APK安装到data/app目录下;解析APK信息;解压安装包的dex文件,执行dexopt转换操作,把转换过的文件保存到dalvik-cache目录,更新apk安装后的一些信息完成安装
安装应用后,会在/data/system/packages.xml文件中写入app的信息.同理删除某个app时,也会从中删除该app的信息.尤其是内置应用,更需要将app的残留删除干净,不然再次安装app会报错:INSTALL_FAILED_UPDATE_INCOMPATIBLE
在应用中,我们通常是调用Context的getPackageManager()方法返回PackageManager对象,源码如下:
// msm8976/repo/frameworks/base/core/java/android/app/ContextImpl.java
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
这里返回的实际是ApplicationPackageManager对象,这个对象创建时使用IPackageManager对象作为参数,IPackageManager是PackageManagerService实现的AIDL接口,通过binder来获取PackageManagerService对象,源码如下:
// msm8976/repo/frameworks/base/core/java/android/app/ActivityThread.java
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
因此ApplicationPackageManager是PackageManagerService的代理对象.ApplicationPackageManager继承自PackageManager,PackageManager中定义了可以操作PackageManagerService的接口.
在PackageManager中,实现对apk安装的方法是installExistingPackage和installPackageWithVerificationAndEncryption(前者是通过包名去升级应用,后者是安装新的应用),对应源码如下:
// msm8976/repo/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java
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);
}
}
最终调用到PackageManagerService中的installExistingPackageAsUser和installPackage方法继续安装逻辑(这也就是传说中静默安装需要调用的方法)
PackageManagerService的构造中会扫描对应目录下的apk执行安装内置apk,同时会初始化两个重要的成员变量mInstaller和mInstallerService,mInstallerService是PackageInstallerService的实例,主要来管理安装会话,mInstaller是类Installer的实例,这个类比较简单,它有一个mInstaller的成员变量,这个变量是InstallerConnection的实例.InstallerConnection类中存在与守护进程Install通讯的Socket命令通道,实际上系统中进行APK文件格式转换等工作最后是由InstallID进程来完成;InstallerConnection中通过socket在系统installd进程中完成安装
//源码路径: msm8976/repo/frameworks/base/core/java/com/android/internal/os/InstallerConnection.java
private boolean connect() {
if (mSocket != null) {
return true;
}
Slog.i(TAG, "connecting...");
try {
mSocket = new LocalSocket();
LocalSocketAddress address = new LocalSocketAddress("installd",
LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);
mIn = mSocket.getInputStream();
mOut = mSocket.getOutputStream();
} catch (IOException ex) {
disconnect();
return false;
}
return true;
}
为什么需要installID进程?因为PMS运行在system_server中,是一个System用户,system用户并没有访问应用程序目录的权限,而InstallID的作用是处理root权限的操作.
installd源码路径:msm8976/repo/frameworks/native/cmds/installd
继续安装逻辑,PackageManagerService中installPackage会调用installPackageAsUser方法,该方法首先调用enforceCallingOrSelfPermission判断调用安装的程序是否申请了安装权限(所以要实现静默安装也需要申请该权限).这里判断安装来源,然后通过mHandler发送INIT_COPY的消息,这个mHandler运行在一个HandlerThread中
@Override
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 int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
try {
if (observer != null) {
observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
}
} catch (RemoteException re) {
}
return;
}
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
// Caller holds INSTALL_PACKAGES permission, so we're less strict
// about installerPackageName.
installFlags &= ~PackageManager.INSTALL_FROM_ADB;
installFlags &= ~PackageManager.INSTALL_ALL_USERS;
}
UserHandle user;
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}
// Only system components can circumvent runtime permissions when installing.
if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
&& mContext.checkCallingOrSelfPermission(Manifest.permission
.INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
throw new SecurityException("You need the "
+ "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
verificationParams.setInstallerUid(callingUid);
final File originFile = new File(originPath);
final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
null, verificationParams, user, packageAbiOverride, null);
mHandler.sendMessage(msg);
}
INIT_COPY主要是确保DefaultContainerService已bound,DefaultContainerService是一个应用服务,负责实现APK等相关资源文件在内部或外部存储器上的存储工作.而MCS_BOUND中则执行了
params.startCopy(),将apk文件复制到data/app目录下.复制的关键代码如下:
/*
* Invoke remote method to get package information and install
* location values. Override install location based on default
* policy if needed and then create install arguments based
* on the install location.
*/
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
...
...
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
PackageInfoLite pkgLite = null;
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 {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
/*
* If we have too little free space, try to free cache
* before giving up.
*/
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// TODO: focus freeing disk space on the target device
final StorageManager storage = StorageManager.from(mContext);
final long lowThreshold = storage.getStorageLowBytes(
Environment.getDataDirectory());
final long sizeBytes = mContainerService.calculateInstalledSize(
origin.resolvedPath, isForwardLocked(), packageAbiOverride);
if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);
}
/*
* The cache free must have deleted the file we
* downloaded to install.
*
* TODO: fix the "freeCache" call to not delete
* the file we care about.
*/
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
}
}
...
...
final InstallArgs args = createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
/*
* ADB installs appear as UserHandle.USER_ALL, and can only be performed by
* UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
*/
int userIdentifier = getUser().getIdentifier();
if (userIdentifier == UserHandle.USER_ALL
&& ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
userIdentifier = UserHandle.USER_OWNER;
}
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
: getPackageUid(mRequiredVerifierPackage, userIdentifier);
...
{
/*
* No package verification is enabled, so immediately start
* the remote call to initiate copy using temporary file.
*/
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
handleStartCopy的核心是copyApk,其它是一些安全校验(存储空间检查,权限检查等)
startCopy方法走完copy apk的逻辑之后来到了handleReturnCode方法,然后调用processPendingInstall方法,这个方法调用installPackageLI来执行解析package等操作之后通过mHandler发送一个POST_INSTALL消息完成安装(发送Intent.ACTION_PACKAGE_ADDED广播)
installPackageLI首先解析package包,然后做大量签名和权限校验工作,该方法又会调用到installNewPackageLI,这个方法主要做了两件事: scanPackageLI负责安装,updateSettingLI完成安装后的设置信息更新
scanPackageLI调用scanPackageDirtyLI实现具体的逻辑,摘取该方法中一段
if ((scanFlags & SCAN_NO_DEX) == 0) {
int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */,
forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */,
(scanFlags & SCAN_BOOTING) == 0);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI");
}
}
执行mPackageDexOptimizer.performDexOpt(x,x,x)方法,继续跟代码最后调用到performDexOptLI方法执行dexopt操作,同PMS构造方法中一样,通过socket与installd进程通讯完成转换工作
Apk文件其实是一个压缩包,我们的代码最终都编译成了.dex文件,但为了提高运行性能,android系统并不会直接执行.dex,而是在安装过程中执行dexopt操作来优化.dex文件,最终系统运行的是优化后的’odex’文件(odex文件的后缀也是.dex,路径在data/dalvik-cache).对于dalvik虚拟机,dexopt就是优化操作,而对于art虚拟机,dexopt执行的则是dex2oat操作,将.dex文件翻译成oat文件.