PackageManagerService は SystemServer プロセスを通じて起動され、このサービスは システム内の特定のディレクトリをスキャンし、その中の APK 形式のファイルを探して解析(AppInfo に関連する情報を取得)し、最終的にインストールを完了します。
PKMS はアプリケーションのインストールプロセス中に APK 内の AndroidManifest.xml
ファイルを解析(Android の 4 つの主要コンポーネントを解析)し、これらの解析された情報は Android システムに保存され、システムがいつでも利用できるようになります。
主な機能#
.apk
ファイルをスキャンし、システムアプリ
、ローカルアプリ
をインストール- AndroidManifest.xml マニフェストファイルを解析し、4 つの主要コンポーネント、権限などの情報を分析し、PKMS に保存して照会を容易にする
- ローカルアプリを管理し、
インストール
、アンインストール
、APK情報の照会
などの機能を提供 - アプリケーションの UID、GID を割り当てる
GID(グループ ID)を通じて権限管理を行い、特定のグループ内でのみ特定の機能を使用できるようにします。
使用#
Context#getPackageManager()
メソッドを通じて PKMS サービスを取得できます。PackageManager は抽象クラスで、実際には ApplicationPackageManager です。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PackageManager packageManager = this.getBaseContext().getPackageManager();
}
ServiceManager を通じて PKMS を取得#
ActivityThread は ServiceManager を通じて PKMS の IBinder を取得します。ここで
IPackageManager は AIDL を通じて自動生成されたクラスで、IPackageManager は asInterface
を通じて Java プロキシクラスを取得し、そのプロキシクラスを通じて PKMS と通信します。
// ActivityThread.java
@UnsupportedAppUsage
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
// 1.ServiceManagerのプロキシを取得
// 2.プロキシを通じて「package」を渡してPKMSのハンドルを取得
// 3.PKMSのプロキシを返す
final IBinder b = ServiceManager.getService("package");
// AIDLのプロキシクラスを作成
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
SystemServer で PKMS を初期化#
SystemServer はブートストラップサービスを起動します(startBootstrapServices):PKMS はここで起動します。
// SystemServer.java
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
... 省略部分
try {
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
} /* 省略 catch */
... 省略部分
// 永久にループします。
Looper.loop();
throw new RuntimeException("メインスレッドループが予期せず終了しました");
}
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t)
...
// インストールサービス
Installer installer = mSystemServiceManager.startService(Installer.class);
mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
...
try {
... watch dog
// PackageManagerServiceの静的mainメソッドを呼び出す
// @ mainを解析
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
domainVerificationService, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
mOnlyCore);
} ... watch dog
...
}
startBootstrapServices#
Installer サービスを起動し、その後このサービスを使用してすべてのアプリをインストールします。
// SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
... 省略部分
Installer installer = mSystemServiceManager.startService(Installer.class);
... 省略部分
}
VoldProperties#decrypt
はデバイスが暗号化されているかどうかを判断します(init.rc
ファイルの設定を読み取ります)。mOnlyCore = true
はコアプログラムのみを実行することを示します。
// SystemServer.java
private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
// デバイスが暗号化されている場合は「コア」アプリのみを実行します。
String cryptState = VoldProperties.decrypt().orElse("");
if (ENCRYPTING_STATE.equals(cryptState)) {
// コアプログラムのみを実行
... log msg
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
... log msg
mOnlyCore = true;
}
...
}
PKMS#main
メソッドを呼び出して PKMS オブジェクトを作成します。
// SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
... 省略部分
try {
...
PackageManagerService.main(mSystemContext, installer,
domainVerificationService,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
mOnlyCore); // falseを渡すと仮定します
} /* 省略 finally */
... 省略部分
}
デバイスが暗号化されていない場合、A/B OTA dexopting を実行します。OtaDexoptService#main
メソッド。
// SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
if (!mOnlyCore) {
boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
false);
if (!disableOtaDexopt) {
... trace log
try {
... watch dog
OtaDexoptService.main(mSystemContext, mPackageManagerService);
} /* catch、finally */
}
}
...
}
PKMS は SystemServer#startBootstrapServices
関数で初期化操作を完了した後、startOtherServices
で後続の操作を行います(Dex 最適化、systemReady を完了します)。PKMS#performFstrimIfNeeded
を通じて dex 最適化を完了します。
// SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
... 省略部分
try {
mPackageManagerService.performFstrimIfNeeded();
} ...
... 省略部分
}
PKMS は準備が整ったらsystemReadyを呼び出し、デフォルトの権限を読み込みます。
PKMS の分析#
PKMS main
#
PackageManagerService#main
メソッドを通じて PackageManagerService オブジェクトを作成し、PKMS をpackage、package_nativeを通じて ServiceManager に追加します。
// PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
@NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
boolean onlyCore) {
// パッケージコンパイルに関連するシステムプロパティをチェック
PackageManagerServiceCompilerMapping.checkProperties();
...
Injector injector = new Injector(
context, lock, installer, installLock,
new PackageAbiHelperImpl(),
backgroundHandler,
SYSTEM_PARTITIONS,
(i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock),
(i, pm) -> PermissionManagerService.create(context,
i.getSystemConfig().getAvailableFeatures()),
(i, pm) -> new UserManagerService(context, pm,
new UserDataPreparer(installer, installLock, context, onlyCore),
lock),
(i, pm) -> new Settings(Environment.getDataDirectory(),
RuntimePermissionsPersistence.createInstance(),
i.getPermissionManagerServiceInternal(),
domainVerificationService, lock),
(i, pm) -> AppsFilter.create(pm.mPmInternal, i),
(i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
(i, pm) -> SystemConfig.getInstance(),
(i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(),
i.getContext(), "*dexopt*"),
(i, pm) -> new DexManager(i.getContext(), pm, i.getPackageDexOptimizer(),
i.getInstaller(), i.getInstallLock()),
(i, pm) -> new ArtManagerService(i.getContext(), pm, i.getInstaller(),
i.getInstallLock()),
(i, pm) -> ApexManager.getInstance(),
(i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
(i, pm) -> (IncrementalManager)
i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
(i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
() -> LocalServices.getService(UserManagerInternal.class)),
(i, pm) -> new DisplayMetrics(),
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
i.getDisplayMetrics(), pm.mCacheDir,
pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, pm.mOnlyCore,
i.getDisplayMetrics(), null,
pm.mPackageParserCallback) /* scanningPackageParserProducer */,
(i, pm) -> new PackageParser2(pm.mSeparateProcesses, false, i.getDisplayMetrics(),
null, pm.mPackageParserCallback) /* preparingPackageParserProducer */,
// ステージングマネージャがapexファイルを解析するためのパッケージパーサーの供給者を準備します
(i, pm) -> new PackageInstallerService(
i.getContext(), pm, i::getScanningPackageParser),
(i, pm, cn) -> new InstantAppResolverConnection(
i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
(i, pm) -> new ModuleInfoProvider(i.getContext(), pm),
(i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
(i, pm) -> domainVerificationService,
(i, pm) -> {
// apkのインストール、アンインストールを担当
HandlerThread thread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
thread.start();
return pm.new PackageHandler(thread.getLooper());
},
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService);
// PackageMangerServiceのコンストラクタを呼び出す
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
Build.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG, Build.VERSION.SDK_INT,
Build.VERSION.INCREMENTAL);
...
// package、package_nativeを通じてServiceManagerに追加
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
// PackageManagerService.java
public static class Injector {
@VisibleForTesting(visibility = Visibility.PRIVATE)
interface Producer<T> {
/** Produce an instance of type {@link T} */
T produce(Injector injector, PackageManagerService packageManager);
}
...
@VisibleForTesting(visibility = Visibility.PRIVATE)
static class Singleton<T> {
private final Producer<T> mProducer;
private volatile T mInstance = null;
Singleton(Producer<T> producer) {
this.mProducer = producer;
}
T get(Injector injector, PackageManagerService packageManagerService) {
if (mInstance == null) {
mInstance = mProducer.produce(injector, packageManagerService);
}
return mInstance;
}
}
private final Singleton<PermissionManagerServiceInternal> mPermissionManagerServiceProducer;
private final Singleton<UserManagerService> mUserManagerProducer;
private final Singleton<Settings> mSettingsProducer;
private final Singleton<AppsFilter> mAppsFilterProducer;
...
private final Singleton<DisplayMetrics> mDisplayMetricsProducer;
Injector(Context context, PackageManagerTracedLock lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
Handler backgroundHandler,
List<ScanPartition> systemPartitions,
...
Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer,
Producer<UserManagerService> userManagerProducer,
Producer<Settings> settingsProducer,
Producer<DisplayMetrics> displayMetricsProducer,
) {
...
mPermissionManagerServiceProducer = new Singleton<>(permissionManagerServiceProducer);
mUserManagerProducer = new Singleton<>(userManagerProducer);
mSettingsProducer = new Singleton<>(settingsProducer);
...
mDisplayMetricsProducer = new Singleton<>(displayMetricsProducer);
}
public PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
return mPermissionManagerServiceProducer.get(this, mPackageManager);
}
public Context getContext() {
return mContext;
}
public Settings getSettings() {
return mSettingsProducer.get(this, mPackageManager);
}
public DisplayMetrics getDisplayMetrics() {
return mDisplayMetricsProducer.get(this, mPackageManager);
}
...
}
PKMS のコンストラクタ#
PackageManagerService のコンストラクタでは、PKMS が5 つの段階で構築されることがわかります(EventLog に記録されます)。
段階フラグ | 説明 | その他 |
---|---|---|
BOOT_PROGRESS_PMS_START | 初期段階、Inject 内部クラスを通じてシングルトンのさまざまなサービス、オブジェクトを取得 | DisplayMetrics、Installer、mPermissionManager、mSettings、mPackageDexOptimizer |
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START | システムをスキャン | |
BOOT_PROGRESS_PMS_DATA_SCAN_START | データブロックをスキャン | |
BOOT_PROGRESS_PMS_SCAN_END | スキャン終了 | |
BOOT_PROGRESS_PMS_READY | 準備段階 |
PKMS は複数のスレッドを使用し、その中には注意すべき 2 つの重要なロックがあります。
ロック名 | 説明 | 補足 |
---|---|---|
mPackages(小ロック) | メモリ内のすべてのパッケージの詳細、状態、更新を保護するために使用 | mInstallLock を保持しているときに mPackages を取得することは安全です |
mInstallLock(大ロック) | すべての APK インストール時のロックを保護するために使用され、通常はアプリ APP の再ロードに関連します(単一スレッド、重い IO 操作に関与) | mPackages を取得する際にこのロックを取得すべきではありません |
第一段階 - BOOT_PROGRESS_PMS_START#
第一段階の重点は Settings#readLPw メソッドで、
/data/system
ディレクトリ内のファイルを読み取ります。
BOOT_PROGRESS_PMS_START:DisplayMetrics
(解像度)、Installer
(インストール)、PermissionManager
(権限管理)、mSettings
(インストール情報の保存、存在しないアプリの削除)、PackageDexOptimizer
(Dex 最適化)などを取得します。詳細はコメントを参照してください。
// PackageManagerService.java
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,final String buildFingerprint, final boolean isEngBuild,final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
mOnlyCore = onlyCore;
mMetrics = injector.getDisplayMetrics();
mInstaller = injector.getInstaller();
mUserManager = injector.getUserManagerService(); // マルチユーザー管理
// 権限管理サービス
mPermissionManager = injector.getPermissionManagerServiceInternal();
// 'data/system/'に関連するファイル
// packages.xml
// packages-backup.xml
// packages.list
// packages-stopped.xml
// packages-stopped-backup.xmlファイル
mSettings = injector.getSettings();
...
// system、phone、log、nfc、bluetooth、shell、se、
// networkstack、uwbなどのsharedUserLPwをmSettingsに追加
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);
mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
...
// dex最適化を処理(DexOpt最適化)
mPackageDexOptimizer = injector.getPackageDexOptimizer();
mDexManager = injector.getDexManager();
// Art仮想マシン管理
mArtManagerService = injector.getArtManagerService();
...
// /data/appフォルダを作成
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
// APKインストール時に必要なロック、すべてのinstalledへのアクセスを保護
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
// PackageManager Threadを起動し、apkのインストール、アンインストールを担当
mHandler = injector.getHandler();
// プロセス記録Handler
mProcessLoggingHandler = new ProcessLoggingHandler();
// PackageManager Threadが10分間タイムアウトするかどうかを監視
Watchdog.getInstance().addThread(mHandler,
// WATCHDOG_TIMEOUT => 10分
WATCHDOG_TIMEOUT);
...
// インストール関連のSELinuxポリシーを読み取る
SELinuxMMAC.readInstallPolicy();
// /data/system内のxmlファイルを読み取り、解析する
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
t.traceEnd();
}
}
}
ここで mSettings.addSharedUserLPw
メソッドは SharedUserId
を Settings に追加します。一般的にプロセス間データは共有できませんが、共通の SharedUserId
があればデータを共有できます。
mPackageDexOptimizer
は Dex の最適化ツールです。- mHandler はバックグラウンド ServiceThread のメッセージキューにバインドされ、PKMS はこれを通じて APK のコピーとインストールを駆動し、この Handler は WatchDog によって監視されます(過度な時間を費やさないように)。
PKMD は以下の重要なフォルダにアクセスします。
アクセス | フォルダ |
---|---|
File(Environment.getDataDirectory(), “app”) | /data/app |
File(Environment.getDataDirectory(), “app-lib”) | /data/app-lib |
Settings の作成#
まず、Settings オブジェクトの由来を復習します:PKMS の Settings オブジェクトはPKMS#Inject
クラスによって提供されます。
// PackageManager
public static class Injector {
private final Singleton<Settings> mSettingsProducer;
Injector(/* 省略パラメータ */) {
...
mSettingsProducer = new Singleton<>(settingsProducer);
}
public Settings getSettings() {
return mSettingsProducer.get(this, mPackageManager);
}
}
public static PackageManagerService main(Context context, Installer installer,
@NonNull DomainVerificationService domainVerificationService, boolean factoryTest,
boolean onlyCore) {
Injector injector = new Injector(
context, lock, installer, installLock, new PackageAbiHelperImpl(),
backgroundHandler,
SYSTEM_PARTITIONS,
...
(i, pm) -> new Settings(Environment.getDataDirectory(),
RuntimePermissionsPersistence.createInstance(),
i.getPermissionManagerServiceInternal(),
domainVerificationService,
lock),
);
}
Settings クラスのコンストラクタは必要なファイルとフォルダを作成し、それらの権限を設定します。
// Settings.java
Settings(File dataDir, //渡されたdataDirは/data
RuntimePermissionsPersistence runtimePermissionsPersistence,
LegacyPermissionDataProvider permissionDataProvider,
@NonNull DomainVerificationManagerInternal domainVerificationManager,
@NonNull PackageManagerTracedLock lock) {
...
// /data/systemディレクトリを作成
mSystemDir = new File(dataDir, "system");//'/data/system'
mSystemDir.mkdirs(); // /data/systemを作成
// フォルダの権限 775
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
// /data/system/packages.xmlを作成
mSettingsFilename = new File(mSystemDir, "packages.xml");
// /data/system/packages-backup.xmlを作成
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
// data/system/packages.listを作成
mPackageListFilename = new File(mSystemDir, "packages.list");
// chmod 0640を設定
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
final File kernelDir = new File("/config/sdcardfs");
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;
// 非推奨:移行に必要
// data/system/packages-stopped.listを作成
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
// data/system/packages-stopped-backup.listを作成
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
...
}
Settings - readLPw でパッケージファイルを読み取る#
mSettings#readLPw () 関数:/data/system
ディレクトリ内のファイルを読み取り、Settings オブジェクトに保存します。ファイルは以下のように分類されます(重要なものをいくつか挙げます)。
ファイル名 | 説明 | その他 |
---|---|---|
packages.xml | インストールされたアプリ情報 | PKMS がディレクトリファイルをスキャンした後にこのファイルが作成され、システムがインストール、アンインストール、更新などの操作を行うたびにこのファイルが更新されます。 |
packages-backup.xml | インストールされたアプリのバックアップ情報 | インストール中にシャットダウンすることを避けるため。 |
packages.list | すべてのインストールされたアプリ情報(システム APP ではない) | すべての非システム APK 情報を記述し、サードパーティアプリが変更されるとこのファイルが変更されます。 |
packages-stopped.xml | 強制停止されたアプリ情報 | ユーザー操作で特定のアプリを強制停止した場合、システムはそのアプリの関連情報をこのファイルに記録します。 |
packages-stopped-backup.xml | 強制停止されたアプリのバックアップ情報 | |
**mSettings#readLPw ()** の機能:最初にpackages.xml 、packages-backup.xml ファイルをスキャンし、Linux で使用される UID に関連する関数は以下の通りです。 | ||
「package」タグ:readPackageLPw 関数を使用して、前回割り当てられた Linux UID を読み取ります。 | ||
「shared-user」タグ:readSharedUserLPw 関数を使用して、前回アプリが共有した shared ID を読み取ります。 |
// Settings.java
boolean readLPw(@NonNull List<UserInfo> users) {
FileInputStream str = null;
// packages-backup.xmlが存在する場合はバックアップIOストリームを取得
if (mBackupSettingsFilename.exists()) {
try {
str = new FileInputStream(mBackupSettingsFilename);
...
} /* 省略 catch */
}
try {
if (str == null) {
...
// バックアップがない場合はpackages.xmlを使用
str = new FileInputStream(mSettingsFilename);
}
// xmlを解析
final TypedXmlPullParser parser = Xml.resolvePullParser(str);
int type;
// xmlインデックス位置を調整
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
// 解析形式を判断し、xml形式が正しいか確認
if (type != XmlPullParser.START_TAG) {
... err log
return false;
}
int outerDepth = parser.getDepth();
// xmlの解析を開始
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
// ノード名を解析
String tagName = parser.getName();
if (tagName.equals("package")) {
// 前回割り当てられたLinux UIDを読み取る
readPackageLPw(parser, users);
} else if (tagName.equals("permissions")) {
mPermissions.readPermissions(parser);
} else if (tagName.equals("permission-trees")) {
mPermissions.readPermissionTrees(parser);
} else if (tagName.equals("shared-user")) {
// 前回アプリが共有したGIDを読み取る
readSharedUserLPw(parser, users);
} ... 省略部分 else if
else {
... log
XmlUtils.skipCurrentTag(parser);
}
}
str.close();
} /* 省略 catch、finally */
...
return true;
}
readPackageLPw アプリに独立した Linux ID を割り当てる#
次に、私たちは「package」タグ内の 3 つの要素に注目します。
要素名 | 機能 | 補足 |
---|---|---|
name | アプリのパッケージ名 | パッケージ名は必須です。 |
userId | Linux が前回このアプリに割り当てた 独立 UID | |
sharedUserId | このアプリは 独立 UID を持たず、他のアプリと UID を共有します。 | このアプリをmPendingPackages リストに追加した後に判断します。 |
// Setting.java
public static final String ATTR_NAME = "name";
private final WatchedArrayList<PackageSetting> mPendingPackages = new WatchedArrayList<>();
private void readPackageLPw(TypedXmlPullParser parser, List<UserInfo> users,
ArrayMap<String, Long> originalFirstInstallTimes)
throws XmlPullParserException, IOException {
String name = null;
int userId = 0;
int sharedUserAppId = 0;
try {
// アプリパッケージ名
name = parser.getAttributeValue(null, ATTR_NAME);
...
userId = parser.getAttributeInt(null, "userId", 0);
sharedUserAppId = parser.getAttributeInt(null, "sharedUserId", 0);
if (name == null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"エラー:パッケージマネージャ設定:<package>に名前がありません。位置:"
+ parser.getPositionDescription());
} else if (codePathStr == null) {
...
} else if (userId > 0) { // 独立IDが割り当てられている
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags,
null /* usesSdkLibraries */, null /* usesSdkLibraryVersions */,
null /* usesStaticLibraries */, null /* usesStaticLibraryVersions */,
null /* mimeGroups */, domainSetId);
...
} else if (sharedUserAppId != 0) { // 独立IDが割り当てられていない
if (sharedUserAppId > 0) {
// このアプリの情報を保存
packageSetting = new PackageSetting(name.intern(), realName,
new File(codePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
versionCode, pkgFlags, pkgPrivateFlags, sharedUserAppId,
null /* usesSdkLibraries */,
null /* usesSdkLibrariesVersions */,
null /* usesStaticLibraries */,
null /* usesStaticLibraryVersions */,
null /* mimeGroups */, domainSetId);
...
// `mPendingPackages`リストに追加
mPendingPackages.add(packageSetting);
...
} else {
...
}
} else {
...
}
} /* 省略 catch */
}
addPackageLPw
関数:PKMS 内の 各アプリは PackageSetting
オブジェクトとして存在し、パッケージ名をキーとした HashMap に保存されます。
// Setting.java
// すべてのアプリの情報を保存
final WatchedArrayMap<String, PackageSetting> mPackages;
private final AppIdSettingMap mAppIds;
PackageSetting addPackageLPw(String name, String realName, File codePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
String secondaryCpuAbiString, String cpuAbiOverrideString, int uid,
... /* 省略部分 */) {
// nameを使用してPackageSettingを取得
PackageSetting p = mPackages.get(name);
if (p != null) {
// uidを判断
if (p.getAppId() == uid) {
// 等しい場合は独立UIDが割り当てられていることを示します。
return p;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"重複パッケージを追加しています。最初のものを保持します:" + name);
return null;
}
p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
... /* 省略部分 */);
// このPackageSettingにUIDを設定
p.setAppId(uid);
// このIDをシステムに登録
if (mAppIds.registerExistingAppId(uid, p, name)) {
mPackages.put(name, p);
return p;
}
return null;
}
registerExistingAppId関数:割り当てられたアプリはmNonSystemSettings
から null を取得します。
// Process.java
public static final int FIRST_APPLICATION_UID = 10000;
// ------------------------------------------------------------
// AppIdSettingMap.java
private final WatchedArrayList<SettingBase> mNonSystemSettings;
public boolean registerExistingAppId(int appId, SettingBase setting, Object name) {
// FIRST_APPLICATION_UID以上は独立アプリを示します。
if (appId >= Process.FIRST_APPLICATION_UID) {
int size = mNonSystemSettings.size();
final int index = appId - Process.FIRST_APPLICATION_UID;
// インデックスが有効になるまで配列を埋めます。
while (index >= size) {
mNonSystemSettings.add(null);
size++;
}
// すでに割り当てられている場合はnullを取得します。
if (mNonSystemSettings.get(index) != null) {
// 重複UID
...err msg
return false;
}
mNonSystemSettings.set(index, setting);
} else {
// FIRST_APPLICATION_UID未満は共有アプリを示します。
...
}
return true;
}
readPackageLPw 共有 Linux ID をアプリに割り当てる#
次に、私たちは「shared-user」タグ内の 3 つの要素に注目します。
要素名 | 機能 | 補足 |
---|---|---|
name | 共有 Linux ユーザーの名前を説明するために使用されます。 | |
userId | 共有 Linux ユーザーの ID を説明します。 | |
system | この ID がシステム型、ユーザー型であることを示します。 |
// Setting.java
private void readSharedUserLPw(TypedXmlPullParser parser, List<UserInfo> users)
throws XmlPullParserException, IOException {
String name = null;
int pkgFlags = 0;
int pkgPrivateFlags = 0;
SharedUserSetting su = null;
{
name = parser.getAttributeValue(null, ATTR_NAME);
int userId = parser.getAttributeInt(null, "userId", 0);
if (parser.getAttributeBoolean(null, "system", false)) {
pkgFlags |= ApplicationInfo.FLAG_SYSTEM;
}
if (name == null) {
...
} else if (userId == 0) {
...
} else {
if ((su = addSharedUserLPw(name.intern(), userId, pkgFlags, pkgPrivateFlags))
== null)
...
}
}
}
...
}
Setting#addSharedUserLPw
関数:SharedUserSetting
を使用して共有アプリを説明します。ID を確認して問題がないことを確認した後、mSharedUsers
リストに追加します。
// Setting.java
final WatchedArrayMap<String, SharedUserSetting> mSharedUsers
= new WatchedArrayMap<>();
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
if (s.mAppId == uid) {
return s;
}
...err
return null;
}
// 対応するオブジェクトを作成
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
// IDを設定
s.mAppId = uid;
// 登録を試みる
if (mAppIds.registerExistingAppId(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
return null;
}
registerExistingAppId 関数:割り当てられたアプリはmNonSystemSettings
から null を取得します。
// Process.java
public static final int FIRST_APPLICATION_UID = 10000;
// ------------------------------------------------------------
// AppIdSettingMap.java
private final WatchedArrayList<SettingBase> mNonSystemSettings;
public boolean registerExistingAppId(int appId, SettingBase setting, Object name) {
// FIRST_APPLICATION_UID以上は独立アプリを示します。
if (appId >= Process.FIRST_APPLICATION_UID) {
...
} else {
// FIRST_APPLICATION_UID未満は共有アプリを示します。
if (mSystemSettings.get(appId) != null) {
...err
return false;
}
mSystemSettings.put(appId, setting);
}
return true;
}
readLPw
関数に戻ります:上記の分析を経て、“shared-user”
、“package”
の情報を取得し、次に共有アプリの ID を処理します。
// Settings.java
private final AppIdSettingMap mAppIds;
boolean readLPw(@NonNull List<UserInfo> users) {
...
final int N = mPendingPackages.size();
for (int i = 0; i < N; i++) {
final PackageSetting p = mPendingPackages.get(i);
final int sharedUserAppId = p.getSharedUserAppId();
// 共有IDは必ず0より大きい
if (sharedUserAppId <= 0) {
continue;
}
final Object idObj = getSettingLPr(sharedUserAppId);
if (idObj instanceof SharedUserSetting) {
final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
addPackageSettingLPw(p, sharedUser);
} else if (idObj != null) {
...
} else {
...
}
}
mPendingPackages.clear();
return true;
}
public SettingBase getSettingLPr(int appId) {
return mAppIds.getSetting(appId);
}
void addPackageSettingLPw(PackageSetting p, SharedUserSetting sharedUser) {
mPackages.put(p.getPackageName(), p);
// 共有アプリの場合はさらに判断が必要
if (sharedUser != null) {
SharedUserSetting existingSharedUserSetting = getSharedUserSettingLPr(p);
if (existingSharedUserSetting != null && existingSharedUserSetting != sharedUser) {
...err msg
sharedUser.removePackage(p);
} else if (p.getAppId() != sharedUser.mAppId) {
...err msg
}
sharedUser.addPackage(p);
p.setSharedUserAppId(sharedUser.mAppId);
// 最終的なIDを決定
p.setAppId(sharedUser.mAppId);
}
Object userIdPs = getSettingLPr(p.getAppId());
if (sharedUser == null) {
if (userIdPs != null && userIdPs != p) {
mAppIds.replaceSetting(p.getAppId(), p);
}
} else {
if (userIdPs != null && userIdPs != sharedUser) {
mAppIds.replaceSetting(p.getAppId(), sharedUser);
}
}
}
第二段階 - BOOT_PROGRESS_PMS_SYSTEM_SCAN_START#
BOOT_PROGRESS_PMS_SYSTEM_SCAN_START:システムスキャン段階で、ディレクトリには(system
、vendor
、product
、odm
、oem
などのディレクトリ内のpriv-app
、app
、overlay
)が含まれ、このステップは2 つのロック内で実行されます。
scanDirTracedLI メソッド:このメソッドは APK パッケージをスキャンし、渡されたパス内の APK ファイルをスキャンし、その後 scanDirTracedLI を分析します。
// PackageManagerService.java
// キーはアプリパッケージ名、値はアプリパッケージ
final ArrayMap<String, PackageParser.Package> mPackages =
new ArrayMap<String, PackageParser.Package>();
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
...
// 開始時間を記録
long startTime = SystemClock.uptimeMillis();
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
// init.rcから環境変数BOOTCLASSPATH、
// SYSTEMSERVERCLASSPATHを取得
final String bootClassPath =
System.getenv("BOOTCLASSPATH");
final String systemServerClassPath =
System.getenv("SYSTEMSERVERCLASSPATH");
// /system/frameworkディレクトリを取得
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
// 内部バージョンを取得
final VersionInfo ver = mSettings.getInternalVersion();
// 更新が必要かどうかを判断
mIsUpgrade =
!buildFingerprint.equals(ver.fingerprint);
// Android Mからのアップグレードバージョンでは、システムアプリの権限をインストール時に要求するのではなく、実行時に要求する必要があります。
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
// Android Nからのアップグレードバージョンでは、初回起動時のようにパッケージ抽出を処理する必要があります。利用可能な解析データがないため。
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;
...
// パッケージ解析キャッシュを準備
mCacheDir = preparePackageParserCache(mIsEngBuild);
// フラグを設定し、インストールファイルをスキャンする際にapkパスを変更しない
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
// vendor/product/system_ext overlayパッケージを収集します。
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getOverlayFolder() == null) {
continue;
}
// overlayなどのフォルダ内のapkをスキャン
scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
// frameworkフォルダ内のapkをスキャン
scanDirTracedLI(frameworkDir, systemParseFlags,
systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
packageParser, executorService);
// インストールパッケージがandroidであるかどうかを判断
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"フレームワークパッケージの読み込みに失敗しました。警告をログで確認してください。");
}
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
// priv-appフォルダをスキャン
scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
packageParser, executorService);
}
// /system/appフォルダをスキャン
scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
systemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
...
if (!mOnlyCore) {
// mPackagesを変更する前にこれを最初に行います。「より良いことを期待している」ケースのために
final int numPackages = mPackages.size();
for (int index = 0; index < numPackages; index++) {
final AndroidPackage pkg = mPackages.valueAt(index);
if (pkg.isStub()) {
stubSystemApps.add(pkg.getPackageName());
}
}
// すべてのアプリを逆順でスキャンします。
for (int index = packageSettings.size() - 1; index >= 0; index--) {
final PackageSetting ps = packageSettings.valueAt(index);
// パッケージにFLAG_SYSTEMがある場合は無視します。
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
/*
* パッケージがスキャンされている場合、消去されていません。
*/
final AndroidPackage scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
//無効なパッケージリストにある場合、OTAを介して追加し、
//その後削除します。
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
...
// システムアプリのPackageSettingをPKMSからmPackageから削除します。
removePackageLI(scannedPkg, true);
// アップグレードパッケージのパスをmExpectingBetterリストに追加します。
mExpectingBetter.put(ps.name, ps.getPath());
}
continue;
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
logCriticalInfo(Log.WARN, "システムパッケージ " + ps.name
+ " はもはや存在しません。データが消去されます。");
// システムAPPが存在しない場合、そのAPPのデータを削除します。
removePackageDataLIF(ps, userIds, null, 0, false);
} else {
// まだ無効なシステムパッケージがありますが、削除されている可能性があります。
// コードパスがまだ存在するかどうかを確認し、パッケージがまだ存在するかどうかを確認します。
// 後者はOTAが同じコードパスを保持しますが、パッケージ名を変更する場合に発生する可能性があります。
final PackageSetting disabledPs =
mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
|| disabledPs.pkg == null) {
// このシステムアプリはisDisabledSystemPackageにあり、アップグレードパッケージが見つかりません。
possiblyDeletedUpdatedSystemApps.add(ps.name);
} else {
// システムアプリは無効のままであるべきですが、データバージョンがスキャンできない場合に回復するために
// 期待されるより良いものに追加します。
mExpectingBetter.put(disabledPs.name, disabledPs.getPath());
}
}
}
}
...
}
}
}
システムフォルダのアップグレードアプリをスキャン#
現在の段階(BOOT_PROGRESS_PMS_SYSTEM_SCAN_START
)では、主に/system
フォルダをスキャンします。このディレクトリ内のファイルはどのように配置されているか。
Android システムアーキテクチャはアプリケーション層、アプリケーションフレームワーク層、システム実行層、ハードウェア抽象層、カーネル層に分かれています。
system 内のフォルダ | 内容 |
---|---|
app | システムアプリ |
framework | アプリケーションフレームワークの jar パッケージ |
priv-app | 特権アプリ |
lib | 動的 so ファイルを配置 |
fonts | システムフォント |
media | システム音声 |
第二の目的はシステムファイルをスキャンして処理することです。処理の重点は ==OTA アップグレード ==で、システムはアップグレード可能なシステムアプリを DisabledSystemPackage としてマークします。状況は 3 つあります。
システム APP が更新される:removePackageLI
を通じてシステムアプリの PackageSetting を PKMS から mPackage から削除し、mExpectingBetter リストに追加します。
システム APP が削除される:removePackageDataLIFを通じてシステム APP データを削除します。
アップグレードパッケージがない:システムアプリが DisabledSystemPackage であるが、アップグレードパッケージが見つからない場合、システムアップグレードパッケージが削除された可能性があります。
第三段階 - BOOT_PROGRESS_PMS_DATA_SCAN_START#
BOOT_PROGRESS_PMS_DATA_SCAN_START:主に/data
フォルダをスキャンします。
スキャン処理、更新/data
ディレクトリのアプリ情報(不要なデータも即座に削除されます)。
前のステップで残された possiblyDeletedUpdatedSystemApps リストを処理します。
- mPackage からパッケージを取得できない場合、その APP を削除します。
- パッケージを取得できるがシステム APP でない場合、以下のことを行います。
- システム権限を削除します。
- パッケージを削除します。
- パッケージパスの APK を再スキャンします。
mExpectingBetter リストをスキャンします。 - システム APP のアップグレードパッケージのパスを取得し、以下のことを行います。
- システム APP が存在するディレクトリに基づいてスキャン解析パラメータを設定します。
- PackageName を
mSetting#mPackages
に設定します(removeDisabledSystemPackageLPw メソッド)。 - mExpectingBetter リストをクリアします。
// PackageManagerService.java
private final File mAppInstallDir;
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
// /data/appフォルダを取得
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
...
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
// /data/appフォルダをスキャン
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
packageParser, executorService);
}
// パーサーストリームを閉じる(パッケージxmlの解析ストリーム)
packageParser.close();
// Executorのすべてのタスクを閉じる
List<Runnable> unfinishedTasks = executorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("すべてのタスクが終了する前に閉じられました:" + unfinishedTasks);
}
if (!mOnlyCore) {
// 2.前のステップで残されたAPPパッケージをスキャン(逆順)
for (int i = possiblyDeletedUpdatedSystemApps.size() - 1; i >= 0; --i) {
final String packageName = possiblyDeletedUpdatedSystemApps.get(i);
final AndroidPackage pkg = mPackages.get(packageName);
final String msg;
// 無効なシステムリスト(mDisabledSystemPackage)から削除します。
mSettings.removeDisabledSystemPackageLPw(packageName);
if (pkg == null) {
// アップグレードパッケージを見つけるはずですが、見つかりませんでした(このAPPを削除します)。
msg = "更新されたシステムパッケージ " + packageName
+ " はもはや存在しません。データを削除します。";
// (実際にはすぐに削除されるわけではありません)
} else {
// pkgを取得できた場合、APPがData区に存在することを示し、
// システムAPPではないため、システム権限を削除します。
msg = "更新されたシステムパッケージ " + packageName
+ " はもはや存在しません。データを再スキャンします。";
// 以下、削除して再スキャンします。
removePackageLI(pkg, true); // 削除
try {
final File codePath = new File(pkg.getPath());
// 再スキャン
scanPackageTracedLI(codePath, 0, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "更新されたシステムパッケージの解析に失敗しました:" + e.getMessage());
}
}
// もしパッケージ設定がまだ存在する場合[つまり、以前にスキャンされてシステムに知られている]
// しかし、Pkgが存在しない場合[つまり、/dataをスキャンする際にエラーが発生した場合]
// パッケージデータを完全に削除します。
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && mPackages.get(packageName) == null) {
// データ内のエラーが発生したパッケージデータを削除します。
removePackageDataLIF(ps, userIds, null, 0, false);
}
logCriticalInfo(Log.WARN, msg);
}
// 3. mExpectingBetterリストをスキャン
for (int i = 0; i < mExpectingBetter.size(); i++) {
final String packageName = mExpectingBetter.keyAt(i);
if (!mPackages.containsKey(packageName)) {
// システムAPPのアップグレードパッケージのパスを取得します。
final File scanFile = mExpectingBetter.valueAt(i);
for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
final ScanPartition partition = mDirsToScanAsSystem.get(i1);
// システムAPPが存在するディレクトリに基づいてスキャン解析パラメータを設定します。
if (partition.containsPrivApp(scanFile)) {
reparseFlags = systemParseFlags;
// スキャンパラメータを設定します。
rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag;
break;
}
if (partition.containsApp(scanFile)) {
reparseFlags = systemParseFlags;
rescanFlags = systemScanFlags | partition.scanFlag;
break;
}
}
if (rescanFlags == 0) {
Slog.e(TAG, "予期しないフォールバックパスを無視しています " + scanFile);
continue;
}
//PackageNameに対応するパッケージ設定データ(PackagesSetting)
//をmSettingのmPackagesに追加します。
mSettings.enableSystemPackageLPw(packageName);
try {
// システムAPPのアップグレードパッケージをスキャンします。
final AndroidPackage newPkg = scanPackageTracedLI(
scanFile, reparseFlags, rescanFlags, 0, null);
// スタブを再スキャンしました。スタブされたシステムパッケージのリストに追加します。
if (newPkg.isStub()) {
stubSystemApps.add(packageName);
}
} /* 省略 catch */
}
}
//ルートシステムアプリを解凍&インストールします。
//この操作は最後に実行され、すべてのスタブが置き換えられるか無効にされることを確認します。
// スタブ(stub)
installSystemStubPackages(stubSystemApps, scanFlags);
...
}
// アップグレードリストをクリアします。
mExpectingBetter.clear();
// ストレージ管理パッケージ名を取得します。
mStorageManagerPackage = getStorageManagerPackageName();
// 保護アクションフィルタを解決します。これらのアクションに対してセットアップウィザードのみが最高権限のフィルタを持つことを許可します。
mSetupWizardPackage = getSetupWizardPackageNameImpl();
...
// 保持するパッケージの最終使用時間を読み取り、更新します。
mPackageUsage.read(packageSettings);
mCompilerStats.read();
}
}
scanDirTracedLI - data ディレクトリの内容を分析#
/data
フォルダはデータパーティションとして機能し、主に 2 つの目的があります。
すべてのユーザーの個人データを保存します。
すべてのユーザーの設定ファイルを保存します。
次に、/data ディレクトリ内のサブディレクトリがどのようなデータを保存しているかを見てみましょう。
/data 下のディレクトリ | 意味 |
---|---|
app | このデバイスにインストールされた APP アプリを保存します。 |
data | すべてのインストールされた APP データを保存します。システム APP データも含まれます。 |
app-private | APP のプライベートストレージ |
app-lib | すべての APP の JNI Lib を保存します。 |
system | システム設定ファイルを保存します。 |
anr | ANR が発生したときにシステムが生成するtraces.text ファイルを保存します。 |
第四段階 - BOOT_PROGRESS_PMS_SCAN_END#
BOOT_PROGRESS_PMS_SCAN_END 段階アップグレード後の初回起動時、不要なキャッシュデータ、権限をクリアし、package.xml
ファイルを更新します(packages.xml はすべての APP の情報を保存するために使用されます)。
SDK バージョンが変更され、権限が再更新されます。
OTA アップグレード後の初回起動時、不要なキャッシュをクリアします。
OTA の英語のフルネームはOver-the-Air Technology
で、携帯電話がモバイルネットワークを介して携帯電話のシステムやアプリケーションを管理する技術です。
権限の更新が完了した後、関連データをクリアします。
mSetting の内容を通じて package.xml ファイルを保存し更新し、今後 PKMS が作成されると新しい Setting が読み込まれます。
// PackageManagerService.java
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
...
// 第四段階に入ります。
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
SystemClock.uptimeMillis());
// 権限のリセット
mPermissionManager.onStorageVolumeMounted(
StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade);
ver.sdkVersion = mSdkVersion;
// 初回起動またはpre-Mからのアップグレードであり、正常に起動している場合、ユーザー定義のデフォルトの優先アプリを初期化する必要があります。
if (!mOnlyCore && (mPromoteSystemApps || mFirstBoot)) {
for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
mSettings.applyDefaultPreferredAppsLPw(user.id);
}
}
//起動中にシステムユーザーのストレージスペースを準備します。
//SettingsProviderやSystemUIはユーザーの起動を待つことができません。
final int storageFlags;
if (StorageManager.isFileEncryptedNativeOrEmulated()) {
storageFlags = StorageManager.FLAG_STORAGE_DE;
} else {
storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
}
...
// OTAアップグレードが成功した後の初回起動時、不要なキャッシュをクリアしますが、アプリケーションの設定ファイルはクリアしません。
if (mIsUpgrade && !mOnlyCore) {
Slog.i(TAG, "ビルドフィンガープリントが変更されました。コードキャッシュをクリアします。");
for (int i = 0; i < packageSettings.size(); i++) {
final PackageSetting ps = packageSettings.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
// この時点でアプリは起動しないため、フリーズする必要はありません。
clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
}
}
ver.fingerprint = Build.FINGERPRINT;
}
// Launcherでアイコンを隠します(Android-Q以前の非システムアプリ)。
if (!mOnlyCore && mIsPreQUpgrade) {
Slog.i(TAG, "すべての既存アプリをホワイトリストに追加し、アイコンを隠します。");
int size = packageSettings.size();
for (int i = 0; i < size; i++) {
final PackageSetting ps = packageSettings.valueAt(i);
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
continue;
}
ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
UserHandle.USER_SYSTEM);
}
}
// 権限や他のデフォルトが更新された後にのみクリアします。
mPromoteSystemApps = false;
// すべての変更はスキャン中に完了します。
ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;
// package.xmlを更新します。
t.traceBegin("設定を書き込みます");
writeSettingsLPrTEMP();
t.traceEnd();
...
}
}
}
第五段階 - BOOT_PROGRESS_PMS_READY#
BOOT_PROGRESS_PMS_READY:PKMS の最終段階です。
PackageInstallerService はインストールセッションサービスを管理し、各インストールプロセスでセッション ID が割り当てられます。
GC を要求してメモリを回収します。
// PackageManagerService.java
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
...
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
...
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
SystemClock.uptimeMillis());
...
// PermissionControllerはシステムコアであり、デフォルトで同意し、権限を管理します。
mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr();
... 省略部分
// インストーラーサービスを取得します。
mInstallerService = mInjector.getPackageInstallerService();
...
// dexファイルの使用法を読み取り、更新します。
// PM initが終了したときにこの操作を実行し、すべてのパッケージがデータディレクトリを調整できるようにします。
// 検証可能なファイルを構築し、メモリキャッシュを構築します。
// ファイルを使用することは非常に小さいため、他のアクティビティと比較して、読み込みと検証に多くの時間がかかります。
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) {
FrameworkStatsLog.write(
FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME,
SystemClock.uptimeMillis() - startTime);
}
// 属性が再構成された後、ライブコンピュータを再構成します。
mLiveComputer = createLiveComputer();
}
}
...
// アプリのzipを開いた後、すべてを更新し、GCを要求します。
VMRuntime.getRuntime().requestConcurrentGC();
mInstaller.setWarnIfHeld(mLock);
...
}