概述

这里主要介绍 PackageManagerService(简称PMS)的启动 和 一个应用的安装过程。这里只是大致总结,供参考,不少地方同样需要进一步深入了解学习的。

该篇相关代码也是基于AndroidQ的。

PMS也是核心服务之一,管理包相关内容,解析AndroidManifest.xml、管理应用等,最常见的是应用的安装和卸载。

和AMS类似,PMS提供服务也是通过binder完成,也有相应的服务端和客户端。

PMS的几个相关类:

IPackageManager.aidl:接口,定义了服务端和客户端之间通信的函数方法。

PackageManagerService:服务端。继承IPackageManager.Stub,继承Binder。

PackageManager:抽象类,对外的接口(可调用,SDK)。可以通过Context#getPackageManager获取,可以看下下面源码中关于PackageManager的注释部分。

ApplicationPackageManager:客户端。PackageManager实现类,通过内部的mPM变量参与Binder通信。

//PackageManager.java
/**
* Class for retrieving various kinds of information related to the application
* packages that are currently installed on the device.
*
* You can find this class through {@link Context#getPackageManager}.
*/
public abstract class PackageManager {
......
} //ApplicationPackageManager.java
protected ApplicationPackageManager(ContextImpl context,
IPackageManager pm) {
mContext = context;
mPM = pm;
}

PMS的启动

之前已经总结过 AMS的启动过程 以及 应用的第一次启动过程 ,比较详细,关于PMS的启动就容易理解了。

PMS启动过程 和 AMS启动过程 类似,都是在SystemServer启动之后完成的,若想详细了解可以先参考AMS的启动过程了解下,关于PMS的启动这里就大致说明下。

SystemServer中启动PMS

PMS的启动也是在SystemServer启动之后,也属于引导服务,在startBootstrapServices()中启动。

//SystemServer.java
private void startBootstrapServices() {
......
//Installer服务,真正安装的服务,与installd交互
Installer installer = mSystemServiceManager.startService(Installer.class);
......
//标记1
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
......
//标记2
mPackageManager = mSystemContext.getPackageManager();
} private void startOtherServices() {
......
//完成dex优化
mPackageManagerService.updatePackagesIfNeeded();
......
//清理磁盘,释放空间
mPackageManagerService.performFstrimIfNeeded();
......
//PMS准备就绪
mPackageManagerService.systemReady();
......
}

可以看注释大致了解下。

这里主要看下标记1和标记2处,PackageManagerService.main()和mSystemContext.getPackageManager()。

标记1:mSystemContext.getPackageManager()

先看下mSystemContext.getPackageManager(),一路看下去,就看到如下代码。即Context#getPackageManager是创建了ApplicationPackageManager,mPM得到赋值 IPackageManager对象,可以通过mPm参与PMS通信。

//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;
}

标记2:PackageManagerService.main():

这是真正创建PMS服务,并启动。

//PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
//初始化PMS
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
//注册服务 package
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
//注册服务 package_native
ServiceManager.addService("package_native", pmn);
return m;
}

这里很简单,主要检查PMS环境,然创建并注册了两个服务package和package_native。

接下来主要看 创建PMS对象过程做了些什么。

PMS的创建

PMS的构造方法很长,下面折叠起来了,要了解可以打开。

