需求
1,实现预置APP,在系统恢复出厂设置后能够依然存在
2,能够实现ota增量升级,如果用户当前的应用和ota中带的应用相同且用户版本的高则当前用户版本为准
3,被用户手动卸载掉的app在ota升级之后依然不出现
4,内置app的目录可以放在system和vendor目录下以防空间不够的问题
PackageManagerService
PackageManagerService主要负责Android系统的应用管理,它提供对各种APK文件的安装、卸载、优化和查询服务。
PackageManagerService在系统启动时会扫描所有存在的APK文件和Jar包信息,并将它们读取、保存起来;这样系统在运行时,就能很快的查询到各种应用和相关组件的信息。
预置app的原理就是在PackageManagerService扫描当前系统app之前将需要内置的app放到特定的目录,所以了解PackageManagerService的工作流程是最关键的。
PackageManagerService启动流程
不走流程 直接上图
PackageManagerService 作为系统的核心服务,由 SystemServer 创建,SystemServer 调用了 PackageManagerService 的 main() 创建 PackageManagerService 实例
SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
// Start services.
try {
traceBeginAndSlog("StartServices");
startBootstrapServices();
startCoreServices();
startOtherServices();
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
traceEnd();
}
startBootstrapServices()
// Start the package manager.
if (!mRuntimeRestart) {
MetricsLogger.histogram(null, "boot_package_manager_init_start",
(int) SystemClock.elapsedRealtime());
}
traceBeginAndSlog("StartPackageManagerService");
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
mFirstBoot = mPackageManagerService.isFirstBoot();
mPackageManager = mSystemContext.getPackageManager();
traceEnd();
if (!mRuntimeRestart && !isFirstBootOrUpgrade()) {
MetricsLogger.histogram(null, "boot_package_manager_init_ready",
(int) SystemClock.elapsedRealtime());
}
// Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename
// A/B artifacts after boot, before anything else might touch/need them.
// Note: this isn't needed during decryption (we don't have /data anyways).
if (!mOnlyCore) {
boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
false);
if (!disableOtaDexopt) {
traceBeginAndSlog("StartOtaDexOptService");
try {
OtaDexoptService.main(mSystemContext, mPackageManagerService);
} catch (Throwable e) {
reportWtf("starting OtaDexOptService", e);
} finally {
traceEnd();
}
}
}
PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
PackageManagerService构造函数
PMS构造函数工作是扫描 Android 系统中几个目标文件夹中的 APK,从而建立合适的数据结构来管理各种信息,如:Package 信息、四大组件信息、权限信息等
1,Settings
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);
mPermissionManager = PermissionManagerService.create(context,
new DefaultPermissionGrantedCallback() {
@Override
public void onDefaultRuntimePermissionsGranted(int userId) {
synchronized(mPackages) {
mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
}
}
}, mPackages /*externalLock*/);
mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();
mSettings = new Settings(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);
uid值的定义
Process.java
//系统进程使用的UID/GID,值为1000
publicstatic final int SYSTEM_UID = 1000;
//Phone进程使用的UID/GID,值为1001
publicstatic final int PHONE_UID = 1001;
//shell进程使用的UID/GID,值为2000
publicstatic final int SHELL_UID = 2000;
//使用LOG的进程所在的组的UID/GID为1007
publicstatic final int LOG_UID = 1007;
//供WIF相关进程使用的UID/GID为1010
publicstatic final int WIFI_UID = 1010;
//mediaserver进程使用的UID/GID为1013
publicstatic final int MEDIA_UID = 1013;
//设置能读写SD卡的进程的GID为1015
publicstatic final int SDCARD_RW_GID = 1015;
//NFC相关的进程的UID/GID为1025
publicstatic final int NFC_UID = 1025;
//有权限读写内部存储的进程的GID为1023
publicstatic final int MEDIA_RW_GID = 1023;
//第一个应用Package的起始UID为10000
publicstatic final int FIRST_APPLICATION_UID = 10000;
//系统所支持的最大的应用Package的UID为99999
publicstatic final int LAST_APPLICATION_UID = 99999;
//和蓝牙相关的进程的GID为2000
publicstatic final int BLUETOOTH_GID = 2000;
PMS构造函数的时候会先new一个Settings类来建立与某些系统配置文件、目录之间的关联。
public final class Settings {
... ...
Settings(PermissionSettings permissions, Object lock) {
this(Environment.getDataDirectory(), permissions, lock);
}
Settings(File dataDir, PermissionSettings permission, Object lock) {
mLock = lock;
mPermissions = permission;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
// 创建指向 /data/system/ 目录的 File
mSystemDir = new File(dataDir, "system");
// 创建目录
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
// 用于描述系统所安装的 Package 信息
mSettingsFilename = new File(mSystemDir, "packages.xml");
// packages.xml的备份信息
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
// 保存系统中存在的所有非系统自带的 APK 信息,即 UID 大于 10000 的 apk
mPackageListFilename = new File(mSystemDir, "packages.list");
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
// sdcardfs 相关的文件
final File kernelDir = new File("/config/sdcardfs");
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
// 记录系统中被强制停止运行的 App 信息,如有 App 被强制停止运行,会将一些信息记录到该文件中
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
// packages-stopped.xml 的备份信息
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
上面源码中涉及到 5 个文件:
✨ packages.xml: PMS 扫描完目标文件夹后,会创建packages.xml。当系统进行程序安装、卸载和更新等操作时,均会更新该文件;
✨ packages-backup.xml:packages.xml 文件的备份;
✨ packages.list:用于描述系统中存在的所有非系统自带的 APK 信息。当这些 APK 有变化时,PKMS就会更新该文件;
✨ packages-stopped.xml:记录被用户强行停止的应用的 Package 信息(例如,从设置进入某个应用,然后点击强行停止,那么应用的Package信息就会被记录);
✨ packages-stopped-back.xml:packages-stopped.xml 文件的备份。
上面的介绍中涉及到了两个 back-up 文件,它们是做什么的呢?其实 Android 系统在修改 packages.xml、packages-stopped.xml 之前,会先对它们进行备份。当对它们的修改操作正常完成,则会删掉备份的文件。如果在修改过程中系统出现问题重启了,会再次去读取这两个文件;如果此时发现它们的备份文件还存在,则说明上一次对两份文件的修改操作发生了异常,这两份文件的内容可能已经不准确了,这时系统会去使用之前备份的文件的内容。
packages.xml
<package name="com.xp.browser" codePath="/system/app/xpbrowser" nativeLibraryPath="/system/app/xpbrowser/lib" primaryCpuAbi="arm64-v8a" publicFlags="945339973" privateFlags="0" ft="16c1abf5ae0" it="16c1abf5ae0" ut="16c1abf5ae0" version="601600" userId="10101" isOrphaned="true">
<sigs count="1" schemeVersion="1">
<cert index="8" key="3082036f30820257a0030201020204216b0842300d06092a864886f70d01010b05003068310b30090603550406130238363110300e060355040813076c696579696e673111300f060355040713087368656e7a68656e3110300e060355040a13076c696579696e673110300e060355040b13076c696579696e673110300e060355040313076c696579696e67301e170d3136303731393038353831355a170d3431303731333038353831355a3068310b30090603550406130238363110300e060355040813076c696579696e673111300f060355040713087368656e7a68656e3110300e060355040a13076c696579696e673110300e060355040b13076c696579696e673110300e060355040313076c696579696e6730820122300d06092a864886f70d01010105000382010f003082010a0282010100e85892ff05c9bba893bababe5b5993e2f89fdd66f6a07b9e5e42038142ca71eaf8d8a3c951821298224c227bd5b92c5e544ed69789eaaf43335312cab857ce0d6f40cdd2a8361a314bb1944a5a462719a1e3370a7fb8b22ee60d883f6e006d43376e4e2d1f63f6bd489688c500be2329be9663a24b205fcf7d6e630f6f30b77dcadbd845f2462a0a98dfff00b6f56b019aeef3b3b21d98eaac453330c17ec8d25c7022494b55f7bca50953c69374065b3f5d53f4719449d0e4b66bbf1f781cebeede4bde06cd956da1cd4a719cd27987ebaa9b39ab4741d02d297e02d9572d6ebc86cc1d3c95419572ffc332d45f6225775b31c4b65619d393c626f981b0c61f0203010001a321301f301d0603551d0e04160414e034d4ba9946d12fbb3e15f6db370bf983a8c153300d06092a864886f70d01010b050003820101005558409dd627bf1eaad4731ac39fa71e9fe554996dffd6388abc88e018e7046b514557b708197bdade44c80ff6a96d429f7fd935035eecfe7ecdcbc1f9505e4fc791570ebb71b4509adccb27057c96c9baa6991bacd514111492321322fc91330b6f9e55e927b2e1f45b0d95c50e4c628a9403bbd407a1d5ae83afa42ca6bb1c81796e77bdc68d260dcb716771003e5fce2647b83e48f1f6450d10e80793e83e7d2783e8eedc83cee85e788c0b5222e44453eba37811ad75356e73b76e1146bc9d12e58d698275ac8fe799ffccd0afc9dc8e9359f3ea9f8014f9f5fd5db7a30f0a18482808a72486cf4ccd3c2c2218a1d3d54e20773ffc291ed3fc9798ce0124" />
</sigs>
<perms>
<item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
<item name="android.permission.RESTART_PACKAGES" granted="true" flags="0" />
<item name="android.permission.MODIFY_AUDIO_SETTINGS" granted="true" flags="0" />
<item name="android.permission.SYSTEM_ALERT_WINDOW" granted="true" flags="0" />
<item name="android.permission.NFC" granted="true" flags="0" />
<item name="android.permission.CHANGE_NETWORK_STATE" granted="true" flags="0" />
<item name="android.permission.FOREGROUND_SERVICE" granted="true" flags="0" />
<item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
<item name="android.permission.EXPAND_STATUS_BAR" granted="true" flags="0" />
<item name="android.permission.GET_TASKS" granted="true" flags="0" />
<item name="android.permission.INTERNET" granted="true" flags="0" />
<item name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" granted="true" flags="0" />
<item name="android.permission.CHANGE_WIFI_STATE" granted="true" flags="0" />
<item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
<item name="android.permission.DISABLE_KEYGUARD" granted="true" flags="0" />
<item name="android.permission.SET_WALLPAPER" granted="true" flags="0" />
<item name="android.permission.SET_WALLPAPER_HINTS" granted="true" flags="0" />
<item name="android.permission.VIBRATE" granted="true" flags="0" />
<item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" />
<item name="com.android.launcher.permission.INSTALL_SHORTCUT" granted="true" flags="0" />
<item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="12" />
</package>
ft表示apk文件上次被更改的时间,it表示app第一次安装的时间,ut表示app上次被更新时间
关于package.xml更详细的说明请参考:
2,SystemConfig.java
SystemConfig通过readPermissions()读取指定/system/etc/、/oem/etc/等目录下的permission xml文件,解析其中的内容并转换为特定的数据结构
SystemConfig() {
// Read configuration from system
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
// Vendors are only allowed to customze libs, features and privapp permissions
int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
// For backward compatibility
vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
}
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);
// Allow ODM to customize system configs as much as Vendor, because /odm is another
// vendor partition other than /vendor.
int odmPermissionFlag = vendorPermissionFlag;
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
// Allow OEM to customize features and OEM permissions
int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS;
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);
// Allow Product to customize system configs around libs, features, permissions and apps
int productPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS;
readPermissions(Environment.buildPath(
Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag);
readPermissions(Environment.buildPath(
Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag);
}
frameworks/base/core/java/com/android/server/SystemConfig.java
void readPermissions(File libraryDir, int permissionFlag) {
... ...
// Iterate over the files in the directory and scan .xml files
File platformFile = null;
for (File f : libraryDir.listFiles()) {
// We'll read platform.xml last
// 处理该目录下的非 platform.xml 文件
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
platformFile = f;
continue;
}
... ...
// 调用 readPermissionsFromXml 解析此 XML 文件
readPermissionsFromXml(f, permissionFlag);
}
// Read platform permissions last so it will take precedence
if (platformFile != null) {
// 不知道你有没有发现,platform.xml文件的解析优先级最高!
readPermissionsFromXml(platformFile, permissionFlag);
}
}
platform.xml
plateform.xml中出现的标签种类则较为多样,它们的含义分别是:
<permission >标签:把属性name所描述的权限赋予给<group>标签中属性gid所表示的用户组
<assign-permission>标签:把属性name所描述的权限赋予给uid属性所表示的用户
<library>标签:除framework中动态库以外的,所有系统会自动加载的动态库
最后将上面xml解析出来的数据做如下存储:
<group>标签gid属性的值会存放在mGlobalGids数组中;
<permission>标签,解析得到的值会存放在mPermissions集合中;
<assign-permission>标签解析得到的值会存放在mSystemPermissions中;
<library>标签解析得到的值会存放在mSharedLibraries中;
3 mFirstBoot
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
读取packages.xml的内容,如果读取不到packages.xml则认为是首次开机, 并对mSettings::mPackages等成员进行赋值;packages.xml文件中的内容是上一次扫描apk目录的结果;当前这一次扫描的结果是保存在PackageManagerService::mPackages列表中
4 scanDirTracedLI
扫描系统App
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);
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);
// 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);
// Collect all Regionalization packages form Carrier's res packages.
if (RegionalizationEnvironment.isSupported()) {
Log.d(TAG, "Load Regionalization vendor apks");
final List<File> RegionalizationDirs =
RegionalizationEnvironment.getAllPackageDirectories();
for (File f : RegionalizationDirs) {
File RegionalizationSystemDir = new File(f, "system");
// Collect packages in <Package>/system/app
scanDirLI(new File(RegionalizationSystemDir, "app"),
PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM,
0);
// Collect overlay in <Package>/system/vendor
scanDirLI(new File(RegionalizationSystemDir, "vendor/overlay"),
PackageParser.PARSE_IS_SYSTEM_DIR,
scanFlags
| SCAN_AS_SYSTEM
| SCAN_AS_VENDOR,
0);
}
}
扫描非系统App
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
| PackageParser.PARSE_FORWARD_LOCK,
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 (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
final String msg;
if (deletedPkg == null) {
// should have found an update, but, we didn't; remove everything
msg = "Updated system package " + deletedAppName
+ " 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 + " + deletedAppName
+ " no longer exists; revoking system privileges";
// Don't do anything if a stub is removed from the system image. If
// we were to remove the uncompressed version from the /data partition,
// this is where it'd be done.
final PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
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 {
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());
}
}
}
其中,系统安装目录有:
/system/framework 系统库
/system/app 默认的系统应用
/vendor/app 厂商定制的应用
非系统apk信息目录:
/data/app/
/data/app-private/
scanDirLI()
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
mParallelPackageParserCallback)) {
// Submit files for parsing in parallel
int fileCount = 0;
//=================yubang add for presetapp start==========================
int originalScanFlags = scanFlags;
//=================yubang add for presetapp end==========================
for (File file : files) {
//=================yubang add for presetapp start==========================
if("/data/app".equals(scanDir.getPath()) && !file.getPath().endsWith("==") && !file.getPath().contains(".")){
scanFlags = SCAN_BOOTING | SCAN_INITIAL & ~SCAN_REQUIRE_KNOWN;
}else{
scanFlags = originalScanFlags;
}
//=================yubang add for presetapp end==========================
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
// Exclude Regionalization exclude.list from Carrier's configure.
if (RegionalizationEnvironment.isSupported()) {
if (RegionalizationEnvironment.isExcludedApp(file.getName())) {
Log.d(TAG, "Regionalization Excluded:" + file.getName());
continue;
}
}
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
ParallelPackageParser.java
submit()
public void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
try {
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
pr.pkg = parsePackage(pp, scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
try {
mQueue.put(pr);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// Propagate result to callers of take().
// This is helpful to prevent main thread from getting stuck waiting on
// ParallelPackageParser to finish in case of interruption
mInterruptedInThread = Thread.currentThread().getName();
}
});
}
系统app的升级逻辑
addForInitLI()
boolean shouldHideSystemApp = false;
// A new application appeared on /system, but, we already have a copy of
// the application installed on /data.
//我们安装一个系统APP的时候,发现已经有了一个相同包名的APP,而且这个相同包名APP是在非系统的分区中
if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
&& !pkgSetting.isSystem()) {
if (!pkg.mSigningDetails.checkCapability(pkgSetting.signatures.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)
&& !pkgSetting.signatures.mSigningDetails.checkCapability(
pkg.mSigningDetails,
PackageParser.SigningDetails.CertCapabilities.ROLLBACK)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
+ " name: " + pkgSetting.name);
try (PackageFreezer freezer = freezePackage(pkg.packageName,
"scanPackageInternalLI")) {
// 如果两个APK 签名不匹配,则调用deletePackageLI方法清除APK文件及其数据
deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
}
pkgSetting = null;
} else if (newPkgVersionGreater) {
// The application on /system is newer than the application on /data.
// Simply remove the application on /data [keeping application data]
// and replace it with the version on /system.
// 如果新安装的系统APP的版本号比当前已经安装的版本号要高,所以要要删之前已安装的APP,并安装新的系统APP
logCriticalInfo(Log.WARN,
"System package enabled;"
+ " name: " + pkgSetting.name
+ "; " + pkgSetting.versionCode + " --> " + pkg.getLongVersionCode()
+ "; " + pkgSetting.codePathString + " --> " + pkg.codePath);
InstallArgs args = createInstallArgsForExisting(
packageFlagsToInstallFlags(pkgSetting), pkgSetting.codePathString,
pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting));
synchronized (mInstallLock) {
args.cleanUpResourcesLI();
}
} else {
// The application on /system is older than the application on /data. Hide
// the application on /system and the version on /data will be scanned later
// and re-added like an update.
shouldHideSystemApp = true;
logCriticalInfo(Log.INFO,
"System package disabled;"
+ " name: " + pkgSetting.name
+ "; old: " + pkgSetting.codePathString + " @ " + pkgSetting.versionCode
+ "; new: " + pkg.codePath + " @ " + pkg.codePath);
}
}
##第三方APP的安装流程
PMS主体启动过程
创建一个PMS.Settings,在该对象的构造函数中,将给裁员变量静态赋值
调用mSettings.addSharedUserLP()添加四个共享用户ID
创建一个Installer对象,该对象将辅助程序的安装。
给以下几个数据文件路径静态赋值:
mAppDataDir,代表程序的数据目录,其值为/data/data
mSecureAppDataDir,所谓的解密数据区,目前无用,路径为/data/secure/data
mDrmAppPrivateInstallDir,所谓的DRM数据区,目前无用,路径为/data/app-private
调用readPermission()函数,从/system/etc/permissions目录下读取全部的xml文件
调用mSettings对象的readLP()函数从/data/system/packages.xml文件中读取所有应用程序中和包管理相关的信息,将其保存到mSettings.mPackages变量中。
对java系统中的库文件进行dex提取(转换)
创建FileObserver,用来检测目录中的添加、删除文件的时间,并当时间发生时执行相应的操作
调用scanDirLI()扫描AndroidManifest文件,并将扫描结果保存到PMS中。
删除已经不存在程序对应的数据记录。
清除没有安装成功的数据记录
调用deleteTempPackageFiles()删除/data/app目录下,一vmdl开头的.tmp结尾的文件
如果系统升级,调用updatePermissionsLP()重新为应用程序赋予权限。
调用mSettings.writeLP()将mSettings.mPackages中的数据值重新写入packages.xml文件中
预置APP和手动安装APP最终都会调到PMS的scanPackageTracedLI()经行最终的安装
预置APP的原理
PackageManagerService启动之后在扫描系统app之前通过如下三个条件判断是否执行预制动作
a,通过mFirstBoot判断当前是否是第一次开机(恢复出厂设置后也算)
b,通过比较fingerprint的value判断是否是ota升级之后第一次开机
c,通过读取/data/system/cpreset.txt文件内容判断是否之前有拷贝失败的情况 如果有则需要重新copy
如果满足上诉条件任何之一则将/system/presetapp/目录下的app通过PackageManagerServicePresetInjector的copyPresetApp()copy到/data/app/并且赋予权限。
copy之前会解析/system/presetapp目录下的app包名和versionCode,通过包名去/data/app/目录查询是否用户已经安装该应用,如果安装了再比较两个app的versionCode,然后使用版本高的。
会记录用户卸载的预置app包名 下次ota的时候会去根据包名检测内置的app是否被删除过 如果删除过则放弃掉。
预置方法
内置app的路径为:vendor/blackshark/prebuilt/
更新app:
如果内置的app本身在vendor/blackshark/prebuilt/已经存在 只是更新app的话 将新的app改为和原来的app一样的名字然后直接覆盖就好,同样的如果app里面包含so文件的话需要将新的app里面的so文件提取出来 替换掉/lib/下面的全部so文件。
预置新app:
如果需要预制的app 在vendor/blackshark/prebuilt/不存在则需要新建一个文件夹将apk放进去 然后新建一个Android.mk文件 例如:
########################################start########################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := baiduime-system.presetapp
LOCAL_MODULE_PATH := $(TARGET_OUT)/presetapp
LOCAL_POST_INSTALL_CMD := mkdir -p $(PRODUCT_OUT)/system/presetapp/baiduime/lib/arm/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/*.apk $(PRODUCT_OUT)/system/presetapp/baiduime/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/lib/*.so $(PRODUCT_OUT)/system/presetapp/baiduime/lib/arm/
include $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)
LOCAL_MODULE := baiduime-system.app
LOCAL_MODULE_PATH := $(TARGET_OUT)/app
LOCAL_POST_INSTALL_CMD := mkdir -p $(PRODUCT_OUT)/system/app/baiduime/lib/arm/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/*.apk $(PRODUCT_OUT)/system/app/baiduime/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/lib/*.so $(PRODUCT_OUT)/system/app/baiduime/lib/arm/
include $(BUILD_PHONY_PACKAGE)
include $(CLEAR_VARS)
LOCAL_MODULE := baiduime-system.privapp
LOCAL_MODULE_PATH := $(TARGET_OUT)/priv-app
LOCAL_POST_INSTALL_CMD := mkdir -p $(PRODUCT_OUT)/system/priv-app/baiduime/lib/arm/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/*.apk $(PRODUCT_OUT)/system/priv-app/baiduime/
LOCAL_POST_INSTALL_CMD += && cp -fr $(LOCAL_PATH)/lib/*.so $(PRODUCT_OUT)/system/priv-app/baiduime/lib/arm/
include $(BUILD_PHONY_PACKAGE)
########################################end########################################
鉴于之前app可能需要内置发方式或者路径可以会变化 所以对应的Android.mk文件里面写三个MODULE 分别控制不同的内置路径。在preset_app_xxx.mk文件中引入最终需要内置LOCAL_MODULE即可。
如果app有so文件将apk里面的so文件提出出来放在同目录下面的/lib/arm/目录下
apk文件的命令最好按照应用的英文名称 后续更新的时候都要改为一样的名字直接覆盖。
preset_app_xxx.mk xxx是对应的项目名(它会在device/blackshark/xxx/xxx.mk文件中引用)
如skywalk 对应的未preset_app_如skywalk.mk 所有需要在skywalk内置的应用需要把LOCAL_MODULE在文件里面注册 例如:
PRODUCT_PACKAGES += \
xpbrowser-system.presetapp \
mi_mall-system.presetapp \
mibrowser-system.privapp \
miweather-system.presetapp \
BSMall-system.presetapp \
preputetc-system.privapp \
h5_gamehall-system.presetapp \
baiduime-system.app
注意事项
1) 更新apk要保证apk文件名和之前版本的文件名一样覆盖。
2) ota升级预置的应用如果手机已经安装了该应并且版本比ota里面的应用高则不会更新反之则替换为ota里面新版本。
3) 预置应用一定要讲app里面的so文件提取出来 使用makefile文件copy
参考资料
https://www.jianshu.com/p/8e2831428110
项目差异比较
1 p-q 预置app丢失
2 p-q 第三方app丢失
3 p-q 删除掉的预置app又回来了
4 删除掉的预置app 用户又安装回来了 p-q app丢失
5 p-q 预置app打开闪退
6 p-q(小米签名) 预置app丢失
a,q的预置app versioncode 不大于p的预置app
b,用户自己安装的使用了黑鲨签名的系统级应用 比如:sharkmonitor