代码已折叠,点击显示PMS的完整构造方法
    public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis()); if (mSdkVersion <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
} mContext = context; mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mMetrics = new DisplayMetrics();
mInstaller = installer; // Create sub-components that provide services / data. Order here is important.
synchronized (mInstallLock) {
synchronized (mPackages) {
// Expose private service for system components to use.
LocalServices.addService(
PackageManagerInternal.class, new PackageManagerInternalImpl());
sUserManager = new UserManagerService(context, this,
new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages);
mComponentResolver = new ComponentResolver(sUserManager,
LocalServices.getService(PackageManagerInternal.class),
mPackages);
mPermissionManager = PermissionManagerService.create(context,
mPackages /*externalLock*/);
mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
mSettings = new Settings(Environment.getDataDirectory(),
mPermissionManager.getPermissionSettings(), 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);
mSettings.addSharedUserLPw("android.uid.se", SE_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
mSeparateProcesses = null;
Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
} else {
mDefParseFlags = 0;
mSeparateProcesses = separateProcesses.split(",");
Slog.w(TAG, "Running with debug.separate_processes: "
+ separateProcesses);
}
} else {
mDefParseFlags = 0;
mSeparateProcesses = null;
} mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock);
mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper()); mViewCompiler = new ViewCompiler(mInstallLock, mInstaller); mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper()); getDefaultDisplayMetrics(context, mMetrics); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "get system config");
SystemConfig systemConfig = SystemConfig.getInstance();
mAvailableFeatures = systemConfig.getAvailableFeatures();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); mProtectedPackages = new ProtectedPackages(mContext); mApexManager = new ApexManager(context);
synchronized (mInstallLock) {
// writer
synchronized (mPackages) {
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
mInstantAppRegistry = new InstantAppRegistry(this); ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
= systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
addBuiltInSharedLibraryLocked(entry.filename, name);
} // Now that we have added all the libraries, iterate again to add dependency
// information IFF their dependencies are added.
long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
final int dependencyCount = entry.dependencies.length;
for (int j = 0; j < dependencyCount; j++) {
final SharedLibraryInfo dependency =
getSharedLibraryInfoLPr(entry.dependencies[j], undefinedVersion);
if (dependency != null) {
getSharedLibraryInfoLPr(name, undefinedVersion).addDependency(dependency);
}
}
} SELinuxMMAC.readInstallPolicy(); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "loadFallbacks");
FallbackCategoryProvider.loadFallbacks();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Clean up orphaned packages for which the code path doesn't exist
// and they are an update to a system app - caused by bug/32321269
final int packageSettingCount = mSettings.mPackages.size();
for (int i = packageSettingCount - 1; i >= 0; i--) {
PackageSetting ps = mSettings.mPackages.valueAt(i);
if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
&& mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
mSettings.mPackages.removeAt(i);
mSettings.enableSystemPackageLPw(ps.name);
}
} if (!mOnlyCore && mFirstBoot) {
requestCopyPreoptedFiles();
} String customResolverActivityName = Resources.getSystem().getString(
R.string.config_customResolverActivity);
if (!TextUtils.isEmpty(customResolverActivityName)) {
mCustomResolverComponentName = ComponentName.unflattenFromString(
customResolverActivityName);
} long startTime = SystemClock.uptimeMillis(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime); final String bootClassPath = System.getenv("BOOTCLASSPATH");
final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH"); if (bootClassPath == null) {
Slog.w(TAG, "No BOOTCLASSPATH found!");
} if (systemServerClassPath == null) {
Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
} File frameworkDir = new File(Environment.getRootDirectory(), "framework"); final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
if (mIsUpgrade) {
logCriticalInfo(Log.INFO,
"Upgrading from " + ver.fingerprint + " to " + Build.FINGERPRINT);
} // when upgrading from pre-M, promote system app permissions from install to runtime
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1; // When upgrading from pre-N, we need to handle package extraction like first boot,
// as there is no profiling data available.
mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N; mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q; int preUpgradeSdkVersion = ver.sdkVersion; // save off the names of pre-existing system packages prior to scanning; we don't
// want to automatically grant runtime permissions for new system apps
if (mPromoteSystemApps) {
Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
while (pkgSettingIter.hasNext()) {
PackageSetting ps = pkgSettingIter.next();
if (isSystemApp(ps)) {
mExistingSystemPackages.add(ps.name);
}
}
} mCacheDir = preparePackageParserCache(); // Set flag to monitor and not change apk file paths when
// scanning install directories.
int scanFlags = SCAN_BOOTING | SCAN_INITIAL; if (mIsUpgrade || mFirstBoot) {
scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
} // Collect vendor/product/product_services overlay packages. (Do this before scanning
// any apps.)
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR,
0);
scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT,
0);
scanDirTracedLI(new File(PRODUCT_SERVICES_OVERLAY_DIR),
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES,
0);
scanDirTracedLI(new File(ODM_OVERLAY_DIR),
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_ODM,
0);
scanDirTracedLI(new File(OEM_OVERLAY_DIR),
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_OEM,
0); mParallelPackageParserCallback.findStaticOverlayPackages(); // Find base frameworks (resource packages without code).
scanDirTracedLI(frameworkDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_NO_DEX
| SCAN_AS_SYSTEM
| SCAN_AS_PRIVILEGED,
0);
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
} // Collect privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRIVILEGED,
0); // Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirTracedLI(systemAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM,
0); // Collect privileged vendor packages.
File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
try {
privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(privilegedVendorAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR
| SCAN_AS_PRIVILEGED,
0); // Collect ordinary vendor packages.
File vendorAppDir = new File(Environment.getVendorDirectory(), "app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(vendorAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR,
0); // Collect privileged odm packages. /odm is another vendor partition
// other than /vendor.
File privilegedOdmAppDir = new File(Environment.getOdmDirectory(),
"priv-app");
try {
privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(privilegedOdmAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR
| SCAN_AS_PRIVILEGED,
0); // Collect ordinary odm packages. /odm is another vendor partition
// other than /vendor.
File odmAppDir = new File(Environment.getOdmDirectory(), "app");
try {
odmAppDir = odmAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(odmAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR,
0); // Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirTracedLI(oemAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_OEM,
0); // Collected privileged /product packages.
File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
try {
privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(privilegedProductAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT
| SCAN_AS_PRIVILEGED,
0); // Collect ordinary /product packages.
File productAppDir = new File(Environment.getProductDirectory(), "app");
try {
productAppDir = productAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(productAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT,
0); // Collected privileged /product_services packages.
File privilegedProductServicesAppDir =
new File(Environment.getProductServicesDirectory(), "priv-app");
try {
privilegedProductServicesAppDir =
privilegedProductServicesAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(privilegedProductServicesAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES
| SCAN_AS_PRIVILEGED,
0); // Collect ordinary /product_services packages.
File productServicesAppDir = new File(Environment.getProductServicesDirectory(), "app");
try {
productServicesAppDir = productServicesAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(productServicesAppDir,
mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES,
0); // Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
// Stub packages must either be replaced with full versions in the /data
// partition or be disabled.
final List<String> stubSystemApps = new ArrayList<>();
if (!mOnlyCore) {
// do this first before mucking with mPackages for the "expecting better" case
final Iterator<PackageParser.Package> pkgIterator = mPackages.values().iterator();
while (pkgIterator.hasNext()) {
final PackageParser.Package pkg = pkgIterator.next();
if (pkg.isStub) {
stubSystemApps.add(pkg.packageName);
}
} final Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next(); /*
* If this is not a system app, it can't be a
* disable system app.
*/
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
} /*
* If the package is scanned, it's not erased.
*/
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
/*
* If the system app is both scanned and in the
* disabled packages list, then it must have been
* added via OTA. Remove it from the currently
* scanned package so the previously user-installed
* application can be scanned.
*/
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
logCriticalInfo(Log.WARN,
"Expecting better updated system app for " + ps.name
+ "; removing system app. Last known"
+ " codePath=" + ps.codePathString
+ ", versionCode=" + ps.versionCode
+ "; scanned versionCode=" + scannedPkg.getLongVersionCode());
removePackageLI(scannedPkg, true);
mExpectingBetter.put(ps.name, ps.codePath);
} continue;
} if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
logCriticalInfo(Log.WARN, "System package " + ps.name
+ " no longer exists; it's data will be wiped");
// Actual deletion of code and data will be handled by later
// reconciliation step
} else {
// we still have a disabled system package, but, it still might have
// been removed. check the code path still exists and check there's
// still a package. the latter can happen if an OTA keeps the same
// code path, but, changes the package name.
final PackageSetting disabledPs =
mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()
|| disabledPs.pkg == null) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
} else {
// We're expecting that the system app should remain disabled, but add
// it to expecting better to recover in case the data version cannot
// be scanned.
mExpectingBetter.put(disabledPs.name, disabledPs.codePath);
}
}
}
} //delete tmp files
deleteTempPackageFiles(); final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get(); // Remove any shared userIDs that have no associated packages
mSettings.pruneSharedUsersLPw();
final long systemScanTime = SystemClock.uptimeMillis() - startTime;
final int systemPackagesCount = mPackages.size();
Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
+ " ms, packageCount: " + systemPackagesCount
+ " , timePerPackage: "
+ (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+ " , cached: " + cachedSystemApps);
if (mIsUpgrade && systemPackagesCount > 0) {
MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time",
((int) systemScanTime) / systemPackagesCount);
}
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0); // Remove disable package settings for updated system apps that were
// removed via an OTA. If the update is no longer present, remove the
// app completely. Otherwise, revoke their system privileges.
for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
final PackageParser.Package pkg = mPackages.get(packageName);
final String msg; // remove from the disabled system list; do this first so any future
// scans of this package are performed without this state
mSettings.removeDisabledSystemPackageLPw(packageName); if (pkg == null) {
// should have found an update, but, we didn't; remove everything
msg = "Updated system package " + packageName
+ " no longer exists; removing its data";
// Actual deletion of code and data will be handled by later
// reconciliation step
} else {
// found an update; revoke system privileges
msg = "Updated system package " + packageName
+ " no longer exists; rescanning package on data"; // NOTE: We don't do anything special if a stub is removed from the
// system image. But, if we were [like removing the uncompressed
// version from the /data partition], this is where it'd be done. // remove the package from the system and re-scan it without any
// special privileges
removePackageLI(pkg, true);
try {
final File codePath = new File(pkg.applicationInfo.getCodePath());
scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ e.getMessage());
}
} // one final check. if we still have a package setting [ie. it was
// previously scanned and known to the system], but, we don't have
// a package [ie. there was an error scanning it from the /data
// partition], completely remove the package data.
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && mPackages.get(packageName) == null) {
removePackageDataLIF(ps, null, null, 0, false); }
logCriticalInfo(Log.WARN, msg);
} /*
* Make sure all system apps that we expected to appear on
* the userdata partition actually showed up. If they never
* appeared, crawl back and revive the system version.
*/
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
final File scanFile = mExpectingBetter.valueAt(i); logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system"); final @ParseFlags int reparseFlags;
final @ScanFlags int rescanFlags;
if (FileUtils.contains(privilegedAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRIVILEGED;
} else if (FileUtils.contains(systemAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM;
} else if (FileUtils.contains(privilegedVendorAppDir, scanFile)
|| FileUtils.contains(privilegedOdmAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR
| SCAN_AS_PRIVILEGED;
} else if (FileUtils.contains(vendorAppDir, scanFile)
|| FileUtils.contains(odmAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR;
} else if (FileUtils.contains(oemAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_OEM;
} else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT
| SCAN_AS_PRIVILEGED;
} else if (FileUtils.contains(productAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT;
} else if (FileUtils.contains(privilegedProductServicesAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES
| SCAN_AS_PRIVILEGED;
} else if (FileUtils.contains(productServicesAppDir, scanFile)) {
reparseFlags =
mDefParseFlags |
PackageParser.PARSE_IS_SYSTEM_DIR;
rescanFlags =
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_PRODUCT_SERVICES;
} else {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
continue;
} mSettings.enableSystemPackageLPw(packageName); try {
scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
}
}
} // Uncompress and install any stubbed system applications.
// This must be done last to ensure all stubs are replaced or disabled.
installSystemStubPackages(stubSystemApps, scanFlags); final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get()
- cachedSystemApps; final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
final int dataPackagesCount = mPackages.size() - systemPackagesCount;
Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+ " ms, packageCount: " + dataPackagesCount
+ " , timePerPackage: "
+ (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ " , cached: " + cachedNonSystemApps);
if (mIsUpgrade && dataPackagesCount > 0) {
MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",
((int) dataScanTime) / dataPackagesCount);
}
}
mExpectingBetter.clear(); // Resolve the storage manager.
mStorageManagerPackage = getStorageManagerPackageName(); // Resolve protected action filters. Only the setup wizard is allowed to
// have a high priority filter for these actions.
mSetupWizardPackage = getSetupWizardPackageName();
mComponentResolver.fixProtectedFilterPriorities(); mSystemTextClassifierPackage = getSystemTextClassifierPackageName(); mWellbeingPackage = getWellbeingPackageName();
mDocumenterPackage = getDocumenterPackageName();
mConfiguratorPackage = getDeviceConfiguratorPackageName();
mAppPredictionServicePackage = getAppPredictionServicePackageName();
mIncidentReportApproverPackage = getIncidentReportApproverPackageName(); // Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
updateAllSharedLibrariesLocked(null, Collections.unmodifiableMap(mPackages)); for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
// NOTE: We ignore potential failures here during a system scan (like
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
final List<String> changedAbiCodePath =
adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
final String codePathString = changedAbiCodePath.get(i);
try {
mInstaller.rmdex(codePathString,
getDexCodeInstructionSet(getPreferredInstructionSet()));
} catch (InstallerException ignored) {
}
}
}
// Adjust seInfo to ensure apps which share a sharedUserId are placed in the same
// SELinux domain.
setting.fixSeInfoLocked();
} // Now that we know all the packages we are keeping,
// read and update their last usage times.
mPackageUsage.read(mPackages);
mCompilerStats.read(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
Slog.i(TAG, "Time to scan packages: "
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds"); // If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion);
if (sdkUpdated) {
Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
+ mSdkVersion + "; regranting permissions for internal storage");
}
mPermissionManager.updateAllPermissions(
StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated, mPackages.values(),
mPermissionCallback);
ver.sdkVersion = mSdkVersion; // If this is the first boot or an update from pre-M, and it is a normal
// boot, then we need to initialize the default preferred apps across
// all defined users.
if (!onlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : sUserManager.getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(user.id);
primeDomainVerificationsLPw(user.id);
}
} // Prepare storage for system user really early during boot,
// since core system apps like SettingsProvider and SystemUI
// can't wait for user to start
final int storageFlags;
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
storageFlags = StorageManager.FLAG_STORAGE_DE;
} else {
storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
}
List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
true /* onlyCoreApps */);
mPrepareAppDataFuture = SystemServerInitThreadPool.get().submit(() -> {
TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
Trace.TRACE_TAG_PACKAGE_MANAGER);
traceLog.traceBegin("AppDataFixup");
try {
mInstaller.fixupAppData(StorageManager.UUID_PRIVATE_INTERNAL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
} catch (InstallerException e) {
Slog.w(TAG, "Trouble fixing GIDs", e);
}
traceLog.traceEnd(); traceLog.traceBegin("AppDataPrepare");
if (deferPackages == null || deferPackages.isEmpty()) {
return;
}
int count = 0;
for (String pkgName : deferPackages) {
PackageParser.Package pkg = null;
synchronized (mPackages) {
PackageSetting ps = mSettings.getPackageLPr(pkgName);
if (ps != null && ps.getInstalled(UserHandle.USER_SYSTEM)) {
pkg = ps.pkg;
}
}
if (pkg != null) {
synchronized (mInstallLock) {
prepareAppDataAndMigrateLIF(pkg, UserHandle.USER_SYSTEM, storageFlags,
true /* maybeMigrateAppData */);
}
count++;
}
}
traceLog.traceEnd();
Slog.i(TAG, "Deferred reconcileAppsData finished " + count + " packages");
}, "prepareAppData"); // If this is first boot after an OTA, and a normal boot, then
// we need to clear code cache directories.
// Note that we do *not* clear the application profiles. These remain valid
// across OTAs and are used to drive profile verification (post OTA) and
// profile compilation (without waiting to collect a fresh set of profiles).
if (mIsUpgrade && !onlyCore) {
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
for (int i = 0; i < mSettings.mPackages.size(); i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// No apps are running this early, so no need to freeze
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
}
ver.fingerprint = Build.FINGERPRINT;
} // Grandfather existing (installed before Q) non-system apps to hide
// their icons in launcher.
if (!onlyCore && mIsPreQUpgrade) {
Slog.i(TAG, "Whitelisting all existing apps to hide their icons");
int size = mSettings.mPackages.size();
for (int i = 0; i < size; i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
continue;
}
ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
UserHandle.USER_SYSTEM);
}
} // clear only after permissions and other defaults have been updated
mExistingSystemPackages.clear();
mPromoteSystemApps = false; // All the changes are done during package scanning.
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION; // can downgrade to reader
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "write settings");
mSettings.writeLPr();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis()); if (!mOnlyCore) {
mRequiredVerifierPackage = getRequiredButNotReallyRequiredVerifierLPr();
mRequiredInstallerPackage = getRequiredInstallerLPr();
mRequiredUninstallerPackage = getRequiredUninstallerLPr();
mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
if (mIntentFilterVerifierComponent != null) {
mIntentFilterVerifier = new IntentVerifierProxy(mContext,
mIntentFilterVerifierComponent);
} else {
mIntentFilterVerifier = null;
}
mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES,
SharedLibraryInfo.VERSION_UNDEFINED);
mSharedSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
SharedLibraryInfo.VERSION_UNDEFINED);
} else {
mRequiredVerifierPackage = null;
mRequiredInstallerPackage = null;
mRequiredUninstallerPackage = null;
mIntentFilterVerifierComponent = null;
mIntentFilterVerifier = null;
mServicesSystemSharedLibraryPackageName = null;
mSharedSystemSharedLibraryPackageName = null;
}
// PermissionController hosts default permission granting and role management, so it's a
// critical part of the core system.
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(); // Initialize InstantAppRegistry's Instant App list for all users.
final int[] userIds = UserManagerService.getInstance().getUserIds();
for (PackageParser.Package pkg : mPackages.values()) {
if (pkg.isSystem()) {
continue;
}
for (int userId : userIds) {
final PackageSetting ps = (PackageSetting) pkg.mExtras;
if (ps == null || !ps.getInstantApp(userId) || !ps.getInstalled(userId)) {
continue;
}
mInstantAppRegistry.addInstantAppLPw(userId, ps.appId);
}
} mInstallerService = new PackageInstallerService(context, this, mApexManager);
final Pair<ComponentName, String> instantAppResolverComponent =
getInstantAppResolverLPr();
if (instantAppResolverComponent != null) {
if (DEBUG_INSTANT) {
Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent);
}
mInstantAppResolverConnection = new InstantAppResolverConnection(
mContext, instantAppResolverComponent.first,
instantAppResolverComponent.second);
mInstantAppResolverSettingsComponent =
getInstantAppResolverSettingsLPr(instantAppResolverComponent.first);
} else {
mInstantAppResolverConnection = null;
mInstantAppResolverSettingsComponent = null;
}
updateInstantAppInstallerLocked(null); // Read and update the usage of dex files.
// Do this at the end of PM init so that all the packages have their
// data directory reconciled.
// At this point we know the code paths of the packages, so we can validate
// the disk file and build the internal cache.
// The usage file is expected to be small so loading and verifying it
// should take a fairly small time compare to the other activities (e.g. package
// scanning).
final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
for (int userId : userIds) {
userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
}
mDexManager.load(userPackages);
if (mIsUpgrade) {
MetricsLogger.histogram(null, "ota_package_manager_init_time",
(int) (SystemClock.uptimeMillis() - startTime));
}
} // synchronized (mPackages)
} // synchronized (mInstallLock) mModuleInfoProvider = new ModuleInfoProvider(mContext, this); // Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
// tidy.
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "GC");
Runtime.getRuntime().gc();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // The initial scanning above does many calls into installd while
// holding the mPackages lock, but we're mostly interested in yelling
// once we have a booted system.
mInstaller.setWarnIfHeld(mPackages); PackageParser.readConfigUseRoundIcon(mContext.getResources()); mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}

从构造方法中的EventLogTags的标记,大致分为5个阶段,根据这个大致了解下。

  1. BOOT_PROGRESS_PMS_START

    创建和保存需要使用的相关对象,下面列出一些比较重要的:
  • mInstaller:Installer对象,系统服务,与installd交互,管理包的安装、卸载、数据、迁移等。
  • sUserManager:UserManagerService,用户管理服务。
  • mPermissionManager:PermissionManagerServiceInternal,权限管理相关。
  • mSettings:Settings,保存动态设置信息。这里通过addSharedUserLPw设置shared uid到Settings中(有8种:system、phone、log、nfc、bluetooth、shell、se、networkstack)。这里的Settings是frameworks/base/services/core/java/com/android/server/pm/Settings.java而不是frameworks/base/core/java/android/provider/Settings.java下的。
  • mPackageDexOptimizer:PackageDexOptimizer,助手工具类,进行dex优化等,dexopt。
  • mDexManager:DexManager,dex管理类,跟踪dex文件的使用等。
  • SystemConfig:用于获取系统的全局配置信息。
  • mApexManager:ApexManager,apex管理类,处理与apex服务通信。
  1. BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

    扫描系统目录中的app等,通过scanDirTracedLI()方法。

    这个阶段主要扫描的路径是:/vendor/overlay;/product/overlay;/product_services/overlay;/odm/overlay;/oem/overlay;/system/framework;/system/priv-app;/system/app;/vendor/priv-app;/vendor/app;/odm/priv-app;/odm/app;/oem/app;/product/priv-app;/product/app;/product_services/priv-app;/product_services/app;

    概括起来,就是vendor/product/product_services/odm/oem下的overlay目录;/system/framework/;system/vendor/odm/oem/product/product_services下的priv-app和app目录(没有/oem/priv-app)。

  2. BOOT_PROGRESS_PMS_DATA_SCAN_START

    扫描非系统目录app等。

    这个阶段主要扫描目录:/data/app;

  3. BOOT_PROGRESS_PMS_SCAN_END

    扫描完成,将结果写入文件中。

    通过mSettings.writeLPr();方法。写入到文件:/data/system/packages.xml和/data/system/packages.list。

    安装、卸载等都会更新这个目录。

  4. BOOT_PROGRESS_PMS_READY

    PMS准备完成,创建PackageInstallerService服务。mInstallerService = new PackageInstallerService(context, this, mApexManager);。

题外话:

apex是 AndroidQ开始提出的mainline计划后 而有的一种新格式,google将framework也分成一个个小模块,最终希望通过play store对system的核心内容的进行更新,不需要odm、oem干预。而这一个个模块与apk应用不同,因此有了apex这种格式。其实google在Android O开始就再弄的Treble计划,一步步将system与vendor分开了独立,最终整个系统可以直接使用一套公共的system.img即GSI(Generic System Image)。在Android R开始,kernel部分也开始使用这套,弄一套通用内核镜像GKI(Generic Kernel Image)。 Android各个版本之间变动是非常大的,可能用户感受不到。为了实现这些,引入了很多新的概念和技术。

/data/system/packages.xml,在 Android调试非常有用的命令集 总结中也提到过,这里就是该文件内容是什么,怎么来的。

所以预置应用越多,扫描时间会越长,影响开机时间。

一个应用的安装

应用的安装,最常见的是点击apk后的有界面的安装 和 开发中通过adb命令的静默安装。

下面关于这两种安装方式,简单说明下。

点击安装(有界面)

1.点击安装,PackageInstaller.apk处理过程

这个过程通过系统预置的PackageInstaller.apk或类似应用完成。

点击apk,进行一些验证后跳转到了PackageInstallerActivity,即弹出的”是否安装此应用“的确认安装界面。点击确认安装后,进入了InstallInstalling。

点击apk进行安装,PackageInstaller.apk是从InstallStart->PackageInstallerActivity->InstallInstalling。

注:这里以aosp中的PackageInstaller.apk说明(com.android.packageinstaller),在外单中 一般预置的是GMS包中的GooglePackageInstaller。

下面是大致的代码过程的关键代码:

//InstallStart.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
Intent nextActivity = new Intent(intent);
.....
if (isSessionInstall) {
nextActivity.setClass(this, PackageInstallerActivity.class);
}
......
if (nextActivity != null) {
startActivity(nextActivity);
}
} //PackageInstallerActivity.java
protected void onCreate(Bundle icicle) {
.......
bindUi();
} private void bindUi() {
......
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
startInstall();
}
}
}, null);
......
} private void startInstall() {
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallInstalling.class);
......
startActivity(newIntent);
finish();
}

可以清晰看到,点击确认安装后,通过startInstall()方法进入了InstallInstalling。

接着看InstallInstalling的处理:

//InstallInstalling.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
......
mPackageURI = getIntent().getData(); if ("package".equals(mPackageURI.getScheme())) {
.......
} else {
......
if (savedInstanceState != null) {
......
} else {
......
try {
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
} try {
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
} catch (IOException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE); mSessionCallback = new InstallSessionCallback();
}
} @Override
protected void onResume() {
super.onResume(); // This is the first onResume in a single life of the activity
if (mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId); if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
// we will receive a broadcast when the install is finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}

onCreate()中创建了一个安装会话并返回获得一个sessionId。onResume()中进行进一步安装。

2.创建安装的会话

先看onCreate()中创建安装会话,返回的sessionId很重要。

InstallEventReceiver是一个BroadcastReceiver,接收安装事件并回调给EventResultPersister。mSessionId是安装的会话id。

添加log可以看到,mPackageURI.getScheme()的值是"file"。最后关键执行到的是getPackageManager().getPackageInstaller().createSession(params)这句。

getPackageManager()获取PM对象,然后调用实现类ApplicationPackageManager中getPackageInstaller()得到的是PackageInstaller对象。最终调用的是PackageInstaller中的createSession()创建安装会话并返回sessionId。这里不列出代码,直接看createSession()方法。

//PackageInstaller.java
//frameworks/base/core/java/android/content/pm/PackageInstaller.java
private final IPackageInstaller mInstaller;
public int createSession(@NonNull SessionParams params) throws IOException {
try {
final String installerPackage;
......
return mInstaller.createSession(params, installerPackage, mUserId);
}
......
}

注意,这里调用的是mInstaller.createSession()。mInstaller是IPackageInstaller,一看就知道这也是通过binder调用的一个服务,这个服务是PackageInstallerService,即最终调用的是PackageInstallerService的createSession()方法创建了一个安装会话 并 返回了sessionId。

这里不细述创建安装会话的过程了。

3.安装会话创建后,进一步安装

接着看onResume()。这里主要是看mInstallingTask.execute()。

/**
* Send the package to the package installer and then register a event result observer that
* will call {@link #launchFinishBasedOnResult(int, int, String)}
*/
private final class InstallingAsyncTask extends AsyncTask<Void, Void,
PackageInstaller.Session> {
volatile boolean isDone; @Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
return null;
} session.setStagingProgress(0); try {
File file = new File(mPackageURI.getPath()); try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
int numRead = in.read(buffer); if (numRead == -1) {
session.fsync(out);
break;
} if (isCancelled()) {
session.close();
break;
} out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
float fraction = ((float) numRead / (float) sizeBytes);
session.addProgress(fraction);
}
}
}
} return session;
} catch (IOException | SecurityException e) {
Log.e(LOG_TAG, "Could not write package", e); session.close(); return null;
} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
} @Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId); PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT); session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
getPackageManager().getPackageInstaller().abandonSession(mSessionId); if (!isCancelled()) {
launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
}

InstallingAsyncTask是个异步任务。

doInBackground中,apk的信息(通过URI获取)通过IO流写入到PackageInstaller.Session中。在onPostExecute中,通过session.commit()发送安装。

Session就是通过sessionId获取打开的。

接下来主要看发送安装。

PackageInstaller.Session.commit()发送安装

//frameworks/base/core/java/android/content/pm/PackageInstaller.java
public static class Session implements Closeable {
protected final IPackageInstallerSession mSession; public void commit(@NonNull IntentSender statusReceiver) {
try {
mSession.commit(statusReceiver, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
} //PackageInstallerSession.java
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
......
if (!markAsCommitted(statusReceiver, forTransfer)) {
return;
}
if (isMultiPackage()) {
final SparseIntArray remainingSessions = mChildSessionIds.clone();
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(remainingSessions, statusReceiver)
.getIntentSender();
RuntimeException commitException = null;
boolean commitFailed = false;
for (int i = mChildSessionIds.size() - 1; i >= 0; --i) {
final int childSessionId = mChildSessionIds.keyAt(i);
try {
// commit all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mSessionProvider.getSession(childSessionId)
.markAsCommitted(childIntentSender, forTransfer)) {
commitFailed = true;
}
} catch (RuntimeException e) {
commitException = e;
}
}
if (commitException != null) {
throw commitException;
}
if (commitFailed) {
return;
}
}
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
}
}

最终调用到PackageInstallerSession中的commit()。主要看mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); ,即通过Handler发送了MSG_COMMIT的消息,进程间通信。

接着看消息处理代码:

//PackageInstallerSession.java
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_COMMIT:
handleCommit();
break;
......
} return true;
}
}; private void handleCommit() {
......
// For a multiPackage session, read the child sessions
// outside of the lock, because reading the child
// sessions with the lock held could lead to deadlock
// (b/123391593).
List<PackageInstallerSession> childSessions = getChildSessions(); try {
synchronized (mLock) {
commitNonStagedLocked(childSessions);
}
} catch (PackageManagerException e) {
......
}
} @GuardedBy("mLock")
private void commitNonStagedLocked(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
final PackageManagerService.ActiveInstallSession committingSession =
makeSessionActiveLocked();
if (committingSession == null) {
return;
}
if (isMultiPackage()) {
List<PackageManagerService.ActiveInstallSession> activeChildSessions =
new ArrayList<>(childSessions.size());
boolean success = true;
PackageManagerException failure = null;
for (int i = 0; i < childSessions.size(); ++i) {
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.ActiveInstallSession activeSession =
session.makeSessionActiveLocked();
if (activeSession != null) {
activeChildSessions.add(activeSession);
}
} catch (PackageManagerException e) {
failure = e;
success = false;
}
}
if (!success) {
try {
mRemoteObserver.onPackageInstalled(
null, failure.error, failure.getLocalizedMessage(), null);
} catch (RemoteException ignored) {
}
return;
}
mPm.installStage(activeChildSessions);
} else {
mPm.installStage(committingSession);
}
}

很清晰,最终通过mPm.installStage() 进入PMS进行处理。

4.安装进入PMS

直接看PMS的installStage()。

//PackageManagerService.java
void installStage(ActiveInstallSession activeInstallSession) {
if (DEBUG_INSTANT) {
if ((activeInstallSession.getSessionParams().installFlags
& PackageManager.INSTALL_INSTANT_APP) != 0) {
Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
}
}
final Message msg = mHandler.obtainMessage(INIT_COPY);
final InstallParams params = new InstallParams(activeInstallSession);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params; Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",
System.identityHashCode(msg.obj));
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(msg.obj)); mHandler.sendMessage(msg);
} class PackageHandler extends Handler {
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
if (params != null) {
if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
System.identityHashCode(params));
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
params.startCopy();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
break;
}
......
}
}
}

通过Handler发送了INIT_COPY消息,开始拷贝apk到执行地方进行进一步安装。

5.拷贝APK

看startCopy():

//PackageManagerService.java
private abstract class HandlerParams {
final void startCopy() {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
handleStartCopy();
handleReturnCode();
}
abstract void handleStartCopy();
abstract void handleReturnCode();
} class InstallParams extends HandlerParams {
@Override
public void handleStartCopy() {
int ret = PackageManager.INSTALL_SUCCEEDED; // If we're already staged, we've firmly committed to an install location
...... /*
* If we have too little free space, try to free cache
* before giving up.
*/
...... if (ret == PackageManager.INSTALL_SUCCEEDED) {
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
ret = PackageManager.INSTALL_FAILED_INVALID_APK;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
ret = PackageManager.INSTALL_FAILED_INVALID_URI;
} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
} else {
// Override with defaults if needed.
......
}
}
final InstallArgs args = createInstallArgs(this);
mVerificationCompleted = true;
mEnableRollbackCompleted = true;
Args = args;
......
mRet = ret;
} @Override
void handleReturnCode() {
if (mVerificationCompleted && mEnableRollbackCompleted) {
......
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
mRet = mArgs.copyApk();
}
processPendingInstall(mArgs, mRet);
}
}
}

startCopy()只有两个方法,handleStartCopy()和handleReturnCode()。

handleStartCopy()中进行安装前的各种验证,检查文件、空间、发送给所有sufficientVerifiers进行验证。 验证成功,即mRet == PackageManager.INSTALL_SUCCEEDED,在handleReturnCode()中进行apk拷贝工作。

拷贝apk目标路径

接着看下handleReturnCode()中的拷贝apk操作:mArgs.copyApk()。

mArgs这里是FileInstallArgs。

//PackageManagerService.java
private InstallArgs createInstallArgs(InstallParams params) {
if (params.move != null) {
return new MoveInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
} //PackageManagerService.java
class FileInstallArgs extends InstallArgs {
int copyApk() {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
return doCopyApk();
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} private int doCopyApk() {
......
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
resourceFile = tempDir;
......
int ret = PackageManagerServiceUtils.copyPackage(
origin.file.getAbsolutePath(), codeFile);
......
return ret;
}
} //PackageManagerServiceUtils.java
public static int copyPackage(String packagePath, File targetDir) {
if (packagePath == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
} try {
final File packageFile = new File(packagePath);
final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
copyFile(pkg.baseCodePath, targetDir, "base.apk");
if (!ArrayUtils.isEmpty(pkg.splitNames)) {
for (int i = 0; i < pkg.splitNames.length; i++) {
copyFile(pkg.splitCodePaths[i], targetDir,
"split_" + pkg.splitNames[i] + ".apk");
}
}
return PackageManager.INSTALL_SUCCEEDED;
} catch (PackageParserException | IOException | ErrnoException e) {
Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
}

拷贝到的目标文件是:/data/app/vmdl[sessionId].tmp/base.apk。若是多个,/data/app/vmdl[sessionId].tmp/split_[pkgname].apk。

例如:如我的一次安装 /data/app/vmdl1101585668.tmp/base.apk,这次安装的sessionId是1101585668。

注:用userdebug软件,安装抓取的log也能看出大致安装过程的信息,可以辅助。如搜索TAG为 PackageManager(PMS类的TAG)。

下面是路径相关代码:

//PackageInstallerService.java
public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
synchronized (mSessions) {
......
final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid);
......
}
} private File buildTmpSessionDir(int sessionId, String volumeUuid) {
final File sessionStagingDir = getTmpSessionDir(volumeUuid);
return new File(sessionStagingDir, "vmdl" + sessionId + ".tmp");
} private File getTmpSessionDir(String volumeUuid) {
return Environment.getDataAppDirectory(volumeUuid);
}

6.真正安装

copyFile完成拷贝后,回头看handleReturnCode(),mArgs.copyApk();后执行processPendingInstall(mArgs, mRet);开始真正的安装。

//PackageManagerService.java
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
if (args.mMultiPackageInstallParams != null) {
args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
} else {
//安装参数
PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
processInstallRequestsAsync(
res.returnCode == PackageManager.INSTALL_SUCCEEDED,
Collections.singletonList(new InstallRequest(args, res)));
}
} // Queue up an async operation since the package installation may take a little while.
private void processInstallRequestsAsync(boolean success,
List<InstallRequest> installRequests) {
mHandler.post(() -> {
if (success) {
for (InstallRequest request : installRequests) {
//如果之前安装失败,清理无用信息
request.args.doPreInstall(request.installResult.returnCode);
}
synchronized (mInstallLock) {
//进行安装
installPackagesTracedLI(installRequests);
}
for (InstallRequest request : installRequests) {
//安装失败,清理无用信息
request.args.doPostInstall(
request.installResult.returnCode, request.installResult.uid);
}
}
for (InstallRequest request : installRequests) {
restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
new PostInstallData(request.args, request.installResult, null));
}
});
} private PackageInstalledInfo createPackageInstalledInfo(
int currentStatus) {
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
return res;
}

看上述注释大致了解下,我们接着看installPackagesTracedLI()。

//PMS
@GuardedBy({"mInstallLock", "mPackages"})
private void installPackagesTracedLI(List<InstallRequest> requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
installPackagesLI(requests);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} private void installPackagesLI(List<InstallRequest> requests) {
......
try {
for (InstallRequest request : requests) {
// TODO(b/109941548): remove this once we've pulled everything from it and into
// scan, reconcile or commit.
final PrepareResult prepareResult;
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
//分析安装状态、分析包并检查验证
//检查包并分析完整性;检查SDK版本、静态库等;检查签名;设置权限;
prepareResult = preparePackageLI(request.args, request.installResult);
}
......
try {
//扫描apk
final List<ScanResult> scanResults = scanPackageTracedLI(
prepareResult.packageToScan, prepareResult.parseFlags,
prepareResult.scanFlags, System.currentTimeMillis(),
request.args.user);
}
......
}
executePostCommitSteps(commitRequest);
} finally {
......
}
}

installPackagesLI()方法很复杂,这里也不细述了。

installPackagesLI()对安装包本身以及安装环境等进行一些相关的检查验证。检查包的完整性、SDK版本、签名、共享库、设置权限等等,检查安装环境是否满足等。可以大致看下方法中这几个方法:

prepareResult = preparePackageLI(request.args, request.installResult);
final List<ScanResult> scanResults = scanPackageTracedLI(
prepareResult.packageToScan, prepareResult.parseFlags,
prepareResult.scanFlags, System.currentTimeMillis(),
request.args.user);
ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,

最后执行executePostCommitSteps()进一步进行安装,最终通过binder进入installd进程完成安装。这个过程也很复杂,本人也需进一步阅读。executePostCommitSteps()后续简单参考过程:

PackageManagerService.java
installPackagesLI()->executePostCommitSteps()->prepareAppDataAfterInstallLIF()->prepareAppDataLIF()->prepareAppDataLeafLIF()->mInstaller.createAppData()。
Installer.java:
createAppData()->mInstalld.createAppData()。

关于过程中一些路径操作多说一点:

在preparePackageLI()中有个rename的过程,将前面拷贝的文件目录重命名了下,所以安装后能在手机中看到的是重命名后的目录。

重命名规则:/data/app/[packagename]-[suffix]。

例如我的安装:PackageManager: Renaming /data/app/vmdl1101585668.tmp to /data/app/[packagename]-RDISkz-0p77Yh-pAwNtfFA==

相关代码:

private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
......
if (!args.doRename(res.returnCode, pkg)) {
throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
}
......
} class FileInstallArgs extends InstallArgs {
boolean doRename(int status, PackageParser.Package pkg) {
.....
final File targetDir = codeFile.getParentFile();
final File beforeCodeFile = codeFile;
final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
.....
Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
.....
}
} private File getNextCodePath(File targetDir, String packageName) {
File result;
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[16];
do {
random.nextBytes(bytes);
String suffix = Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP);
result = new File(targetDir, packageName + "-" + suffix);
} while (result.exists());
return result;
}

一点总结

点击安装,通过类似PackageInstaller.apk处理。

确认安装后,拷贝apk到/data/app/vmdl[sessionId].tmp/base.apk下。

进一步安装中,重命名路径为 /data/app/[packagename]-[suffix]。

如我的安装:拷贝到/data/app/vmdl1101585668.tmp/base.apk。重命名后是:/data/app/[packagename]-RDISkz-0p77Yh-pAwNtfFA==。

查看/data/app/[packagename]-RDISkz-0p77Yh-pAwNtfFA==目录,有base.apk lib/ oat/,安装后的apk、库、odex、vdex都在这个目录下。

系统预置的还在预置目录下,如/system/priv-app。

/data/data/[packagename]存放应用数据,一般有cache code_cache lib。

adb安装(无界面 静默安装)

这也是一个简单参考,不完整。

使用adb命令进行安装,下面大致可以参考:

//system/core/adb/client/commandline.cpp
int adb_commandline(int argc, const char** argv) {
......
else if (!strcmp(argv[0], "install")) {
if (argc < 2) error_exit("install requires an argument");
return install_app(argc, argv);
}
......
} //system/core/adb/client/adb_install.cpp
int install_app(int argc, const char** argv) {
......
switch (installMode) {
case INSTALL_PUSH:
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy, use_localagent);
case INSTALL_STREAM:
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy, use_localagent);
case INSTALL_DEFAULT:
default:
return 1;
}
} static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
bool use_localagent) {
......
result = pm_command(argc, argv);
......
} static int pm_command(int argc, const char** argv) {
std::string cmd = "pm"; while (argc-- > 0) {
cmd += " " + escape_arg(*argv++);
} return send_shell_command(cmd);
}

在AndroidO时,还有源码frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java。AndroidO最终Pm.java执行mInstaller.createSession()进入PMS。

AndroidQ中,frameworks/base/cmds/pm/src/com/android/commands/pm只有一个Andoird.mk了,pm是system/bin下的一个可执行文件了system/bin/pm。

不过应该是和O类似的(AndoridP中也没有Pm.java的源码了),最终也是进入的PMS中方法,回归到类似点击安装的过程。

我们可以在上面点击安装过程中的PMS方法中添加调用栈或log等,了解adb安装的过程是 进入的哪,进一步跟踪了解。

Android10_原理机制系列_PMS的启动及应用的安装过程的更多相关文章

  1. Android10_原理机制系列_事件传递机制

    前言和概述 Android的输入设备,最常用的就是 触摸屏和按键 了.当然还有其他方式,比如游戏手柄,比如支持OTG设备,则可以链接鼠标.键盘等. 那么这些设备的操作 是如何传递到系统 并 控制界面的 ...

  2. Android10_原理机制系列_Window介绍及WMS的启动过程

    简介 Window简介 Android中,Window是一个重要部分,用户看到的界面.触摸显示界面进行一系列操作都涉及到Window.但实际上,Window本身并不具备绘制功能. 该篇简单介绍下Win ...

  3. Android10_原理机制系列_AMS(ATMS)之应用的第一次启动的过程

    概述 该篇基于Android 10的代码.在 AMS之AMS的启动---Android Framework(Android 10) 中已经介绍了,在Android 10中,activity的调度和管理 ...

  4. Android10_原理机制系列_AMS之AMS的启动

    概述 该篇基于AndroidQ,主要介绍系统启动中的 AMS(ActivityManagerService)的启动过程. AMS对四大组件(AndroidQ将activity移到了ActivityTa ...

  5. Android10_原理机制系列_Binder机制

    前言 Binder 从java到c++到kernel,涉及的内容很多,很难在一篇文章中说清楚.这篇主要是自我记录,方便后续查询并拆分总结的. 因为涉及的的确非常多,不能面面俱到,所以可能一些地方感觉比 ...

  6. Android10_原理机制系列_Android消息机制(Handler)详述

    概述 在Android中的多进程.多线程中提过,只有主线程(UI线程)可以更新UI,其他线程不可以,所以一般耗时操作放到子线程.子线程可以通过Handler将相关信息通知到主线程. Android的消 ...

  7. Android10_原理机制系列_Activity窗口添加到WMS过程

    前言 首先看一个Android界面的布局层次结构,最直观的看一下: 我们能清晰看到,这个界面分成了3部分:顶部状态栏(statusbar).底部导航栏(navigationbar).应用界面. 题外话 ...

  8. Java的多线程机制系列:(三)synchronized的同步原理

    synchronized关键字是JDK5之实现锁(包括互斥性和可见性)的唯一途径(volatile关键字能保证可见性,但不能保证互斥性,详细参见后文关于vloatile的详述章节),其在字节码上编译为 ...

  9. Java的多线程机制系列:不得不提的volatile及指令重排序(happen-before)

    一.不得不提的volatile volatile是个很老的关键字,几乎伴随着JDK的诞生而诞生,我们都知道这个关键字,但又不太清楚什么时候会使用它:我们在JDK及开源框架中随处可见这个关键字,但并发专 ...

随机推荐

  1. 基于云开发 CloudBase 搭建在线视频会议应用教程

    基于云开发 CloudBase 搭建在线视频会议应用 在线视频会议应用是基于浏览器的能力 WebRTC 以及 腾讯云开发 CloudBase 能力构建而成的应用. 在云开发的助力下, 一个复杂的在线会 ...

  2. 四:Redis五大数据类型

    Redis的五大数据类型 1.string(字符串) string是Redis最基本的类型,你可以理解成与menmcached一模一样的类型,一个key对应一个value string类型是二进制安全 ...

  3. JAVA SE——集合框架

    1.首先根据业务场景选择哪种集合类型. set(无序,并且不包含重复元素),list(有序,并且允许重复元素),map(key-value,)

  4. Cephfs的文件存到哪里了

    前言 在ceph里面使用rbd接口的时候,存储的数据在后台是以固定的prifix的对象存在的,这样就能根据相同的前缀对象去对image文件进行拼接或者修复 在文件系统里面这一块就要复杂一些,本篇就写的 ...

  5. webpack 无法打包:No configuration file found and no output filename configured via CLI option

    报错内容 No configuration file found and no output filename configured via CLI option.A configuration fi ...

  6. php读取Excel文件数据

    首先先下载 PHPExcel 脚本之家下载地址:https://www.jb51.net/codes/194070.html 然后把文件丢到php脚本同级目录里面 在php文件里面写下以下代码即可 & ...

  7. webug第十关:文件下载

    第十关:文件下载 点击下载 将fname改为download.php....不过好像它的配置有点问题

  8. Android ALSPS驱动分析

    一.alsps的初始化函数和重要结构体 epl2182_init // Epl2182.c (kernel-3.10\drivers\misc\mediatek\alsps\epl2182-new) ...

  9. 公式编辑器MathType之入门攻略

    许多时候在工作.学习,尤其是写文献时,需要在Word文档中输入较多公式,简单的公式或符号,可以借助Word自带的公式编辑器,但是,遇到较多并且复杂的公式,该如何高效解决呢?其实可以借助一款强大的公式编 ...

  10. Folx的分类标签规则怎么自定义

    一个全新的标签,没有任何对应的标签规则,只是一个空有躯壳没有灵魂的标签,是无法用于文件自动分类的,那么如何根据大家的自身需求,创建一个相对应的标签分类规则呢? 下面小编将使用Folx 5版本为大家讲解 ...