From 7010ffae5748a41937427a19623c8524814bcb01 Mon Sep 17 00:00:00 2001 From: ywy Date: Sun, 7 Sep 2025 21:26:52 +0800 Subject: [PATCH] skip selinux check --- .../android/os/storage/StorageManager.java | 2 + .../com/android/server/pm/AppDataHelper.java | 720 ++ .../android/server/pm/AppIdSettingMap.java | 31 +- .../server/pm/AppIdSettingMapHelper.java | 6 +- .../server/pm/PackageManagerService.java | 5 +- .../java/com/android/server/pm/Settings.java | 6630 +++++++++++++++++ .../cmds/installd/InstalldNativeService.cpp | 16 +- .../installd/binder/android/os/IInstalld.aidl | 169 + .../native/cmds/installd/installd_constants.h | 103 + 9 files changed, 7661 insertions(+), 21 deletions(-) create mode 100644 aosp/frameworks/base/services/core/java/com/android/server/pm/AppDataHelper.java create mode 100644 aosp/frameworks/base/services/core/java/com/android/server/pm/Settings.java create mode 100644 aosp/frameworks/native/cmds/installd/binder/android/os/IInstalld.aidl create mode 100644 aosp/frameworks/native/cmds/installd/installd_constants.h diff --git a/aosp/frameworks/base/core/java/android/os/storage/StorageManager.java b/aosp/frameworks/base/core/java/android/os/storage/StorageManager.java index fbde90438..137f824f4 100644 --- a/aosp/frameworks/base/core/java/android/os/storage/StorageManager.java +++ b/aosp/frameworks/base/core/java/android/os/storage/StorageManager.java @@ -265,6 +265,8 @@ public class StorageManager { public static final int FLAG_STORAGE_EXTERNAL = IInstalld.FLAG_STORAGE_EXTERNAL; /** @hide */ public static final int FLAG_STORAGE_SDK = IInstalld.FLAG_STORAGE_SDK; + /** @hide */ + public static final int FLAG_SKIP_SELINUX_CHECK = IInstalld.FLAG_SKIP_SELINUX_CHECK; /** {@hide} */ @IntDef(prefix = "FLAG_STORAGE_", value = { diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/AppDataHelper.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppDataHelper.java new file mode 100644 index 000000000..415b17b84 --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppDataHelper.java @@ -0,0 +1,720 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + +import static com.android.server.pm.PackageManagerService.TAG; +import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal; +import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.PackageManager; +import android.os.CreateAppDataArgs; +import android.os.Environment; +import android.os.FileUtils; +import android.os.Process; +import android.os.Trace; +import android.os.UserHandle; +import android.os.storage.StorageManager; +import android.os.storage.StorageManagerInternal; +import android.os.storage.VolumeInfo; +import android.security.AndroidKeyStoreMaintenance; +import android.system.keystore2.Domain; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; +import android.util.TimingsTraceLog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; +import com.android.server.SystemServerInitThreadPool; +import com.android.server.pm.Installer.LegacyDexoptDisabledException; +import com.android.server.pm.dex.ArtManagerService; +import com.android.server.pm.parsing.pkg.AndroidPackageUtils; +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.PackageUserStateInternal; +import com.android.server.pm.pkg.SELinuxUtil; + +import dalvik.system.VMRuntime; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +/** + * Prepares app data for users + */ +public class AppDataHelper { + private static final boolean DEBUG_APP_DATA = false; + + private final PackageManagerService mPm; + private final Installer mInstaller; + private final ArtManagerService mArtManagerService; + private final PackageManagerServiceInjector mInjector; + + // TODO(b/198166813): remove PMS dependency + AppDataHelper(PackageManagerService pm) { + mPm = pm; + mInjector = mPm.mInjector; + mInstaller = mInjector.getInstaller(); + mArtManagerService = mInjector.getArtManagerService(); + } + + /** + * Prepare app data for the given app just after it was installed or + * upgraded. This method carefully only touches users that it's installed + * for, and it forces a restorecon to handle any seinfo changes. + *

+ * Verifies that directories exist and that ownership and labeling is + * correct for all installed apps. If there is an ownership mismatch, it + * will wipe and recreate the data. + *

+ * Note: To avoid a deadlock, do not call this method with {@code mLock} lock held + */ + @GuardedBy("mPm.mInstallLock") + public void prepareAppDataAfterInstallLIF(AndroidPackage pkg) { + final PackageSetting ps; + synchronized (mPm.mLock) { + ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + } + + prepareAppDataPostCommitLIF(ps, 0 /* previousAppId */, getInstalledUsersForPackage(ps)); + } + + private int[] getInstalledUsersForPackage(PackageSetting ps) { + UserManagerInternal umInternal = mInjector.getUserManagerInternal(); + var users = umInternal.getUsers(false /*excludeDying*/); + int[] userIds = new int[users.size()]; + int userIdsCount = 0; + for (int i = 0, size = users.size(); i < size; ++i) { + int userId = users.get(i).id; + if (ps.getInstalled(userId)) { + userIds[userIdsCount++] = userId; + } + } + return Arrays.copyOf(userIds, userIdsCount); + } + + /** + * For more details about data verification and previousAppId, check + * {@link #prepareAppData} + * @see #prepareAppDataAfterInstallLIF + */ + @GuardedBy("mPm.mInstallLock") + public void prepareAppDataPostCommitLIF(PackageSetting ps, int previousAppId, int[] userIds) { + synchronized (mPm.mLock) { + mPm.mSettings.writeKernelMappingLPr(ps); + } + + // TODO(b/211761016): should we still create the profile dirs? + if (ps.getPkg() != null && !shouldHaveAppStorage(ps.getPkg())) { + Slog.w(TAG, "Skipping preparing app data for " + ps.getPackageName()); + return; + } + + Installer.Batch batch = new Installer.Batch(); + UserManagerInternal umInternal = mInjector.getUserManagerInternal(); + StorageManagerInternal smInternal = mInjector.getLocalService( + StorageManagerInternal.class); + for (int userId : userIds) { + int flags; + if (StorageManager.isCeStorageUnlocked(userId) + && smInternal.isCeStoragePrepared(userId)) { + flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; + } else if (umInternal.isUserRunning(userId)) { + flags = StorageManager.FLAG_STORAGE_DE; + } else { + continue; + } + + if (mPm.mSettings.isAssignAppUid(ps.getAppId())) { + flags |= StorageManager.FLAG_SKIP_SELINUX_CHECK; + } + + // TODO: when user data is locked, mark that we're still dirty + prepareAppData(batch, ps, previousAppId, userId, flags).thenRun(() -> { + // Note: this code block is executed with the Installer lock + // already held, since it's invoked as a side-effect of + // executeBatchLI() + if (umInternal.isUserUnlockingOrUnlocked(userId)) { + // Prepare app data on external storage; currently this is used to + // setup any OBB dirs that were created by the installer correctly. + int uid = UserHandle.getUid(userId, ps.getAppId()); + smInternal.prepareAppDataAfterInstall(ps.getPackageName(), uid); + } + }); + } + executeBatchLI(batch); + } + + private void executeBatchLI(@NonNull Installer.Batch batch) { + try { + batch.execute(mInstaller); + } catch (Installer.InstallerException e) { + Slog.w(TAG, "Failed to execute pending operations", e); + } + } + + private void prepareAppDataAndMigrate(@NonNull Installer.Batch batch, + @NonNull AndroidPackage pkg, @UserIdInt int userId, + @StorageManager.StorageFlags int flags, boolean maybeMigrateAppData) { + if (pkg == null) { + Slog.wtf(TAG, "Package was null!", new Throwable()); + return; + } + if (!shouldHaveAppStorage(pkg)) { + Slog.w(TAG, "Skipping preparing app data for " + pkg.getPackageName()); + return; + } + final PackageSetting ps; + synchronized (mPm.mLock) { + ps = mPm.mSettings.getPackageLPr(pkg.getPackageName()); + } + prepareAppData(batch, ps, Process.INVALID_UID, userId, flags).thenRun(() -> { + // Note: this code block is executed with the Installer lock + // already held, since it's invoked as a side-effect of + // executeBatchLI() + if (maybeMigrateAppData && maybeMigrateAppDataLIF(ps, userId)) { + // We may have just shuffled around app data directories, so + // prepare them one more time + final Installer.Batch batchInner = new Installer.Batch(); + prepareAppData(batchInner, ps, Process.INVALID_UID, userId, flags); + executeBatchLI(batchInner); + } + }); + } + + /** + * Prepare app data for the given app. + *

+ * Verifies that directories exist and that ownership and labeling is + * correct for all installed apps. If there is an ownership mismatch: + *

+ */ + private @NonNull CompletableFuture prepareAppData(@NonNull Installer.Batch batch, + @NonNull PackageSetting ps, int previousAppId, int userId, int flags) { + final String packageName = ps.getPackageName(); + + if (DEBUG_APP_DATA) { + Slog.v(TAG, "prepareAppData for " + packageName + " u" + userId + " 0x" + + Integer.toHexString(flags)); + } + + final String seInfoUser; + synchronized (mPm.mLock) { + seInfoUser = SELinuxUtil.getSeinfoUser(ps.readUserState(userId)); + } + + final AndroidPackage pkg = ps.getPkg(); + final String volumeUuid = ps.getVolumeUuid(); + final int appId = ps.getAppId(); + + String pkgSeInfo = ps.getSeInfo(); + Preconditions.checkNotNull(pkgSeInfo); + + final String seInfo = pkgSeInfo + seInfoUser; + final int targetSdkVersion = ps.getTargetSdkVersion(); + final boolean usesSdk = ps.getUsesSdkLibraries().length > 0; + final CreateAppDataArgs args = Installer.buildCreateAppDataArgs(volumeUuid, packageName, + userId, flags, appId, seInfo, targetSdkVersion, usesSdk); + args.previousAppId = previousAppId; + + return batch.createAppData(args).whenComplete((createAppDataResult, e) -> { + // Note: this code block is executed with the Installer lock + // already held, since it's invoked as a side-effect of + // executeBatchLI() + if (e != null) { + logCriticalInfo(Log.WARN, "Failed to create app data for " + packageName + + ", but trying to recover: " + e); + destroyAppDataLeafLIF(packageName, volumeUuid, userId, flags); + try { + createAppDataResult = mInstaller.createAppData(args); + logCriticalInfo(Log.DEBUG, "Recovery succeeded!"); + } catch (Installer.InstallerException e2) { + logCriticalInfo(Log.DEBUG, "Recovery failed!"); + } + } + + if (!DexOptHelper.useArtService()) { // ART Service handles this on demand instead. + // Prepare the application profiles only for upgrades and + // first boot (so that we don't repeat the same operation at + // each boot). + // + // We only have to cover the upgrade and first boot here + // because for app installs we prepare the profiles before + // invoking dexopt (in installPackageLI). + // + // We also have to cover non system users because we do not + // call the usual install package methods for them. + // + // NOTE: in order to speed up first boot time we only create + // the current profile and do not update the content of the + // reference profile. A system image should already be + // configured with the right profile keys and the profiles + // for the speed-profile prebuilds should already be copied. + // That's done in #performDexOptUpgrade. + // + // TODO(calin, mathieuc): We should use .dm files for + // prebuilds profiles instead of manually copying them in + // #performDexOptUpgrade. When we do that we should have a + // more granular check here and only update the existing + // profiles. + if (pkg != null && (mPm.isDeviceUpgrading() || mPm.isFirstBoot() + || (userId != UserHandle.USER_SYSTEM))) { + try { + mArtManagerService.prepareAppProfiles(pkg, userId, + /* updateReferenceProfileContent= */ false); + } catch (LegacyDexoptDisabledException e2) { + throw new RuntimeException(e2); + } + } + } + + final long ceDataInode = createAppDataResult.ceDataInode; + final long deDataInode = createAppDataResult.deDataInode; + + if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) { + synchronized (mPm.mLock) { + ps.setCeDataInode(ceDataInode, userId); + } + } + if ((flags & StorageManager.FLAG_STORAGE_DE) != 0 && deDataInode != -1) { + synchronized (mPm.mLock) { + ps.setDeDataInode(deDataInode, userId); + } + } + + if (pkg != null) { + prepareAppDataContentsLeafLIF(pkg, ps, userId, flags); + } + }); + } + + public void prepareAppDataContentsLIF(AndroidPackage pkg, + @Nullable PackageStateInternal pkgSetting, int userId, int flags) { + if (pkg == null) { + Slog.wtf(TAG, "Package was null!", new Throwable()); + return; + } + prepareAppDataContentsLeafLIF(pkg, pkgSetting, userId, flags); + } + + private void prepareAppDataContentsLeafLIF(AndroidPackage pkg, + @Nullable PackageStateInternal pkgSetting, int userId, int flags) { + final String volumeUuid = pkg.getVolumeUuid(); + final String packageName = pkg.getPackageName(); + + if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) { + // Create a native library symlink only if we have native libraries + // and if the native libraries are 32 bit libraries. We do not provide + // this symlink for 64 bit libraries. + String primaryCpuAbi = pkgSetting == null + ? AndroidPackageUtils.getRawPrimaryCpuAbi(pkg) : pkgSetting.getPrimaryCpuAbi(); + if (primaryCpuAbi != null && !VMRuntime.is64BitAbi(primaryCpuAbi)) { + final String nativeLibPath = pkg.getNativeLibraryDir(); + if (!(new File(nativeLibPath).exists())) { + return; + } + try { + mInstaller.linkNativeLibraryDirectory(volumeUuid, packageName, + nativeLibPath, userId); + } catch (Installer.InstallerException e) { + Slog.e(TAG, "Failed to link native for " + packageName + ": " + e); + } + } + } + } + + /** + * For system apps on non-FBE devices, this method migrates any existing + * CE/DE data to match the {@code defaultToDeviceProtectedStorage} flag + * requested by the app. + */ + private boolean maybeMigrateAppDataLIF(@NonNull PackageSetting ps, @UserIdInt int userId) { + if (ps.isSystem() && !StorageManager.isFileEncrypted() + && PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) { + final int storageTarget = ps.isDefaultToDeviceProtectedStorage() + ? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE; + try { + mInstaller.migrateAppData(ps.getVolumeUuid(), ps.getPackageName(), userId, + storageTarget); + } catch (Installer.InstallerException e) { + logCriticalInfo(Log.WARN, + "Failed to migrate " + ps.getPackageName() + ": " + e.getMessage()); + } + return true; + } else { + return false; + } + } + + /** + * Reconcile all app data for the given user. + *

+ * Verifies that directories exist and that ownership and labeling is + * correct for all installed apps on all mounted volumes. + */ + @NonNull + public void reconcileAppsData(int userId, @StorageManager.StorageFlags int flags, + boolean migrateAppsData) { + final StorageManager storage = mInjector.getSystemService(StorageManager.class); + for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { + final String volumeUuid = vol.getFsUuid(); + synchronized (mPm.mInstallLock) { + reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppsData); + } + } + } + + @GuardedBy("mPm.mInstallLock") + void reconcileAppsDataLI(String volumeUuid, int userId, @StorageManager.StorageFlags int flags, + boolean migrateAppData) { + reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppData, false /* onlyCoreApps */); + } + + /** + * Reconcile all app data on given mounted volume. + *

+ * Destroys app data that isn't expected, either due to uninstallation or + * reinstallation on another volume. + *

+ * Verifies that directories exist and that ownership and labeling is + * correct for all installed apps. + * + * @return list of skipped non-core packages (if {@code onlyCoreApps} is true) + */ + @GuardedBy("mPm.mInstallLock") + private List reconcileAppsDataLI(String volumeUuid, int userId, + @StorageManager.StorageFlags int flags, boolean migrateAppData, boolean onlyCoreApps) { + Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x" + + Integer.toHexString(flags) + " migrateAppData=" + migrateAppData); + List result = onlyCoreApps ? new ArrayList<>() : null; + + try { + mInstaller.cleanupInvalidPackageDirs(volumeUuid, userId, flags); + } catch (Installer.InstallerException e) { + logCriticalInfo(Log.WARN, "Failed to cleanup deleted dirs: " + e); + } + + final File ceDir = Environment.getDataUserCeDirectory(volumeUuid, userId); + final File deDir = Environment.getDataUserDeDirectory(volumeUuid, userId); + + final Computer snapshot = mPm.snapshotComputer(); + // First look for stale data that doesn't belong, and check if things + // have changed since we did our last restorecon + if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) { + if (StorageManager.isFileEncrypted() && !StorageManager.isCeStorageUnlocked(userId)) { + throw new RuntimeException( + "Yikes, someone asked us to reconcile CE storage while " + userId + + " was still locked; this would have caused massive data loss!"); + } + + final File[] files = FileUtils.listFilesOrEmpty(ceDir); + for (File file : files) { + final String packageName = file.getName(); + try { + assertPackageStorageValid(snapshot, volumeUuid, packageName, userId); + } catch (PackageManagerException e) { + logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e); + try { + mInstaller.destroyAppData(volumeUuid, packageName, userId, + StorageManager.FLAG_STORAGE_CE, 0); + } catch (Installer.InstallerException e2) { + logCriticalInfo(Log.WARN, "Failed to destroy: " + e2); + } + } + } + } + if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) { + final File[] files = FileUtils.listFilesOrEmpty(deDir); + for (File file : files) { + final String packageName = file.getName(); + try { + assertPackageStorageValid(snapshot, volumeUuid, packageName, userId); + } catch (PackageManagerException e) { + logCriticalInfo(Log.WARN, "Destroying " + file + " due to: " + e); + try { + mInstaller.destroyAppData(volumeUuid, packageName, userId, + StorageManager.FLAG_STORAGE_DE, 0); + } catch (Installer.InstallerException e2) { + logCriticalInfo(Log.WARN, "Failed to destroy: " + e2); + } + } + } + } + + // Ensure that data directories are ready to roll for all packages + // installed for this volume and user + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "prepareAppDataAndMigrate"); + Installer.Batch batch = new Installer.Batch(); + List packages = snapshot.getVolumePackages(volumeUuid); + int preparedCount = 0; + for (PackageStateInternal ps : packages) { + final String packageName = ps.getPackageName(); + if (ps.getPkg() == null) { + Slog.w(TAG, "Odd, missing scanned package " + packageName); + // TODO: might be due to legacy ASEC apps; we should circle back + // and reconcile again once they're scanned + continue; + } + // Skip non-core apps if requested + if (onlyCoreApps && !ps.getPkg().isCoreApp()) { + result.add(packageName); + continue; + } + + if (ps.getUserStateOrDefault(userId).isInstalled()) { + prepareAppDataAndMigrate(batch, ps.getPkg(), userId, flags, migrateAppData); + preparedCount++; + } + } + executeBatchLI(batch); + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + + Slog.v(TAG, "reconcileAppsData finished " + preparedCount + " packages"); + return result; + } + + /** + * Asserts that storage path is valid by checking that {@code packageName} is present, + * installed for the given {@code userId} and can have app data. + */ + private void assertPackageStorageValid(@NonNull Computer snapshot, String volumeUuid, + String packageName, int userId) throws PackageManagerException { + final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); + if (packageState == null) { + throw PackageManagerException.ofInternalError("Package " + packageName + " is unknown", + PackageManagerException.INTERNAL_ERROR_STORAGE_INVALID_PACKAGE_UNKNOWN); + } + if (!TextUtils.equals(volumeUuid, packageState.getVolumeUuid())) { + throw PackageManagerException.ofInternalError( + "Package " + packageName + " found on unknown volume " + volumeUuid + + "; expected volume " + packageState.getVolumeUuid(), + PackageManagerException.INTERNAL_ERROR_STORAGE_INVALID_VOLUME_UNKNOWN); + } + final PackageUserStateInternal userState = packageState.getUserStateOrDefault(userId); + if (!userState.isInstalled() && !userState.dataExists()) { + throw PackageManagerException.ofInternalError( + "Package " + packageName + " not installed for user " + userId + + " or was deleted without DELETE_KEEP_DATA", + PackageManagerException.INTERNAL_ERROR_STORAGE_INVALID_NOT_INSTALLED_FOR_USER); + } + if (packageState.getPkg() != null + && !shouldHaveAppStorage(packageState.getPkg())) { + throw PackageManagerException.ofInternalError( + "Package " + packageName + " shouldn't have storage", + PackageManagerException.INTERNAL_ERROR_STORAGE_INVALID_SHOULD_NOT_HAVE_STORAGE); + } + } + + /** + * Prepare storage for system user really early during boot, + * since core system apps like SettingsProvider and SystemUI + * can't wait for user to start + */ + public Future fixAppsDataOnBoot() { + final @StorageManager.StorageFlags int storageFlags; + if (StorageManager.isFileEncrypted()) { + storageFlags = StorageManager.FLAG_STORAGE_DE; + } else { + storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; + } + List deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL, + UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */, + true /* onlyCoreApps */); + Future prepareAppDataFuture = SystemServerInitThreadPool.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 (Installer.InstallerException e) { + Slog.w(TAG, "Trouble fixing GIDs", e); + } + traceLog.traceEnd(); + + traceLog.traceBegin("AppDataPrepare"); + if (deferPackages == null || deferPackages.isEmpty()) { + return; + } + int count = 0; + final Installer.Batch batch = new Installer.Batch(); + for (String pkgName : deferPackages) { + final Computer snapshot = mPm.snapshotComputer(); + final PackageStateInternal packageStateInternal = snapshot.getPackageStateInternal( + pkgName); + if (packageStateInternal != null + && packageStateInternal.getUserStateOrDefault( + UserHandle.USER_SYSTEM).isInstalled()) { + AndroidPackage pkg = packageStateInternal.getPkg(); + prepareAppDataAndMigrate(batch, pkg, + UserHandle.USER_SYSTEM, storageFlags, true /* maybeMigrateAppData */); + count++; + } + } + synchronized (mPm.mInstallLock) { + executeBatchLI(batch); + } + traceLog.traceEnd(); + Slog.i(TAG, "Deferred reconcileAppsData finished " + count + " packages"); + }, "prepareAppData"); + return prepareAppDataFuture; + } + + void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) { + if (pkg == null) { + return; + } + clearAppDataLeafLIF(pkg.getPackageName(), pkg.getVolumeUuid(), userId, flags); + + if ((flags & Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES) == 0) { + clearAppProfilesLIF(pkg); + } + } + + void clearAppDataLeafLIF(String packageName, String volumeUuid, int userId, int flags) { + final Computer snapshot = mPm.snapshotComputer(); + final PackageStateInternal packageStateInternal = + snapshot.getPackageStateInternal(packageName); + for (int realUserId : mPm.resolveUserIds(userId)) { + final long ceDataInode = (packageStateInternal != null) + ? packageStateInternal.getUserStateOrDefault(realUserId).getCeDataInode() : 0; + try { + mInstaller.clearAppData(volumeUuid, packageName, realUserId, + flags, ceDataInode); + } catch (Installer.InstallerException e) { + Slog.w(TAG, String.valueOf(e)); + } + } + } + + void clearAppProfilesLIF(AndroidPackage pkg) { + if (pkg == null) { + Slog.wtf(TAG, "Package was null!", new Throwable()); + return; + } + if (DexOptHelper.useArtService()) { + destroyAppProfilesWithArtService(pkg.getPackageName()); + } else { + try { + mArtManagerService.clearAppProfiles(pkg); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } + } + } + + public void destroyAppDataLIF(AndroidPackage pkg, int userId, int flags) { + if (pkg == null) { + Slog.wtf(TAG, "Package was null!", new Throwable()); + return; + } + destroyAppDataLeafLIF(pkg.getPackageName(), pkg.getVolumeUuid(), userId, flags); + } + + private void destroyAppDataLeafLIF( + String packageName, String volumeUuid, int userId, int flags) { + final Computer snapshot = mPm.snapshotComputer(); + final PackageStateInternal packageStateInternal = + snapshot.getPackageStateInternal(packageName); + for (int realUserId : mPm.resolveUserIds(userId)) { + final long ceDataInode = (packageStateInternal != null) + ? packageStateInternal.getUserStateOrDefault(realUserId).getCeDataInode() : 0; + try { + mInstaller.destroyAppData(volumeUuid, packageName, realUserId, + flags, ceDataInode); + } catch (Installer.InstallerException e) { + Slog.w(TAG, String.valueOf(e)); + } + mPm.getDexManager().notifyPackageDataDestroyed(packageName, userId); + mPm.getDynamicCodeLogger().notifyPackageDataDestroyed(packageName, userId); + } + } + + /** + * Destroy ART app profiles for the package. + */ + void destroyAppProfilesLIF(String packageName) { + if (DexOptHelper.useArtService()) { + destroyAppProfilesWithArtService(packageName); + } else { + try { + mInstaller.destroyAppProfiles(packageName); + } catch (LegacyDexoptDisabledException e) { + throw new RuntimeException(e); + } catch (Installer.InstallerException e) { + Slog.w(TAG, String.valueOf(e)); + } + } + } + + private void destroyAppProfilesWithArtService(String packageName) { + if (!DexOptHelper.artManagerLocalIsInitialized()) { + // This function may get called while PackageManagerService is constructed (via e.g. + // InitAppsHelper.initSystemApps), and ART Service hasn't yet been started then (it + // requires a registered PackageManagerLocal instance). We can skip clearing any stale + // app profiles in this case, because ART Service and the runtime will ignore stale or + // otherwise invalid ref and cur profiles. + return; + } + + try (PackageManagerLocal.FilteredSnapshot snapshot = + getPackageManagerLocal().withFilteredSnapshot()) { + try { + DexOptHelper.getArtManagerLocal().clearAppProfiles(snapshot, packageName); + } catch (IllegalArgumentException e) { + // Package isn't found, but that should only happen due to race. + Slog.w(TAG, e); + } + } + } + + /** + * Returns {@code true} if app's internal storage should be created for this {@code pkg}. + */ + private boolean shouldHaveAppStorage(AndroidPackage pkg) { + PackageManager.Property noAppDataProp = + pkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE); + return (noAppDataProp == null || !noAppDataProp.getBoolean()) && pkg.getUid() >= 0; + } + + /** + * Remove entries from the keystore daemon. Will only remove if the {@code appId} is valid. + */ + public void clearKeystoreData(int userId, int appId) { + if (appId < 0) { + return; + } + + for (int realUserId : mPm.resolveUserIds(userId)) { + AndroidKeyStoreMaintenance.clearNamespace( + Domain.APP, UserHandle.getUid(realUserId, appId)); + } + } +} diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMap.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMap.java index 5650b466e..00fa7c3a8 100644 --- a/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMap.java +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMap.java @@ -40,7 +40,7 @@ final class AppIdSettingMap { private final WatchedSparseArray mSystemSettings; private final SnapshotCache> mSystemSettingsSnapshot; private int mFirstAvailableAppId = Process.FIRST_APPLICATION_UID; - private final AppIdSettingMapHelper settingHelper = new AppIdSettingMapHelper(); + private final AppIdSettingMapHelper mSettingHelper; AppIdSettingMap() { mNonSystemSettings = new WatchedArrayList<>(); @@ -49,6 +49,7 @@ final class AppIdSettingMap { mSystemSettings = new WatchedSparseArray<>(); mSystemSettingsSnapshot = new SnapshotCache.Auto<>( mSystemSettings, mSystemSettings, "AppIdSettingMap.mSystemSettings"); + mSettingHelper = new AppIdSettingMapHelper(); } AppIdSettingMap(AppIdSettingMap orig) { @@ -56,12 +57,13 @@ final class AppIdSettingMap { mNonSystemSettingsSnapshot = new SnapshotCache.Sealed<>(); mSystemSettings = orig.mSystemSettingsSnapshot.snapshot(); mSystemSettingsSnapshot = new SnapshotCache.Sealed<>(); + mSettingHelper = orig.mSettingHelper; } /** Returns true if the requested AppID was valid and not already registered. */ public boolean registerExistingAppId(int appId, SettingBase setting, Object name) { - if (settingHelper.isAssignAppUid(appId)) { - if (!settingHelper.addAssignAppUid(appId, setting)) { + if (mSettingHelper.isAssignAppUid(appId)) { + if (!mSettingHelper.addAssignAppUid(appId, setting)) { PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate user id: " + appId + " name=" + name); @@ -96,9 +98,13 @@ final class AppIdSettingMap { return true; } + public boolean isAssignAppUid(int appId) { + return mSettingHelper.isAssignAppUid(appId); + } + public SettingBase getSetting(int appId) { - if (settingHelper.isAssignAppUid(appId)) { - return (SettingBase)settingHelper.getAssignUidObject(appId); + if (mSettingHelper.isAssignAppUid(appId)) { + return (SettingBase)mSettingHelper.getAssignUidObject(appId); } if (appId >= Process.FIRST_APPLICATION_UID) { final int size = mNonSystemSettings.size(); @@ -110,8 +116,8 @@ final class AppIdSettingMap { } public void removeSetting(int appId) { - if (settingHelper.isAssignAppUid(appId)) { - settingHelper.removeAssignAppUid(appId); + if (mSettingHelper.isAssignAppUid(appId)) { + mSettingHelper.removeAssignAppUid(appId); return; } @@ -135,8 +141,8 @@ final class AppIdSettingMap { } public void replaceSetting(int appId, SettingBase setting) { - if (settingHelper.isAssignAppUid(appId)) { - settingHelper.replaceAssignAppUid(appId, setting); + if (mSettingHelper.isAssignAppUid(appId)) { + mSettingHelper.replaceAssignAppUid(appId, setting); return; } @@ -158,12 +164,15 @@ final class AppIdSettingMap { /** Returns a new AppID or -1 if we could not find an available AppID to assign */ public int acquireAndRegisterNewAppId(SettingBase obj) { - int uid = settingHelper.newAssignAppUid(obj); + int uid = mSettingHelper.newAssignAppUid(obj); if (uid > 0) { return uid; } final int size = mNonSystemSettings.size(); + if (mSettingHelper.isLastApplicationUid(mFirstAvailableAppId, mNonSystemSettings, size)) { + mFirstAvailableAppId = mSettingHelper.getFirstAvailableUid(); + } for (int i = mFirstAvailableAppId - Process.FIRST_APPLICATION_UID; i < size; i++) { if (mNonSystemSettings.get(i) == null) { mNonSystemSettings.set(i, obj); @@ -172,7 +181,7 @@ final class AppIdSettingMap { } // None left? - if (size > (settingHelper.getLastApplicationUid() - Process.FIRST_APPLICATION_UID)) { + if (size > (mSettingHelper.getLastApplicationUid() - Process.FIRST_APPLICATION_UID)) { return -1; } diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMapHelper.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMapHelper.java index 3cf1ca79e..b19a6f960 100644 --- a/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMapHelper.java +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/AppIdSettingMapHelper.java @@ -18,7 +18,8 @@ import android.os.FileObserver; import android.os.Process; import android.util.Log; -import java.util.ArrayList; +import com.android.server.utils.WatchedArrayList; + import java.util.HashMap; import java.util.Map; import java.io.File; @@ -78,7 +79,7 @@ public class AppIdSettingMapHelper { checkXmlFile(); } - public boolean isLastApplicationUid(int mFirstAvailableUid, ArrayList mUserIds, int sum) { + public boolean isLastApplicationUid(int mFirstAvailableUid, WatchedArrayList mUserIds, int sum) { // If mFirstAvailableUid increase to Process.LAST_APPLICATION_UID, // just looking for available uid from index 0. return (mFirstAvailableUid > Process.LAST_APPLICATION_UID) @@ -186,6 +187,7 @@ public class AppIdSettingMapHelper { return false; } mAssignUids.put(uid, obj); + Log.i(TAG, "Add uid " + uid + " settings success."); return true; } diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java index 9d06b0b2f..b017c3cf2 100644 --- a/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5515,8 +5515,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService } // 发送系统广播 + final Bundle extras = new Bundle(); + extras.putInt(Intent.EXTRA_UID, pkgSetting.getAppId()); + extras.putInt(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(pkgSetting.getAppId())); mHandler.post(() -> { - mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, pkgLite.packageName, null, 0, + mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, pkgLite.packageName, extras, 0, null, null, null, null, null, null, null); }); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, diff --git a/aosp/frameworks/base/services/core/java/com/android/server/pm/Settings.java b/aosp/frameworks/base/services/core/java/com/android/server/pm/Settings.java new file mode 100644 index 000000000..37eb339de --- /dev/null +++ b/aosp/frameworks/base/services/core/java/com/android/server/pm/Settings.java @@ -0,0 +1,6630 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; +import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED; +import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; +import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; +import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE; +import static android.os.Process.INVALID_UID; +import static android.os.Process.PACKAGE_INFO_GID; +import static android.os.Process.SYSTEM_UID; + +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; +import static com.android.server.pm.PackageManagerService.WRITE_USER_PACKAGE_RESTRICTIONS; +import static com.android.server.pm.SharedUidMigration.BEST_EFFORT; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.compat.ChangeIdStateCache; +import android.content.ComponentName; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.Flags; +import android.content.pm.IntentFilterVerificationInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.PackagePartitions; +import android.content.pm.PermissionInfo; +import android.content.pm.ResolveInfo; +import android.content.pm.Signature; +import android.content.pm.SuspendDialogInfo; +import android.content.pm.UserInfo; +import android.content.pm.UserPackage; +import android.content.pm.VerifierDeviceIdentity; +import android.content.pm.overlay.OverlayPaths; +import android.net.Uri; +import android.os.Binder; +import android.os.Build; +import android.os.CreateAppDataArgs; +import android.os.FileUtils; +import android.os.Handler; +import android.os.Message; +import android.os.PatternMatcher; +import android.os.PersistableBundle; +import android.os.Process; +import android.os.SELinux; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.os.UserManager; +import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; +import android.service.pm.PackageServiceDumpProto; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.AtomicFile; +import android.util.IntArray; +import android.util.Log; +import android.util.LogPrinter; +import android.util.Pair; +import android.util.Slog; +import android.util.SparseArray; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; +import android.util.SparseLongArray; +import android.util.Xml; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BackgroundThread; +import com.android.internal.pm.pkg.component.ParsedComponent; +import com.android.internal.pm.pkg.component.ParsedIntentInfo; +import com.android.internal.pm.pkg.component.ParsedPermission; +import com.android.internal.pm.pkg.component.ParsedProcess; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.CollectionUtils; +import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.JournaledFile; +import com.android.internal.util.XmlUtils; +import com.android.modules.utils.TypedXmlPullParser; +import com.android.modules.utils.TypedXmlSerializer; +import com.android.permission.persistence.RuntimePermissionsPersistence; +import com.android.permission.persistence.RuntimePermissionsState; +import com.android.server.LocalServices; +import com.android.server.backup.PreferredActivityBackupHelper; +import com.android.server.pm.Installer.InstallerException; +import com.android.server.pm.parsing.PackageInfoUtils; +import com.android.server.pm.permission.LegacyPermissionDataProvider; +import com.android.server.pm.permission.LegacyPermissionSettings; +import com.android.server.pm.permission.LegacyPermissionState; +import com.android.server.pm.permission.LegacyPermissionState.PermissionState; +import com.android.server.pm.pkg.AndroidPackage; +import com.android.server.pm.pkg.ArchiveState; +import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.PackageUserState; +import com.android.server.pm.pkg.PackageUserStateInternal; +import com.android.server.pm.pkg.SharedUserApi; +import com.android.server.pm.pkg.SuspendParams; +import com.android.server.pm.resolution.ComponentResolver; +import com.android.server.pm.verify.domain.DomainVerificationLegacySettings; +import com.android.server.pm.verify.domain.DomainVerificationManagerInternal; +import com.android.server.pm.verify.domain.DomainVerificationPersistence; +import com.android.server.utils.Slogf; +import com.android.server.utils.Snappable; +import com.android.server.utils.SnapshotCache; +import com.android.server.utils.TimingsTraceAndSlog; +import com.android.server.utils.Watchable; +import com.android.server.utils.WatchableImpl; +import com.android.server.utils.Watched; +import com.android.server.utils.WatchedArrayList; +import com.android.server.utils.WatchedArrayMap; +import com.android.server.utils.WatchedArraySet; +import com.android.server.utils.WatchedSparseArray; +import com.android.server.utils.WatchedSparseIntArray; +import com.android.server.utils.Watcher; + +import dalvik.annotation.optimization.NeverCompile; + +import libcore.io.IoUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +/** + * Holds information about dynamic settings. + */ +public final class Settings implements Watchable, Snappable, ResilientAtomicFile.ReadEventLogger { + private static final String TAG = "PackageSettings"; + + /** + * Watchable machinery + */ + private final WatchableImpl mWatchable = new WatchableImpl(); + + /** + * Ensures an observer is in the list, exactly once. The observer cannot be null. The + * function quietly returns if the observer is already in the list. + * + * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes. + */ + public void registerObserver(@NonNull Watcher observer) { + mWatchable.registerObserver(observer); + } + + /** + * Ensures an observer is not in the list. The observer must not be null. The function + * quietly returns if the objserver is not in the list. + * + * @param observer The {@link Watcher} that should not be in the notification list. + */ + public void unregisterObserver(@NonNull Watcher observer) { + mWatchable.unregisterObserver(observer); + } + + /** + * Return true if the {@link Watcher) is a registered observer. + * @param observer A {@link Watcher} that might be registered + * @return true if the observer is registered with this {@link Watchable}. + */ + @Override + public boolean isRegisteredObserver(@NonNull Watcher observer) { + return mWatchable.isRegisteredObserver(observer); + } + + /** + * Invokes {@link Watcher#onChange} on each registered observer. The method can be called + * with the {@link Watchable} that generated the event. In a tree of {@link Watchable}s, this + * is generally the first (deepest) {@link Watchable} to detect a change. + * + * @param what The {@link Watchable} that generated the event. + */ + public void dispatchChange(@Nullable Watchable what) { + mWatchable.dispatchChange(what); + } + /** + * Notify listeners that this object has changed. + */ + protected void onChanged() { + dispatchChange(this); + } + + /** + * Current version of the package database. Set it to the latest version in + * the {@link DatabaseVersion} class below to ensure the database upgrade + * doesn't happen repeatedly. + *

+ * Note that care should be taken to make sure all database upgrades are + * idempotent. + */ + public static final int CURRENT_DATABASE_VERSION = DatabaseVersion.SIGNATURE_MALFORMED_RECOVER; + + /** + * This class contains constants that can be referred to from upgrade code. + * Insert constant values here that describe the upgrade reason. The version + * code must be monotonically increasing. + */ + public static class DatabaseVersion { + /** + * The initial version of the database. + */ + public static final int FIRST_VERSION = 1; + + /** + * Migrating the Signature array from the entire certificate chain to + * just the signing certificate. + */ + public static final int SIGNATURE_END_ENTITY = 2; + + /** + * There was a window of time in + * {@link android.os.Build.VERSION_CODES#LOLLIPOP} where we persisted + * certificates after potentially mutating them. To switch back to the + * original untouched certificates, we need to force a collection pass. + */ + public static final int SIGNATURE_MALFORMED_RECOVER = 3; + } + + static final boolean DEBUG_STOPPED = false; + private static final boolean DEBUG_MU = false; + private static final boolean DEBUG_KERNEL = false; + private static final boolean DEBUG_PARSER = false; + + private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml"; + + private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage"; + private static final String ATTR_ENFORCEMENT = "enforcement"; + + public static final String TAG_ITEM = "item"; + private static final String TAG_DISABLED_COMPONENTS = "disabled-components"; + private static final String TAG_ENABLED_COMPONENTS = "enabled-components"; + private static final String TAG_PACKAGE_RESTRICTIONS = "package-restrictions"; + private static final String TAG_PACKAGE = "pkg"; + private static final String TAG_SHARED_USER = "shared-user"; + private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions"; + private static final String TAG_PERMISSIONS = "perms"; + private static final String TAG_CHILD_PACKAGE = "child-package"; + private static final String TAG_USES_SDK_LIB = "uses-sdk-lib"; + private static final String TAG_USES_STATIC_LIB = "uses-static-lib"; + private static final String TAG_BLOCK_UNINSTALL_PACKAGES = "block-uninstall-packages"; + private static final String TAG_BLOCK_UNINSTALL = "block-uninstall"; + + private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES = + "persistent-preferred-activities"; + static final String TAG_CROSS_PROFILE_INTENT_FILTERS = + "crossProfile-intent-filters"; + public static final String TAG_DOMAIN_VERIFICATION = "domain-verification"; + private static final String TAG_DEFAULT_APPS = "default-apps"; + public static final String TAG_ALL_INTENT_FILTER_VERIFICATION = + "all-intent-filter-verifications"; + private static final String TAG_DEFAULT_BROWSER = "default-browser"; + private static final String TAG_DEFAULT_DIALER = "default-dialer"; + private static final String TAG_VERSION = "version"; + /** + * @deprecated Moved to {@link SuspendParams} + */ + @Deprecated + private static final String TAG_SUSPENDED_DIALOG_INFO = "suspended-dialog-info"; + /** + * @deprecated Moved to {@link SuspendParams} + */ + @Deprecated + private static final String TAG_SUSPENDED_APP_EXTRAS = "suspended-app-extras"; + /** + * @deprecated Moved to {@link SuspendParams} + */ + @Deprecated + private static final String TAG_SUSPENDED_LAUNCHER_EXTRAS = "suspended-launcher-extras"; + private static final String TAG_SUSPEND_PARAMS = "suspend-params"; + private static final String TAG_MIME_GROUP = "mime-group"; + private static final String TAG_MIME_TYPE = "mime-type"; + private static final String TAG_ARCHIVE_STATE = "archive-state"; + private static final String TAG_ARCHIVE_ACTIVITY_INFO = "archive-activity-info"; + + public static final String ATTR_NAME = "name"; + public static final String ATTR_PACKAGE = "package"; + private static final String ATTR_GRANTED = "granted"; + private static final String ATTR_FLAGS = "flags"; + private static final String ATTR_VERSION = "version"; + + private static final String ATTR_CE_DATA_INODE = "ceDataInode"; + private static final String ATTR_DE_DATA_INODE = "deDataInode"; + private static final String ATTR_INSTALLED = "inst"; + private static final String ATTR_STOPPED = "stopped"; + private static final String ATTR_NOT_LAUNCHED = "nl"; + // Legacy, here for reading older versions of the package-restrictions. + private static final String ATTR_BLOCKED = "blocked"; + // New name for the above attribute. + private static final String ATTR_HIDDEN = "hidden"; + private static final String ATTR_DISTRACTION_FLAGS = "distraction_flags"; + private static final String ATTR_SUSPENDED = "suspended"; + private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package"; + + private static final String ATTR_OPTIONAL = "optional"; + /** + * @deprecated Legacy attribute, kept only for upgrading from P builds. + */ + @Deprecated + private static final String ATTR_SUSPEND_DIALOG_MESSAGE = "suspend_dialog_message"; + // Legacy, uninstall blocks are stored separately. + @Deprecated + private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall"; + private static final String ATTR_ENABLED = "enabled"; + private static final String ATTR_ENABLED_CALLER = "enabledCaller"; + private static final String ATTR_DOMAIN_VERIFICATION_STATE = "domainVerificationStatus"; + private static final String ATTR_APP_LINK_GENERATION = "app-link-generation"; + private static final String ATTR_INSTALL_REASON = "install-reason"; + private static final String ATTR_UNINSTALL_REASON = "uninstall-reason"; + private static final String ATTR_INSTANT_APP = "instant-app"; + private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload"; + private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning"; + private static final String ATTR_SPLASH_SCREEN_THEME = "splash-screen-theme"; + private static final String ATTR_MIN_ASPECT_RATIO = "min-aspect-ratio"; + + private static final String ATTR_PACKAGE_NAME = "packageName"; + private static final String ATTR_BUILD_FINGERPRINT = "buildFingerprint"; + private static final String ATTR_FINGERPRINT = "fingerprint"; + private static final String ATTR_VOLUME_UUID = "volumeUuid"; + private static final String ATTR_SDK_VERSION = "sdkVersion"; + private static final String ATTR_DATABASE_VERSION = "databaseVersion"; + private static final String ATTR_VALUE = "value"; + private static final String ATTR_FIRST_INSTALL_TIME = "first-install-time"; + private static final String ATTR_ARCHIVE_ACTIVITY_TITLE = "activity-title"; + private static final String ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME = "original-component-name"; + private static final String ATTR_ARCHIVE_INSTALLER_TITLE = "installer-title"; + private static final String ATTR_ARCHIVE_ICON_PATH = "icon-path"; + private static final String ATTR_ARCHIVE_MONOCHROME_ICON_PATH = "monochrome-icon-path"; + private static final String ATTR_ARCHIVE_TIME = "archive-time"; + + private final Handler mHandler; + + private final PackageManagerTracedLock mLock; + + @Watched(manual = true) + private final RuntimePermissionPersistence mRuntimePermissionsPersistence; + + // Current settings file. + private final File mSettingsFilename; + // Reserve copy of the current settings file. + private final File mSettingsReserveCopyFilename; + // Previous settings file. + // Removed when the current settings file successfully stored. + private final File mPreviousSettingsFilename; + + private final File mPackageListFilename; + private final File mStoppedPackagesFilename; + private final File mBackupStoppedPackagesFilename; + /** The top level directory in configfs for sdcardfs to push the package->uid,userId mappings */ + private final File mKernelMappingFilename; + + // Lock for user package restrictions operations. + private final Object mPackageRestrictionsLock = new Object(); + + // Pending write operations. + @GuardedBy("mPackageRestrictionsLock") + private final SparseIntArray mPendingAsyncPackageRestrictionsWrites = new SparseIntArray(); + + /** Map from package name to settings */ + @Watched + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + final WatchedArrayMap mPackages; + private final SnapshotCache> mPackagesSnapshot; + + /** + * List of packages that were involved in installing other packages, i.e. packages that created + * new sessions or are listed in at least one app's InstallSource. + */ + @Watched + private final WatchedArraySet mInstallerPackages; + private final SnapshotCache> mInstallerPackagesSnapshot; + + /** Map from package name to appId and excluded userids */ + @Watched + private final WatchedArrayMap mKernelMapping; + private final SnapshotCache> mKernelMappingSnapshot; + + // List of replaced system applications + @Watched + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + final WatchedArrayMap mDisabledSysPackages = new WatchedArrayMap<>(); + + /** List of packages that are blocked for uninstall for specific users */ + @Watched + private final WatchedSparseArray> mBlockUninstallPackages = + new WatchedSparseArray<>(); + + private static final class KernelPackageState { + int appId; + int[] excludedUserIds; + } + + /** Map from volume UUID to {@link VersionInfo} */ + @Watched + private final WatchedArrayMap mVersion = new WatchedArrayMap<>(); + + /** + * Version details for a storage volume that may hold apps. + */ + public static class VersionInfo { + /** + * These are the last platform API version we were using for the apps + * installed on internal and external storage. It is used to grant newer + * permissions one time during a system upgrade. + */ + int sdkVersion; + + /** + * The current database version for apps on internal storage. This is + * used to upgrade the format of the packages.xml database not + * necessarily tied to an SDK version. + */ + int databaseVersion; + + /** + * Last known value of {@link Build#FINGERPRINT}. Stored for debug purposes. + */ + String buildFingerprint; + + /** + * Last known value of {@link PackagePartitions#FINGERPRINT}. Used to determine when + * an system update has occurred, meaning we need to clear code caches. + */ + String fingerprint; + + /** + * Force all version information to match current system values, + * typically after resolving any required upgrade steps. + */ + public void forceCurrent() { + sdkVersion = Build.VERSION.SDK_INT; + databaseVersion = CURRENT_DATABASE_VERSION; + buildFingerprint = Build.FINGERPRINT; + fingerprint = PackagePartitions.FINGERPRINT; + } + } + + /** Device identity for the purpose of package verification. */ + @Watched(manual = true) + private VerifierDeviceIdentity mVerifierDeviceIdentity; + + // The user's preferred activities associated with particular intent + // filters. + @Watched + private final WatchedSparseArray mPreferredActivities; + private final SnapshotCache> + mPreferredActivitiesSnapshot; + + // The persistent preferred activities of the user's profile/device owner + // associated with particular intent filters. + @Watched + private final WatchedSparseArray + mPersistentPreferredActivities; + private final SnapshotCache> + mPersistentPreferredActivitiesSnapshot; + + + // For every user, it is used to find to which other users the intent can be forwarded. + @Watched + private final WatchedSparseArray mCrossProfileIntentResolvers; + private final SnapshotCache> + mCrossProfileIntentResolversSnapshot; + + @Watched + final WatchedArrayMap mSharedUsers = new WatchedArrayMap<>(); + @Watched(manual = true) + private final AppIdSettingMap mAppIds; + + // Packages that have been renamed since they were first installed. + // Keys are the new names of the packages, values are the original + // names. The packages appear everywhere else under their original + // names. + @Watched + private final WatchedArrayMap mRenamedPackages = + new WatchedArrayMap(); + + // For every user, it is used to find the package name of the default browser app pending to be + // applied, either on first boot after upgrade, or after backup & restore but before app is + // installed. + @Watched + final WatchedSparseArray mPendingDefaultBrowser = new WatchedSparseArray<>(); + + // TODO(b/161161364): This seems unused, and is probably not relevant in the new API, but should + // verify. + // App-link priority tracking, per-user + @NonNull + @Watched + private final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray(); + + final StringBuilder mReadMessages = new StringBuilder(); + + /** + * Used to track packages that have a shared user ID that hasn't been read + * in yet. + *

+ * TODO: make this just a local variable that is passed in during package + * scanning to make it less confusing. + */ + @Watched + private final WatchedArrayList mPendingPackages; + private final SnapshotCache> mPendingPackagesSnapshot; + + private final File mSystemDir; + + private final KeySetManagerService mKeySetManagerService; + + /** Settings and other information about permissions */ + @Watched(manual = true) + final LegacyPermissionSettings mPermissions; + + @Watched(manual = true) + private final LegacyPermissionDataProvider mPermissionDataProvider; + + @Watched(manual = true) + private final DomainVerificationManagerInternal mDomainVerificationManager; + + /** + * The observer that watches for changes from array members + */ + private final Watcher mObserver = new Watcher() { + @Override + public void onChange(@Nullable Watchable what) { + Settings.this.dispatchChange(what); + } + }; + + private final SnapshotCache mSnapshot; + + // Create a snapshot cache + private SnapshotCache makeCache() { + return new SnapshotCache(this, this) { + @Override + public Settings createSnapshot() { + Settings s = new Settings(mSource); + s.mWatchable.seal(); + return s; + }}; + } + + private void registerObservers() { + mPackages.registerObserver(mObserver); + mInstallerPackages.registerObserver(mObserver); + mKernelMapping.registerObserver(mObserver); + mDisabledSysPackages.registerObserver(mObserver); + mBlockUninstallPackages.registerObserver(mObserver); + mVersion.registerObserver(mObserver); + mPreferredActivities.registerObserver(mObserver); + mPersistentPreferredActivities.registerObserver(mObserver); + mCrossProfileIntentResolvers.registerObserver(mObserver); + mSharedUsers.registerObserver(mObserver); + mAppIds.registerObserver(mObserver); + mRenamedPackages.registerObserver(mObserver); + mNextAppLinkGeneration.registerObserver(mObserver); + mPendingDefaultBrowser.registerObserver(mObserver); + mPendingPackages.registerObserver(mObserver); + } + + // CONSTRUCTOR + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public Settings(Map pkgSettings) { + mPackages = new WatchedArrayMap<>(); + mPackagesSnapshot = + new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages"); + mKernelMapping = new WatchedArrayMap<>(); + mKernelMappingSnapshot = + new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping"); + mInstallerPackages = new WatchedArraySet<>(); + mInstallerPackagesSnapshot = + new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages, + "Settings.mInstallerPackages"); + mPreferredActivities = new WatchedSparseArray<>(); + mPreferredActivitiesSnapshot = new SnapshotCache.Auto<>(mPreferredActivities, + mPreferredActivities, "Settings.mPreferredActivities"); + mPersistentPreferredActivities = new WatchedSparseArray<>(); + mPersistentPreferredActivitiesSnapshot = new SnapshotCache.Auto<>( + mPersistentPreferredActivities, mPersistentPreferredActivities, + "Settings.mPersistentPreferredActivities"); + mCrossProfileIntentResolvers = new WatchedSparseArray<>(); + mCrossProfileIntentResolversSnapshot = new SnapshotCache.Auto<>( + mCrossProfileIntentResolvers, mCrossProfileIntentResolvers, + "Settings.mCrossProfileIntentResolvers"); + mPendingPackages = new WatchedArrayList<>(); + mPendingPackagesSnapshot = new SnapshotCache.Auto<>(mPendingPackages, mPendingPackages, + "Settings.mPendingPackages"); + mKeySetManagerService = new KeySetManagerService(mPackages); + + // Test-only handler working on background thread. + mHandler = new Handler(BackgroundThread.getHandler().getLooper()); + mLock = new PackageManagerTracedLock(); + mPackages.putAll(pkgSettings); + mAppIds = new AppIdSettingMap(); + mSystemDir = null; + mPermissions = null; + mRuntimePermissionsPersistence = null; + mPermissionDataProvider = null; + mSettingsFilename = null; + mSettingsReserveCopyFilename = null; + mPreviousSettingsFilename = null; + mPackageListFilename = null; + mStoppedPackagesFilename = null; + mBackupStoppedPackagesFilename = null; + mKernelMappingFilename = null; + mDomainVerificationManager = null; + + registerObservers(); + Watchable.verifyWatchedAttributes(this, mObserver); + + mSnapshot = makeCache(); + } + + Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence, + LegacyPermissionDataProvider permissionDataProvider, + @NonNull DomainVerificationManagerInternal domainVerificationManager, + @NonNull Handler handler, + @NonNull PackageManagerTracedLock lock) { + mPackages = new WatchedArrayMap<>(); + mPackagesSnapshot = + new SnapshotCache.Auto<>(mPackages, mPackages, "Settings.mPackages"); + mKernelMapping = new WatchedArrayMap<>(); + mKernelMappingSnapshot = + new SnapshotCache.Auto<>(mKernelMapping, mKernelMapping, "Settings.mKernelMapping"); + mInstallerPackages = new WatchedArraySet<>(); + mInstallerPackagesSnapshot = + new SnapshotCache.Auto<>(mInstallerPackages, mInstallerPackages, + "Settings.mInstallerPackages"); + mPreferredActivities = new WatchedSparseArray<>(); + mPreferredActivitiesSnapshot = new SnapshotCache.Auto<>(mPreferredActivities, + mPreferredActivities, "Settings.mPreferredActivities"); + mPersistentPreferredActivities = new WatchedSparseArray<>(); + mPersistentPreferredActivitiesSnapshot = new SnapshotCache.Auto<>( + mPersistentPreferredActivities, mPersistentPreferredActivities, + "Settings.mPersistentPreferredActivities"); + mCrossProfileIntentResolvers = new WatchedSparseArray<>(); + mCrossProfileIntentResolversSnapshot = new SnapshotCache.Auto<>( + mCrossProfileIntentResolvers, mCrossProfileIntentResolvers, + "Settings.mCrossProfileIntentResolvers"); + mPendingPackages = new WatchedArrayList<>(); + mPendingPackagesSnapshot = new SnapshotCache.Auto<>(mPendingPackages, mPendingPackages, + "Settings.mPendingPackages"); + mKeySetManagerService = new KeySetManagerService(mPackages); + + mHandler = handler; + mLock = lock; + mAppIds = new AppIdSettingMap(); + mPermissions = new LegacyPermissionSettings(); + mRuntimePermissionsPersistence = new RuntimePermissionPersistence( + runtimePermissionsPersistence, new Consumer() { + @Override + public void accept(Integer userId) { + mRuntimePermissionsPersistence.writeStateForUser(userId, mPermissionDataProvider, + mPackages, mSharedUsers, mHandler, mLock, /*sync=*/false); + } + }); + mPermissionDataProvider = permissionDataProvider; + + 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); + mSettingsFilename = new File(mSystemDir, "packages.xml"); + mSettingsReserveCopyFilename = new File(mSystemDir, "packages.xml.reservecopy"); + mPreviousSettingsFilename = new File(mSystemDir, "packages-backup.xml"); + mPackageListFilename = new File(mSystemDir, "packages.list"); + FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID); + + final File kernelDir = new File("/config/sdcardfs"); + mKernelMappingFilename = kernelDir.exists() ? kernelDir : null; + + // Deprecated: Needed for migration + mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml"); + mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml"); + + mDomainVerificationManager = domainVerificationManager; + + registerObservers(); + Watchable.verifyWatchedAttributes(this, mObserver); + + mSnapshot = makeCache(); + } + + /** + * A copy constructor used in snapshot(). Attributes that are supposed to be + * immutable in the PackageManagerService application are referenced. Attributes that + * are changed by PackageManagerService APIs are deep-copied + */ + private Settings(Settings r) { + mPackages = r.mPackagesSnapshot.snapshot(); + mPackagesSnapshot = new SnapshotCache.Sealed<>(); + mKernelMapping = r.mKernelMappingSnapshot.snapshot(); + mKernelMappingSnapshot = new SnapshotCache.Sealed<>(); + mInstallerPackages = r.mInstallerPackagesSnapshot.snapshot(); + mInstallerPackagesSnapshot = new SnapshotCache.Sealed<>(); + mKeySetManagerService = new KeySetManagerService(r.mKeySetManagerService, mPackages); + + // The following assignments satisfy Java requirements but are not + // needed by the read-only methods. Note especially that the lock + // is not required because this clone is meant to support lock-free + // read-only methods. + mHandler = null; + mLock = null; + mRuntimePermissionsPersistence = r.mRuntimePermissionsPersistence; + mSettingsFilename = null; + mSettingsReserveCopyFilename = null; + mPreviousSettingsFilename = null; + mPackageListFilename = null; + mStoppedPackagesFilename = null; + mBackupStoppedPackagesFilename = null; + mKernelMappingFilename = null; + + mDomainVerificationManager = r.mDomainVerificationManager; + + mDisabledSysPackages.snapshot(r.mDisabledSysPackages); + mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages); + mVersion.putAll(r.mVersion); + mVerifierDeviceIdentity = r.mVerifierDeviceIdentity; + mPreferredActivities = r.mPreferredActivitiesSnapshot.snapshot(); + mPreferredActivitiesSnapshot = new SnapshotCache.Sealed<>(); + mPersistentPreferredActivities = r.mPersistentPreferredActivitiesSnapshot.snapshot(); + mPersistentPreferredActivitiesSnapshot = new SnapshotCache.Sealed<>(); + mCrossProfileIntentResolvers = r.mCrossProfileIntentResolversSnapshot.snapshot(); + mCrossProfileIntentResolversSnapshot = new SnapshotCache.Sealed<>(); + + mSharedUsers.snapshot(r.mSharedUsers); + mAppIds = r.mAppIds.snapshot(); + + mRenamedPackages.snapshot(r.mRenamedPackages); + mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration); + mPendingDefaultBrowser.snapshot(r.mPendingDefaultBrowser); + // mReadMessages + mPendingPackages = r.mPendingPackagesSnapshot.snapshot(); + mPendingPackagesSnapshot = new SnapshotCache.Sealed<>(); + mSystemDir = null; + // mKeySetManagerService; + mPermissions = r.mPermissions; + mPermissionDataProvider = r.mPermissionDataProvider; + + // Do not register any Watchables and do not create a snapshot cache. + mSnapshot = new SnapshotCache.Sealed(); + } + + /** + * Return a snapshot. + */ + public Settings snapshot() { + return mSnapshot.snapshot(); + } + + private void invalidatePackageCache() { + PackageManagerService.invalidatePackageInfoCache(); + ChangeIdStateCache.invalidate(); + onChanged(); + } + + PackageSetting getPackageLPr(String pkgName) { + return mPackages.get(pkgName); + } + + WatchedArrayMap getPackagesLocked() { + return mPackages; + } + + WatchedArrayMap getDisabledSystemPackagesLocked() { + return mDisabledSysPackages; + } + + KeySetManagerService getKeySetManagerService() { + return mKeySetManagerService; + } + + String getRenamedPackageLPr(String pkgName) { + return mRenamedPackages.get(pkgName); + } + + String addRenamedPackageLPw(String pkgName, String origPkgName) { + return mRenamedPackages.put(pkgName, origPkgName); + } + + void removeRenamedPackageLPw(String pkgName) { + mRenamedPackages.remove(pkgName); + } + + void pruneRenamedPackagesLPw() { + for (int i = mRenamedPackages.size() - 1; i >= 0; i--) { + PackageSetting ps = mPackages.get(mRenamedPackages.valueAt(i)); + if (ps == null) { + mRenamedPackages.removeAt(i); + } + } + } + + /** Gets and optionally creates a new shared user id. */ + SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags, + boolean create) throws PackageManagerException { + SharedUserSetting s = mSharedUsers.get(name); + if (s == null && create) { + s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags); + s.mAppId = mAppIds.acquireAndRegisterNewAppId(s); + if (s.mAppId < 0) { + // < 0 means we couldn't assign a userid; throw exception + throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, + "Creating shared user " + name + " failed"); + } + Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.mAppId); + mSharedUsers.put(name, s); + } + return s; + } + + WatchedArrayMap getSharedUsersLocked() { + return mSharedUsers; + } + + Collection getAllSharedUsersLPw() { + return mSharedUsers.values(); + } + + boolean disableSystemPackageLPw(String name, boolean replaced) { + final PackageSetting p = mPackages.get(name); + if(p == null) { + Log.w(PackageManagerService.TAG, "Package " + name + " is not an installed package"); + return false; + } + final PackageSetting dp = mDisabledSysPackages.get(name); + // always make sure the system package code and resource paths dont change + if (dp == null && p.getPkg() != null && p.isSystem() + && !p.isUpdatedSystemApp()) { + final PackageSetting disabled; + if (replaced) { + // a little trick... when we install the new package, we don't + // want to modify the existing PackageSetting for the built-in + // version. so at this point we make a copy to place into the + // disabled set. + disabled = new PackageSetting(p); + } else { + disabled = p; + } + p.getPkgState().setUpdatedSystemApp(true); + mDisabledSysPackages.put(name, disabled); + SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(disabled); + if (sharedUserSetting != null) { + sharedUserSetting.mDisabledPackages.add(disabled); + } + return true; + } + return false; + } + + PackageSetting enableSystemPackageLPw(String name) { + PackageSetting p = mDisabledSysPackages.get(name); + if(p == null) { + Log.w(PackageManagerService.TAG, "Package " + name + " is not disabled"); + return null; + } + SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p); + if (sharedUserSetting != null) { + sharedUserSetting.mDisabledPackages.remove(p); + } + p.getPkgState().setUpdatedSystemApp(false); + PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(), p.getAppId(), + p.getFlags(), p.getPrivateFlags(), mDomainVerificationManager.generateNewId()); + if (ret != null) { + ret.setLegacyNativeLibraryPath(p.getLegacyNativeLibraryPath()); + ret.setPrimaryCpuAbi(p.getPrimaryCpuAbiLegacy()); + ret.setSecondaryCpuAbi(p.getSecondaryCpuAbiLegacy()); + ret.setCpuAbiOverride(p.getCpuAbiOverride()); + ret.setLongVersionCode(p.getVersionCode()); + ret.setUsesSdkLibraries(p.getUsesSdkLibraries()); + ret.setUsesSdkLibrariesVersionsMajor(p.getUsesSdkLibrariesVersionsMajor()); + ret.setUsesSdkLibrariesOptional(p.getUsesSdkLibrariesOptional()); + ret.setUsesStaticLibraries(p.getUsesStaticLibraries()); + ret.setUsesStaticLibrariesVersions(p.getUsesStaticLibrariesVersions()); + ret.setMimeGroups(p.getMimeGroups()); + ret.setAppMetadataFilePath(p.getAppMetadataFilePath()); + ret.setAppMetadataSource(p.getAppMetadataSource()); + ret.getPkgState().setUpdatedSystemApp(false); + ret.setTargetSdkVersion(p.getTargetSdkVersion()); + ret.setRestrictUpdateHash(p.getRestrictUpdateHash()); + ret.setScannedAsStoppedSystemApp(p.isScannedAsStoppedSystemApp()); + } + mDisabledSysPackages.remove(name); + return ret; + } + + boolean isDisabledSystemPackageLPr(String name) { + return mDisabledSysPackages.containsKey(name); + } + + void removeDisabledSystemPackageLPw(String name) { + final PackageSetting p = mDisabledSysPackages.remove(name); + if (p != null) { + SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p); + if (sharedUserSetting != null) { + sharedUserSetting.mDisabledPackages.remove(p); + checkAndPruneSharedUserLPw(sharedUserSetting, false); + } + } + } + + PackageSetting addPackageLPw(String name, String realName, File codePath, int uid, int pkgFlags, + int pkgPrivateFlags, @NonNull UUID domainSetId) { + PackageSetting p = mPackages.get(name); + if (p != null) { + if (p.getAppId() == uid) { + return p; + } + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Adding duplicate package, keeping first: " + name); + return null; + } + p = new PackageSetting(name, realName, codePath, pkgFlags, pkgPrivateFlags, domainSetId) + .setAppId(uid); + if (mAppIds.registerExistingAppId(uid, p, name)) { + mPackages.put(name, p); + return p; + } + return null; + } + + SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) { + SharedUserSetting s = mSharedUsers.get(name); + if (s != null) { + if (s.mAppId == uid) { + return s; + } + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Adding duplicate shared user, keeping first: " + name); + return null; + } + s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags); + s.mAppId = uid; + if (mAppIds.registerExistingAppId(uid, s, name)) { + mSharedUsers.put(name, s); + return s; + } + return null; + } + + void pruneSharedUsersLPw() { + List removeKeys = new ArrayList<>(); + List removeValues = new ArrayList<>(); + for (Map.Entry entry : mSharedUsers.entrySet()) { + final SharedUserSetting sus = entry.getValue(); + if (sus == null) { + removeKeys.add(entry.getKey()); + continue; + } + boolean changed = false; + // remove packages that are no longer installed + WatchedArraySet sharedUserPackageSettings = sus.getPackageSettings(); + for (int i = sharedUserPackageSettings.size() - 1; i >= 0; i--) { + PackageSetting ps = sharedUserPackageSettings.valueAt(i); + if (mPackages.get(ps.getPackageName()) == null) { + sharedUserPackageSettings.removeAt(i); + changed = true; + } + } + WatchedArraySet sharedUserDisabledPackageSettings = + sus.getDisabledPackageSettings(); + for (int i = sharedUserDisabledPackageSettings.size() - 1; i >= 0; i--) { + PackageSetting ps = sharedUserDisabledPackageSettings.valueAt(i); + if (mDisabledSysPackages.get(ps.getPackageName()) == null) { + sharedUserDisabledPackageSettings.removeAt(i); + changed = true; + } + } + if (changed) { + sus.onChanged(); + } + if (sharedUserPackageSettings.isEmpty() + && sharedUserDisabledPackageSettings.isEmpty()) { + removeValues.add(sus); + } + } + removeKeys.forEach(mSharedUsers::remove); + removeValues.forEach(sus -> checkAndPruneSharedUserLPw(sus, true)); + } + + /** + * Creates a new {@code PackageSetting} object. + * Use this method instead of the constructor to ensure a settings object is created + * with the correct base. + */ + static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg, + PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser, + File codePath, String legacyNativeLibraryPath, String primaryCpuAbi, + String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags, + UserHandle installUser, boolean allowInstall, boolean instantApp, + boolean virtualPreload, boolean isStoppedSystemApp, UserManagerService userManager, + String[] usesSdkLibraries, long[] usesSdkLibrariesVersions, + boolean[] usesSdkLibrariesOptional, String[] usesStaticLibraries, + long[] usesStaticLibrariesVersions, Set mimeGroupNames, + @NonNull UUID domainSetId, int targetSdkVersion, byte[] restrictUpdatedHash) { + final PackageSetting pkgSetting; + if (originalPkg != null) { + if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package " + + pkgName + " is adopting original package " + originalPkg.getPackageName()); + pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/) + .setPath(codePath) + .setLegacyNativeLibraryPath(legacyNativeLibraryPath) + .setPrimaryCpuAbi(primaryCpuAbi) + .setSecondaryCpuAbi(secondaryCpuAbi) + // NOTE: Create a deeper copy of the package signatures so we don't + // overwrite the signatures in the original package setting. + .setSignatures(new PackageSignatures()) + .setLongVersionCode(versionCode) + .setUsesSdkLibraries(usesSdkLibraries) + .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions) + .setUsesSdkLibrariesOptional(usesSdkLibrariesOptional) + .setUsesStaticLibraries(usesStaticLibraries) + .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions) + // Update new package state. + .setLastModifiedTime(codePath.lastModified()) + .setDomainSetId(domainSetId) + .setTargetSdkVersion(targetSdkVersion) + .setRestrictUpdateHash(restrictUpdatedHash); + pkgSetting.setFlags(pkgFlags) + .setPrivateFlags(pkgPrivateFlags); + } else { + int installUserId = installUser != null ? installUser.getIdentifier() + : UserHandle.USER_SYSTEM; + + pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, pkgFlags, + pkgPrivateFlags, domainSetId) + .setUsesSdkLibraries(usesSdkLibraries) + .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions) + .setUsesSdkLibrariesOptional(usesSdkLibrariesOptional) + .setUsesStaticLibraries(usesStaticLibraries) + .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions) + .setLegacyNativeLibraryPath(legacyNativeLibraryPath) + .setPrimaryCpuAbi(primaryCpuAbi) + .setSecondaryCpuAbi(secondaryCpuAbi) + .setLongVersionCode(versionCode) + .setMimeGroups(createMimeGroups(mimeGroupNames)) + .setTargetSdkVersion(targetSdkVersion) + .setRestrictUpdateHash(restrictUpdatedHash) + .setLastModifiedTime(codePath.lastModified()); + if (sharedUser != null) { + pkgSetting.setSharedUserAppId(sharedUser.mAppId); + } + // If this is not a system app, it starts out stopped. + if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { + if (DEBUG_STOPPED) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(PackageManagerService.TAG, "Stopping package " + pkgName, e); + } + List users = getAllUsers(userManager); + if (users != null && allowInstall) { + for (UserInfo user : users) { + // By default we consider this app to be installed + // for the user if no user has been specified (which + // means to leave it at its original value, and the + // original default value is true), or we are being + // asked to install for all users, or this is the + // user we are installing for. + final boolean installed = installUser == null + || (installUserId == UserHandle.USER_ALL + && !isAdbInstallDisallowed(userManager, user.id) + && !user.preCreated) + || installUserId == user.id; + if (DEBUG_MU) { + Slogf.d(TAG, "createNewSetting(pkg=%s, installUserId=%s, user=%s, " + + "installed=%b)", + pkgName, installUserId, user.toFullString(), installed); + } + pkgSetting.setUserState(user.id, 0, 0, COMPONENT_ENABLED_STATE_DEFAULT, + installed, + true /*stopped*/, + true /*notLaunched*/, + false /*hidden*/, + 0 /*distractionFlags*/, + null /*suspendParams*/, + instantApp, + virtualPreload, + null /*lastDisableAppCaller*/, + null /*enabledComponents*/, + null /*disabledComponents*/, + PackageManager.INSTALL_REASON_UNKNOWN, + PackageManager.UNINSTALL_REASON_UNKNOWN, + null /*harmfulAppWarning*/, + null /*splashscreenTheme*/, + 0 /*firstInstallTime*/, + PackageManager.USER_MIN_ASPECT_RATIO_UNSET, + null /*archiveState*/ + ); + } + } + } else if (isStoppedSystemApp) { + if (DEBUG_STOPPED) { + RuntimeException e = new RuntimeException("here"); + e.fillInStackTrace(); + Slog.i(PackageManagerService.TAG, "Stopping system package " + pkgName, e); + } + pkgSetting.setStopped(true, installUserId); + pkgSetting.setScannedAsStoppedSystemApp(true); + } + if (sharedUser != null) { + pkgSetting.setAppId(sharedUser.mAppId); + } else { + // Clone the setting here for disabled system packages + if (disabledPkg != null) { + // For disabled packages a new setting is created + // from the existing user id. This still has to be + // added to list of user id's + // Copy signatures from previous setting + pkgSetting.setSignatures(new PackageSignatures(disabledPkg.getSignatures())); + pkgSetting.setAppId(disabledPkg.getAppId()); + // Clone permissions + pkgSetting.getLegacyPermissionState() + .copyFrom(disabledPkg.getLegacyPermissionState()); + // Clone component info + List users = getAllUsers(userManager); + if (users != null) { + for (UserInfo user : users) { + final int userId = user.id; + pkgSetting.setDisabledComponentsCopy( + disabledPkg.getDisabledComponents(userId), userId); + pkgSetting.setEnabledComponentsCopy( + disabledPkg.getEnabledComponents(userId), userId); + } + } + } + } + } + return pkgSetting; + } + + private static Map> createMimeGroups(Set mimeGroupNames) { + if (mimeGroupNames == null) { + return null; + } + + return new KeySetToValueMap<>(mimeGroupNames, new ArraySet<>()); + } + + /** + * Updates the given package setting using the provided information. + *

+ * WARNING: The provided PackageSetting object may be mutated. + */ + static void updatePackageSetting(@NonNull PackageSetting pkgSetting, + @Nullable PackageSetting disabledPkg, + @Nullable SharedUserSetting existingSharedUserSetting, + @Nullable SharedUserSetting sharedUser, + @NonNull File codePath, @Nullable String legacyNativeLibraryPath, + @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags, + int pkgPrivateFlags, @NonNull UserManagerService userManager, + @Nullable String[] usesSdkLibraries, @Nullable long[] usesSdkLibrariesVersions, + @Nullable boolean[] usesSdkLibrariesOptional, + @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions, + @Nullable Set mimeGroupNames, @NonNull UUID domainSetId, + int targetSdkVersion, byte[] restrictUpdatedHash, boolean isDontKill) + throws PackageManagerException { + final String pkgName = pkgSetting.getPackageName(); + final File oldCodePath = pkgSetting.getPath(); + if (sharedUser != null) { + if (!Objects.equals(existingSharedUserSetting, sharedUser)) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Package " + pkgName + " shared user changed from " + + (existingSharedUserSetting != null + ? existingSharedUserSetting.name : "") + + " to " + sharedUser.name); + throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED, + "Updating application package " + pkgName + " failed"); + } + pkgSetting.setSharedUserAppId(sharedUser.mAppId); + } else { + // migrating off shared user + pkgSetting.setSharedUserAppId(INVALID_UID); + } + + if (!oldCodePath.equals(codePath)) { + final boolean isSystem = pkgSetting.isSystem(); + Slog.i(PackageManagerService.TAG, + "Update" + (isSystem ? " system" : "") + + " package " + pkgName + + " code path from " + pkgSetting.getPathString() + + " to " + codePath.toString() + + "; Retain data and using new"); + if (!isSystem) { + // The package isn't considered as installed if the application was + // first installed by another user. Update the installed flag when the + // application ever becomes part of the system. + if ((pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 && disabledPkg == null) { + final List allUserInfos = getAllUsers(userManager); + if (allUserInfos != null) { + for (UserInfo userInfo : allUserInfos) { + pkgSetting.setInstalled(true, userInfo.id); + pkgSetting.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userInfo.id); + } + } + } + + // Since we've changed paths, prefer the new native library path over + // the one stored in the package settings since we might have moved from + // internal to external storage or vice versa. + pkgSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPath); + } + pkgSetting.setPath(codePath); + if (isDontKill && Flags.improveInstallDontKill()) { + // We retain old code paths for DONT_KILL installs. Keep a record of old paths until + // they are removed. + pkgSetting.addOldPath(oldCodePath); + } + } + + pkgSetting.setPrimaryCpuAbi(primaryCpuAbi) + .setSecondaryCpuAbi(secondaryCpuAbi) + .updateMimeGroups(mimeGroupNames) + .setDomainSetId(domainSetId) + .setTargetSdkVersion(targetSdkVersion) + .setRestrictUpdateHash(restrictUpdatedHash); + // Update SDK library dependencies if needed. + if (usesSdkLibraries != null && usesSdkLibrariesVersions != null + && usesSdkLibrariesOptional != null + && usesSdkLibraries.length == usesSdkLibrariesVersions.length + && usesSdkLibraries.length == usesSdkLibrariesOptional.length) { + pkgSetting.setUsesSdkLibraries(usesSdkLibraries) + .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions) + .setUsesSdkLibrariesOptional(usesSdkLibrariesOptional); + } else { + pkgSetting.setUsesSdkLibraries(null) + .setUsesSdkLibrariesVersionsMajor(null) + .setUsesSdkLibrariesOptional(null); + + } + + // Update static shared library dependencies if needed. + if (usesStaticLibraries != null && usesStaticLibrariesVersions != null + && usesStaticLibraries.length == usesStaticLibrariesVersions.length) { + pkgSetting.setUsesStaticLibraries(usesStaticLibraries) + .setUsesStaticLibrariesVersions(usesStaticLibrariesVersions); + } else { + pkgSetting.setUsesStaticLibraries(null) + .setUsesStaticLibrariesVersions(null); + } + + // If what we are scanning is a system (and possibly privileged) package, + // then make it so, regardless of whether it was previously installed only + // in the data partition. Reset first. + int newPkgFlags = pkgSetting.getFlags(); + newPkgFlags &= ~ApplicationInfo.FLAG_SYSTEM; + newPkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM; + // Only set pkgFlags. + pkgSetting.setFlags(newPkgFlags); + + boolean wasRequiredForSystemUser = (pkgSetting.getPrivateFlags() + & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0; + if (wasRequiredForSystemUser) { + pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER; + } else { + pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER; + } + pkgSetting.setPrivateFlags(pkgPrivateFlags); + } + + /** + * Registers a user ID with the system. Potentially allocates a new user ID. + * @return {@code true} if a new app ID was created in the process. {@code false} can be + * returned in the case that a shared user ID already exists or the explicit app ID is + * already registered. + * @throws PackageManagerException If a user ID could not be allocated. + */ + boolean registerAppIdLPw(PackageSetting p, boolean forceNew) throws PackageManagerException { + final boolean createdNew; + if (p.getAppId() == 0 || forceNew) { + // Assign new user ID + p.setAppId(mAppIds.acquireAndRegisterNewAppId(p)); + createdNew = true; + } else { + // Add new setting to list of user IDs + createdNew = mAppIds.registerExistingAppId(p.getAppId(), p, p.getPackageName()); + } + if (p.getAppId() < 0) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Package " + p.getPackageName() + " could not be assigned a valid UID"); + throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE, + "Package " + p.getPackageName() + " could not be assigned a valid UID"); + } + return createdNew; + } + + /** + * Writes per-user package restrictions if the user state has changed. If the user + * state has not changed, this does nothing. + */ + void writeUserRestrictionsLPw(PackageSetting newPackage, PackageSetting oldPackage) { + // package doesn't exist; do nothing + if (getPackageLPr(newPackage.getPackageName()) == null) { + return; + } + // no users defined; do nothing + final List allUsers = getAllUsers(UserManagerService.getInstance()); + if (allUsers == null) { + return; + } + for (UserInfo user : allUsers) { + final PackageUserState oldUserState = oldPackage == null + ? PackageUserState.DEFAULT + : oldPackage.readUserState(user.id); + if (!oldUserState.equals(newPackage.readUserState(user.id))) { + writePackageRestrictionsLPr(user.id); + } + } + } + + static boolean isAdbInstallDisallowed(UserManagerService userManager, int userId) { + return userManager.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, + userId); + } + + // TODO: Move to scanPackageOnlyLI() after verifying signatures are setup correctly + // by that time. + void insertPackageSettingLPw(PackageSetting p, AndroidPackage pkg) { + // Update signatures if needed. + if (p.getSigningDetails().getSignatures() == null) { + p.setSigningDetails(pkg.getSigningDetails()); + } + // If this app defines a shared user id initialize + // the shared user signatures as well. + SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p); + if (sharedUserSetting != null) { + if (sharedUserSetting.signatures.mSigningDetails.getSignatures() == null) { + sharedUserSetting.signatures.mSigningDetails = pkg.getSigningDetails(); + } + } + addPackageSettingLPw(p, sharedUserSetting); + } + + // Utility method that adds a PackageSetting to mPackages and + // completes updating the shared user attributes and any restored + // app link verification state + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + void addPackageSettingLPw(PackageSetting p, SharedUserSetting sharedUser) { + mPackages.put(p.getPackageName(), p); + if (sharedUser != null) { + SharedUserSetting existingSharedUserSetting = getSharedUserSettingLPr(p); + if (existingSharedUserSetting != null && existingSharedUserSetting != sharedUser) { + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Package " + p.getPackageName() + " was user " + + existingSharedUserSetting + " but is now " + sharedUser + + "; I am not changing its files so it will probably fail!"); + existingSharedUserSetting.removePackage(p); + } else if (p.getAppId() != 0 && p.getAppId() != sharedUser.mAppId) { + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Package " + p.getPackageName() + " was app id " + p.getAppId() + + " but is now user " + sharedUser + + " with app id " + sharedUser.mAppId + + "; I am not changing its files so it will probably fail!"); + } + + sharedUser.addPackage(p); + p.setSharedUserAppId(sharedUser.mAppId); + p.setAppId(sharedUser.mAppId); + } + + // If we know about this app id, we have to update it as it + // has to point to the same PackageSetting instance as the package. + Object appIdPs = getSettingLPr(p.getAppId()); + if (sharedUser == null) { + if (appIdPs != null && appIdPs != p) { + mAppIds.replaceSetting(p.getAppId(), p); + } + } else { + if (appIdPs != null && appIdPs != sharedUser) { + mAppIds.replaceSetting(p.getAppId(), sharedUser); + } + } + } + + boolean checkAndPruneSharedUserLPw(SharedUserSetting s, boolean skipCheck) { + if (skipCheck || (s.getPackageStates().isEmpty() + && s.getDisabledPackageStates().isEmpty())) { + if (mSharedUsers.remove(s.name) != null) { + removeAppIdLPw(s.mAppId); + return true; + } + } + return false; + } + + + /** + * Remove package from mPackages and its corresponding AppId. + * + * @return True if the AppId has been removed. + * False if the app doesn't exist, or if the app has a shared UID and there are other apps that + * still use the same shared UID even after the target app is removed. + */ + boolean removePackageAndAppIdLPw(String name) { + final PackageSetting p = mPackages.remove(name); + if (p != null) { + removeInstallerPackageStatus(name); + SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(p); + if (sharedUserSetting != null) { + sharedUserSetting.removePackage(p); + return checkAndPruneSharedUserLPw(sharedUserSetting, false); + } else { + removeAppIdLPw(p.getAppId()); + return true; + } + } + return false; + } + + /** + * Checks if {@param packageName} is an installer package and if so, clear the installer + * package name of the packages that are installed by this. + */ + private void removeInstallerPackageStatus(String packageName) { + // Check if the package to be removed is an installer package. + if (!mInstallerPackages.contains(packageName)) { + return; + } + for (int i = 0; i < mPackages.size(); i++) { + mPackages.valueAt(i).removeInstallerPackage(packageName); + } + mInstallerPackages.remove(packageName); + } + + public boolean isAssignAppUid(int appId) { + return mAppIds.isAssignAppUid(appId); + } + + /** Gets the setting associated with the provided App ID */ + public SettingBase getSettingLPr(int appId) { + return mAppIds.getSetting(appId); + } + + /** Unregisters the provided app ID. */ + void removeAppIdLPw(int appId) { + mAppIds.removeSetting(appId); + } + /** + * Transparently convert a SharedUserSetting into PackageSettings without changing appId. + * The sharedUser passed to this method has to be {@link SharedUserSetting#isSingleUser()}. + */ + void convertSharedUserSettingsLPw(SharedUserSetting sharedUser) { + final PackageSetting ps = sharedUser.getPackageSettings().valueAt(0); + mAppIds.replaceSetting(sharedUser.getAppId(), ps); + + // Unlink the SharedUserSetting + ps.setSharedUserAppId(INVALID_UID); + if (!sharedUser.getDisabledPackageSettings().isEmpty()) { + final PackageSetting disabledPs = sharedUser.getDisabledPackageSettings().valueAt(0); + disabledPs.setSharedUserAppId(INVALID_UID); + } + mSharedUsers.remove(sharedUser.getName()); + } + + /** + * Check and convert eligible SharedUserSettings to PackageSettings. + */ + void checkAndConvertSharedUserSettingsLPw(SharedUserSetting sharedUser) { + if (!sharedUser.isSingleUser()) return; + final AndroidPackage pkg = sharedUser.getPackageSettings().valueAt(0).getPkg(); + if (pkg != null && pkg.isLeavingSharedUser() + && SharedUidMigration.applyStrategy(BEST_EFFORT)) { + convertSharedUserSettingsLPw(sharedUser); + } + } + + PreferredIntentResolver editPreferredActivitiesLPw(int userId) { + PreferredIntentResolver pir = mPreferredActivities.get(userId); + if (pir == null) { + pir = new PreferredIntentResolver(); + mPreferredActivities.put(userId, pir); + } + return pir; + } + + PersistentPreferredIntentResolver editPersistentPreferredActivitiesLPw(int userId) { + PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId); + if (ppir == null) { + ppir = new PersistentPreferredIntentResolver(); + mPersistentPreferredActivities.put(userId, ppir); + } + return ppir; + } + + CrossProfileIntentResolver editCrossProfileIntentResolverLPw(int userId) { + CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(userId); + if (cpir == null) { + cpir = new CrossProfileIntentResolver(); + mCrossProfileIntentResolvers.put(userId, cpir); + } + return cpir; + } + + String getPendingDefaultBrowserLPr(int userId) { + return mPendingDefaultBrowser.get(userId); + } + + void setPendingDefaultBrowserLPw(String defaultBrowser, int userId) { + mPendingDefaultBrowser.put(userId, defaultBrowser); + } + + String removePendingDefaultBrowserLPw(int userId) { + return mPendingDefaultBrowser.removeReturnOld(userId); + } + + private File getUserSystemDirectory(int userId) { + // This instead of Environment.getUserSystemDirectory(userId) to support testing. + return new File(new File(mSystemDir, "users"), Integer.toString(userId)); + } + + private ResilientAtomicFile getUserPackagesStateFile(int userId) { + File mainFile = new File(getUserSystemDirectory(userId), "package-restrictions.xml"); + File temporaryBackup = new File(getUserSystemDirectory(userId), + "package-restrictions-backup.xml"); + File reserveCopy = new File(getUserSystemDirectory(userId), + "package-restrictions.xml.reservecopy"); + return new ResilientAtomicFile(mainFile, temporaryBackup, reserveCopy, + FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IWGRP, + "package restrictions", this); + } + + private ResilientAtomicFile getSettingsFile() { + return new ResilientAtomicFile(mSettingsFilename, mPreviousSettingsFilename, + mSettingsReserveCopyFilename, + FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IWGRP, + "package manager settings", this); + } + + private File getUserRuntimePermissionsFile(int userId) { + return new File(getUserSystemDirectory(userId), RUNTIME_PERMISSIONS_FILE_NAME); + } + + // Default version is writing restrictions asynchronously. + void writeAllUsersPackageRestrictionsLPr() { + writeAllUsersPackageRestrictionsLPr(/*sync=*/false); + } + + void writeAllUsersPackageRestrictionsLPr(boolean sync) { + List users = getAllUsers(UserManagerService.getInstance()); + if (users == null) return; + + if (sync) { + // Cancel all pending per-user writes. + synchronized (mPackageRestrictionsLock) { + mPendingAsyncPackageRestrictionsWrites.clear(); + } + mHandler.removeMessages(WRITE_USER_PACKAGE_RESTRICTIONS); + } + + for (UserInfo user : users) { + writePackageRestrictionsLPr(user.id, sync); + } + } + + void writeAllRuntimePermissionsLPr() { + for (int userId : UserManagerService.getInstance().getUserIds()) { + mRuntimePermissionsPersistence.writeStateForUserAsync(userId); + } + } + + boolean isPermissionUpgradeNeeded(int userId) { + return mRuntimePermissionsPersistence.isPermissionUpgradeNeeded(userId); + } + + void updateRuntimePermissionsFingerprint(@UserIdInt int userId) { + mRuntimePermissionsPersistence.updateRuntimePermissionsFingerprint(userId); + } + + int getDefaultRuntimePermissionsVersion(int userId) { + return mRuntimePermissionsPersistence.getVersion(userId); + } + + void setDefaultRuntimePermissionsVersion(int version, int userId) { + mRuntimePermissionsPersistence.setVersion(version, userId); + } + + void setPermissionControllerVersion(long version) { + mRuntimePermissionsPersistence.setPermissionControllerVersion(version); + } + + public VersionInfo findOrCreateVersion(String volumeUuid) { + VersionInfo ver = mVersion.get(volumeUuid); + if (ver == null) { + ver = new VersionInfo(); + mVersion.put(volumeUuid, ver); + } + return ver; + } + + public VersionInfo getInternalVersion() { + return mVersion.get(StorageManager.UUID_PRIVATE_INTERNAL); + } + + public VersionInfo getExternalVersion() { + return mVersion.get(StorageManager.UUID_PRIMARY_PHYSICAL); + } + + public void onVolumeForgotten(String fsUuid) { + mVersion.remove(fsUuid); + } + + /** + * Applies the preferred activity state described by the given XML. This code + * also supports the restore-from-backup code path. + * + * @see PreferredActivityBackupHelper + */ + void readPreferredActivitiesLPw(TypedXmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + 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(TAG_ITEM)) { + PreferredActivity pa = new PreferredActivity(parser); + if (pa.mPref.getParseError() == null) { + final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId); + if (resolver.shouldAddPreferredActivity(pa)) { + resolver.addFilter(null, pa); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: " + + pa.mPref.getParseError() + " at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under : " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } + + private void readPersistentPreferredActivitiesLPw(TypedXmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + 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(TAG_ITEM)) { + PersistentPreferredActivity ppa = new PersistentPreferredActivity(parser); + editPersistentPreferredActivitiesLPw(userId).addFilter(null, ppa); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under <" + TAG_PERSISTENT_PREFERRED_ACTIVITIES + ">: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } + + private void readCrossProfileIntentFiltersLPw(TypedXmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + final String tagName = parser.getName(); + if (tagName.equals(TAG_ITEM)) { + CrossProfileIntentFilter cpif = new CrossProfileIntentFilter(parser); + editCrossProfileIntentResolverLPw(userId).addFilter(null, cpif); + } else { + String msg = "Unknown element under " + TAG_CROSS_PROFILE_INTENT_FILTERS + ": " + + tagName; + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + } + + void readDefaultAppsLPw(XmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + String defaultBrowser = readDefaultApps(parser); + if (defaultBrowser != null) { + mPendingDefaultBrowser.put(userId, defaultBrowser); + } + } + + /** + * @return the package name for the default browser app, or {@code null} if none. + */ + @Nullable + static String readDefaultApps(@NonNull XmlPullParser parser) + throws XmlPullParserException, IOException { + String defaultBrowser = null; + int outerDepth = parser.getDepth(); + int type; + 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(TAG_DEFAULT_BROWSER)) { + defaultBrowser = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); + } else if (tagName.equals(TAG_DEFAULT_DIALER)) { + // Ignored. + } else { + String msg = "Unknown element under " + TAG_DEFAULT_APPS + ": " + + parser.getName(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + return defaultBrowser; + } + + void readBlockUninstallPackagesLPw(TypedXmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + int outerDepth = parser.getDepth(); + int type; + ArraySet packages = new ArraySet<>(); + 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(TAG_BLOCK_UNINSTALL)) { + String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); + packages.add(packageName); + } else { + String msg = "Unknown element under " + TAG_BLOCK_UNINSTALL_PACKAGES + ": " + + parser.getName(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + XmlUtils.skipCurrentTag(parser); + } + } + if (packages.isEmpty()) { + mBlockUninstallPackages.remove(userId); + } else { + mBlockUninstallPackages.put(userId, packages); + } + } + + @Override + public void logEvent(int priority, String msg) { + mReadMessages.append(msg + "\n"); + PackageManagerService.reportSettingsProblem(priority, msg); + } + + + void readPackageRestrictionsLPr(int userId, + @NonNull ArrayMap origFirstInstallTimes) { + if (DEBUG_MU) { + Log.i(TAG, "Reading package restrictions for user=" + userId); + } + + try (ResilientAtomicFile atomicFile = getUserPackagesStateFile(userId)) { + FileInputStream str = null; + try { + synchronized (mPackageRestrictionsLock) { + str = atomicFile.openRead(); + if (str == null) { + // At first boot, make sure no packages are stopped. + // We usually want to have third party apps initialize + // in the stopped state, but not at first boot. Also + // consider all applications to be installed. + for (PackageSetting pkg : mPackages.values()) { + pkg.setUserState(userId, pkg.getCeDataInode(userId), + pkg.getDeDataInode(userId), COMPONENT_ENABLED_STATE_DEFAULT, + true /*installed*/, + false /*stopped*/, + false /*notLaunched*/, + false /*hidden*/, + 0 /*distractionFlags*/, + null /*suspendParams*/, + false /*instantApp*/, + false /*virtualPreload*/, + null /*lastDisableAppCaller*/, + null /*enabledComponents*/, + null /*disabledComponents*/, + PackageManager.INSTALL_REASON_UNKNOWN, + PackageManager.UNINSTALL_REASON_UNKNOWN, + null /*harmfulAppWarning*/, + null /* splashScreenTheme*/, + 0 /*firstInstallTime*/, + PackageManager.USER_MIN_ASPECT_RATIO_UNSET, + null /*archiveState*/ + ); + } + return; + } + } + + final TypedXmlPullParser parser = Xml.resolvePullParser(str); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + // nothing + } + + if (type != XmlPullParser.START_TAG) { + mReadMessages.append("No start tag found in package restrictions file\n"); + PackageManagerService.reportSettingsProblem(Log.WARN, + "No start tag found in package manager package restrictions file"); + return; + } + + int outerDepth = parser.getDepth(); + PackageSetting ps = null; + 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(TAG_PACKAGE)) { + String name = parser.getAttributeValue(null, ATTR_NAME); + ps = mPackages.get(name); + if (ps == null) { + Slog.w(PackageManagerService.TAG, + "No package known for package restrictions " + name); + XmlUtils.skipCurrentTag(parser); + continue; + } + + final long ceDataInode = + parser.getAttributeLong(null, ATTR_CE_DATA_INODE, 0); + final long deDataInode = + parser.getAttributeLong(null, ATTR_DE_DATA_INODE, 0); + final boolean installed = + parser.getAttributeBoolean(null, ATTR_INSTALLED, true); + final boolean stopped = + parser.getAttributeBoolean(null, ATTR_STOPPED, false); + final boolean notLaunched = + parser.getAttributeBoolean(null, ATTR_NOT_LAUNCHED, false); + + // For backwards compatibility with the previous name of "blocked", which + // now means hidden, read the old attribute as well. + boolean hidden = parser.getAttributeBoolean(null, ATTR_HIDDEN, false); + if (!hidden) { + hidden = parser.getAttributeBoolean(null, ATTR_BLOCKED, false); + } + + final int distractionFlags = parser.getAttributeInt(null, + ATTR_DISTRACTION_FLAGS, 0); + final boolean suspended = parser.getAttributeBoolean(null, ATTR_SUSPENDED, + false); + String oldSuspendingPackage = parser.getAttributeValue(null, + ATTR_SUSPENDING_PACKAGE); + final String dialogMessage = parser.getAttributeValue(null, + ATTR_SUSPEND_DIALOG_MESSAGE); + if (suspended && oldSuspendingPackage == null) { + oldSuspendingPackage = PLATFORM_PACKAGE_NAME; + } + + final boolean blockUninstall = + parser.getAttributeBoolean(null, ATTR_BLOCK_UNINSTALL, false); + final boolean instantApp = + parser.getAttributeBoolean(null, ATTR_INSTANT_APP, false); + final boolean virtualPreload = + parser.getAttributeBoolean(null, ATTR_VIRTUAL_PRELOAD, false); + final int enabled = parser.getAttributeInt(null, ATTR_ENABLED, + COMPONENT_ENABLED_STATE_DEFAULT); + final String enabledCaller = parser.getAttributeValue(null, + ATTR_ENABLED_CALLER); + final String harmfulAppWarning = + parser.getAttributeValue(null, ATTR_HARMFUL_APP_WARNING); + final int verifState = parser.getAttributeInt(null, + ATTR_DOMAIN_VERIFICATION_STATE, + PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED); + final int installReason = parser.getAttributeInt(null, ATTR_INSTALL_REASON, + PackageManager.INSTALL_REASON_UNKNOWN); + final int uninstallReason = parser.getAttributeInt(null, + ATTR_UNINSTALL_REASON, + PackageManager.UNINSTALL_REASON_UNKNOWN); + final String splashScreenTheme = parser.getAttributeValue(null, + ATTR_SPLASH_SCREEN_THEME); + final long firstInstallTime = parser.getAttributeLongHex(null, + ATTR_FIRST_INSTALL_TIME, 0); + final int minAspectRatio = parser.getAttributeInt(null, + ATTR_MIN_ASPECT_RATIO, + PackageManager.USER_MIN_ASPECT_RATIO_UNSET); + + ArraySet enabledComponents = null; + ArraySet disabledComponents = null; + SuspendDialogInfo oldSuspendDialogInfo = null; + PersistableBundle oldSuspendedAppExtras = null; + PersistableBundle oldSuspendedLauncherExtras = null; + ArchiveState archiveState = null; + + int packageDepth = parser.getDepth(); + ArrayMap suspendParamsMap = null; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > packageDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + switch (parser.getName()) { + case TAG_ENABLED_COMPONENTS: + enabledComponents = readComponentsLPr(parser); + break; + case TAG_DISABLED_COMPONENTS: + disabledComponents = readComponentsLPr(parser); + break; + case TAG_SUSPENDED_DIALOG_INFO: + oldSuspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser); + break; + case TAG_SUSPENDED_APP_EXTRAS: + oldSuspendedAppExtras = PersistableBundle.restoreFromXml( + parser); + break; + case TAG_SUSPENDED_LAUNCHER_EXTRAS: + oldSuspendedLauncherExtras = PersistableBundle.restoreFromXml( + parser); + break; + case TAG_SUSPEND_PARAMS: + Map.Entry entry = + readSuspensionParamsLPr(userId, parser); + if (entry == null) { + continue; + } + if (suspendParamsMap == null) { + suspendParamsMap = new ArrayMap<>(); + } + suspendParamsMap.put(entry.getKey(), entry.getValue()); + break; + case TAG_ARCHIVE_STATE: + archiveState = parseArchiveState(parser); + break; + default: + Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag " + + TAG_PACKAGE); + } + } + if (oldSuspendDialogInfo == null && !TextUtils.isEmpty(dialogMessage)) { + oldSuspendDialogInfo = new SuspendDialogInfo.Builder() + .setMessage(dialogMessage) + .build(); + } + if (suspended && suspendParamsMap == null) { + final SuspendParams suspendParams = new SuspendParams( + oldSuspendDialogInfo, + oldSuspendedAppExtras, + oldSuspendedLauncherExtras, + false /* quarantined */); + suspendParamsMap = new ArrayMap<>(); + suspendParamsMap.put( + UserPackage.of(userId, oldSuspendingPackage), suspendParams); + } + + if (blockUninstall) { + setBlockUninstallLPw(userId, name, true); + } + ps.setUserState( + userId, ceDataInode, deDataInode, enabled, installed, stopped, + notLaunched, hidden, distractionFlags, suspendParamsMap, instantApp, + virtualPreload, enabledCaller, enabledComponents, + disabledComponents, installReason, uninstallReason, + harmfulAppWarning, splashScreenTheme, + firstInstallTime != 0 ? firstInstallTime + : origFirstInstallTimes.getOrDefault(name, 0L), + minAspectRatio, archiveState); + mDomainVerificationManager.setLegacyUserState(name, userId, verifState); + } else if (tagName.equals("preferred-activities")) { + readPreferredActivitiesLPw(parser, userId); + } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) { + readPersistentPreferredActivitiesLPw(parser, userId); + } else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) { + readCrossProfileIntentFiltersLPw(parser, userId); + } else if (tagName.equals(TAG_DEFAULT_APPS)) { + readDefaultAppsLPw(parser, userId); + } else if (tagName.equals(TAG_BLOCK_UNINSTALL_PACKAGES)) { + readBlockUninstallPackagesLPw(parser, userId); + } else { + Slog.w(PackageManagerService.TAG, + "Unknown element under : " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } catch (IOException | XmlPullParserException e) { + // Remove corrupted file and retry. + atomicFile.failRead(str, e); + + readPackageRestrictionsLPr(userId, origFirstInstallTimes); + } + } + } + + @Nullable + private static Map.Entry readSuspensionParamsLPr( + int userId, TypedXmlPullParser parser) throws IOException { + final String suspendingPackage = parser.getAttributeValue(null, ATTR_SUSPENDING_PACKAGE); + if (suspendingPackage == null) { + Slog.wtf(TAG, "No suspendingPackage found inside tag " + TAG_SUSPEND_PARAMS); + return null; + } + final int suspendingUserId = userId; + return Map.entry( + UserPackage.of(suspendingUserId, suspendingPackage), + SuspendParams.restoreFromXml(parser)); + } + + private static ArchiveState parseArchiveState(TypedXmlPullParser parser) + throws XmlPullParserException, IOException { + String installerTitle = parser.getAttributeValue(null, + ATTR_ARCHIVE_INSTALLER_TITLE); + final long archiveTimeMillis = parser.getAttributeLongHex(null, ATTR_ARCHIVE_TIME, 0); + List activityInfos = + parseArchiveActivityInfos(parser); + + if (installerTitle == null) { + Slog.wtf(TAG, "parseArchiveState: installerTitle is null"); + return null; + } + + if (activityInfos.size() < 1) { + Slog.wtf(TAG, "parseArchiveState: activityInfos is empty"); + return null; + } + + return new ArchiveState(activityInfos, installerTitle, archiveTimeMillis); + } + + private static List parseArchiveActivityInfos( + TypedXmlPullParser parser) throws XmlPullParserException, IOException { + List activityInfos = new ArrayList<>(); + int type; + int outerDepth = parser.getDepth(); + String tagName; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + tagName = parser.getName(); + if (tagName.equals(TAG_ARCHIVE_ACTIVITY_INFO)) { + String title = parser.getAttributeValue(null, + ATTR_ARCHIVE_ACTIVITY_TITLE); + String originalComponentName = + parser.getAttributeValue(null, ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME); + String iconAttribute = parser.getAttributeValue(null, + ATTR_ARCHIVE_ICON_PATH); + Path iconPath = iconAttribute == null ? null : Path.of(iconAttribute); + String monochromeAttribute = parser.getAttributeValue(null, + ATTR_ARCHIVE_MONOCHROME_ICON_PATH); + Path monochromeIconPath = monochromeAttribute == null ? null : Path.of( + monochromeAttribute); + + if (title == null || originalComponentName == null || iconPath == null) { + Slog.wtf( + TAG, + TextUtils.formatSimple( + "Missing attributes in tag %s. %s: %s, %s: %s, %s: %s", + TAG_ARCHIVE_ACTIVITY_INFO, + ATTR_ARCHIVE_ACTIVITY_TITLE, + title, + ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME, + originalComponentName, + ATTR_ARCHIVE_ICON_PATH, + iconPath)); + continue; + } + + activityInfos.add( + new ArchiveState.ArchiveActivityInfo( + title, + ComponentName.unflattenFromString(originalComponentName), + iconPath, + monochromeIconPath)); + } + } + return activityInfos; + } + + void setBlockUninstallLPw(int userId, String packageName, boolean blockUninstall) { + ArraySet packages = mBlockUninstallPackages.get(userId); + if (blockUninstall) { + if (packages == null) { + packages = new ArraySet(); + mBlockUninstallPackages.put(userId, packages); + } + packages.add(packageName); + } else if (packages != null) { + packages.remove(packageName); + if (packages.isEmpty()) { + mBlockUninstallPackages.remove(userId); + } + } + } + + void clearBlockUninstallLPw(int userId) { + mBlockUninstallPackages.remove(userId); + } + + boolean getBlockUninstallLPr(int userId, String packageName) { + ArraySet packages = mBlockUninstallPackages.get(userId); + if (packages == null) { + return false; + } + return packages.contains(packageName); + } + + private ArraySet readComponentsLPr(TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + ArraySet components = null; + int type; + int outerDepth = parser.getDepth(); + String tagName; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + tagName = parser.getName(); + if (tagName.equals(TAG_ITEM)) { + String componentName = parser.getAttributeValue(null, ATTR_NAME); + if (componentName != null) { + if (components == null) { + components = new ArraySet(); + } + components.add(componentName); + } + } + } + return components; + } + + /** + * Record the state of preferred activity configuration into XML. This is used both + * for recording packages.xml internally and for supporting backup/restore of the + * preferred activity configuration. + */ + void writePreferredActivitiesLPr(TypedXmlSerializer serializer, int userId, boolean full) + throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(null, "preferred-activities"); + PreferredIntentResolver pir = mPreferredActivities.get(userId); + if (pir != null) { + for (final PreferredActivity pa : pir.filterSet()) { + serializer.startTag(null, TAG_ITEM); + pa.writeToXml(serializer, full); + serializer.endTag(null, TAG_ITEM); + } + } + serializer.endTag(null, "preferred-activities"); + } + + void writePersistentPreferredActivitiesLPr(TypedXmlSerializer serializer, int userId) + throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES); + PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId); + if (ppir != null) { + for (final PersistentPreferredActivity ppa : ppir.filterSet()) { + serializer.startTag(null, TAG_ITEM); + ppa.writeToXml(serializer); + serializer.endTag(null, TAG_ITEM); + } + } + serializer.endTag(null, TAG_PERSISTENT_PREFERRED_ACTIVITIES); + } + + void writeCrossProfileIntentFiltersLPr(TypedXmlSerializer serializer, int userId) + throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS); + CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(userId); + if (cpir != null) { + for (final CrossProfileIntentFilter cpif : cpir.filterSet()) { + serializer.startTag(null, TAG_ITEM); + cpif.writeToXml(serializer); + serializer.endTag(null, TAG_ITEM); + } + } + serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS); + } + + void writeDefaultAppsLPr(XmlSerializer serializer, int userId) + throws IllegalArgumentException, IllegalStateException, IOException { + String defaultBrowser = mPendingDefaultBrowser.get(userId); + writeDefaultApps(serializer, defaultBrowser); + } + + static void writeDefaultApps(@NonNull XmlSerializer serializer, @Nullable String defaultBrowser) + throws IllegalArgumentException, IllegalStateException, IOException { + serializer.startTag(null, TAG_DEFAULT_APPS); + if (!TextUtils.isEmpty(defaultBrowser)) { + serializer.startTag(null, TAG_DEFAULT_BROWSER); + serializer.attribute(null, ATTR_PACKAGE_NAME, defaultBrowser); + serializer.endTag(null, TAG_DEFAULT_BROWSER); + } + serializer.endTag(null, TAG_DEFAULT_APPS); + } + + void writeBlockUninstallPackagesLPr(TypedXmlSerializer serializer, int userId) + throws IOException { + ArraySet packages = mBlockUninstallPackages.get(userId); + if (packages != null) { + serializer.startTag(null, TAG_BLOCK_UNINSTALL_PACKAGES); + for (int i = 0; i < packages.size(); i++) { + serializer.startTag(null, TAG_BLOCK_UNINSTALL); + serializer.attribute(null, ATTR_PACKAGE_NAME, packages.valueAt(i)); + serializer.endTag(null, TAG_BLOCK_UNINSTALL); + } + serializer.endTag(null, TAG_BLOCK_UNINSTALL_PACKAGES); + } + } + + // Default version is writing restrictions asynchronously. + void writePackageRestrictionsLPr(int userId) { + writePackageRestrictionsLPr(userId, /*sync=*/false); + } + + void writePackageRestrictionsLPr(int userId, boolean sync) { + invalidatePackageCache(); + + final long startTime = SystemClock.uptimeMillis(); + + if (sync) { + writePackageRestrictions(userId, startTime, sync); + } else { + if (DEBUG_MU) { + Log.i(TAG, "Scheduling deferred IO sync for user=" + userId); + } + synchronized (mPackageRestrictionsLock) { + int pending = mPendingAsyncPackageRestrictionsWrites.get(userId, 0) + 1; + mPendingAsyncPackageRestrictionsWrites.put(userId, pending); + } + Runnable r = () -> writePackageRestrictions(userId, startTime, sync); + mHandler.obtainMessage(WRITE_USER_PACKAGE_RESTRICTIONS, r).sendToTarget(); + } + } + + void writePackageRestrictions(Integer[] userIds) { + invalidatePackageCache(); + final long startTime = SystemClock.uptimeMillis(); + for (int userId : userIds) { + writePackageRestrictions(userId, startTime, /*sync=*/true); + } + } + + void writePackageRestrictions(int userId, long startTime, boolean sync) { + if (DEBUG_MU) { + Log.i(TAG, "Writing package restrictions for user=" + userId); + } + + FileOutputStream str = null; + try (ResilientAtomicFile atomicFile = getUserPackagesStateFile(userId)) { + try { + synchronized (mPackageRestrictionsLock) { + if (!sync) { + int pending = mPendingAsyncPackageRestrictionsWrites.get(userId, 0) - 1; + if (pending < 0) { + Log.i(TAG, "Cancel writing package restrictions for user=" + userId); + return; + } + mPendingAsyncPackageRestrictionsWrites.put(userId, pending); + } + + try { + str = atomicFile.startWrite(); + } catch (java.io.IOException e) { + Slog.wtf(PackageManagerService.TAG, + "Unable to write package manager package restrictions, " + + " current changes will be lost at reboot", e); + return; + } + } + + synchronized (mLock) { + final TypedXmlSerializer serializer = Xml.resolveSerializer(str); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", + true); + + serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS); + + if (DEBUG_MU) { + Slogf.i(TAG, "Writing %s (%d packages)", atomicFile, + mPackages.values().size()); + } + for (final PackageSetting pkg : mPackages.values()) { + final PackageUserStateInternal ustate = pkg.readUserState(userId); + if (DEBUG_MU) { + Log.v(TAG, " pkg=" + pkg.getPackageName() + + ", installed=" + ustate.isInstalled() + + ", state=" + ustate.getEnabledState()); + } + + serializer.startTag(null, TAG_PACKAGE); + serializer.attribute(null, ATTR_NAME, pkg.getPackageName()); + if (ustate.getCeDataInode() != 0) { + serializer.attributeLong(null, ATTR_CE_DATA_INODE, + ustate.getCeDataInode()); + } + if (ustate.getDeDataInode() != 0) { + serializer.attributeLong(null, ATTR_DE_DATA_INODE, + ustate.getDeDataInode()); + } + if (!ustate.isInstalled()) { + serializer.attributeBoolean(null, ATTR_INSTALLED, false); + } + if (ustate.isStopped()) { + serializer.attributeBoolean(null, ATTR_STOPPED, true); + } + if (ustate.isNotLaunched()) { + serializer.attributeBoolean(null, ATTR_NOT_LAUNCHED, true); + } + if (ustate.isHidden()) { + serializer.attributeBoolean(null, ATTR_HIDDEN, true); + } + if (ustate.getDistractionFlags() != 0) { + serializer.attributeInt(null, ATTR_DISTRACTION_FLAGS, + ustate.getDistractionFlags()); + } + if (ustate.isSuspended()) { + serializer.attributeBoolean(null, ATTR_SUSPENDED, true); + } + if (ustate.isInstantApp()) { + serializer.attributeBoolean(null, ATTR_INSTANT_APP, true); + } + if (ustate.isVirtualPreload()) { + serializer.attributeBoolean(null, ATTR_VIRTUAL_PRELOAD, true); + } + if (ustate.getEnabledState() != COMPONENT_ENABLED_STATE_DEFAULT) { + serializer.attributeInt(null, ATTR_ENABLED, ustate.getEnabledState()); + } + if (ustate.getLastDisableAppCaller() != null) { + serializer.attribute(null, ATTR_ENABLED_CALLER, + ustate.getLastDisableAppCaller()); + } + if (ustate.getInstallReason() != PackageManager.INSTALL_REASON_UNKNOWN) { + serializer.attributeInt(null, ATTR_INSTALL_REASON, + ustate.getInstallReason()); + } + serializer.attributeLongHex(null, ATTR_FIRST_INSTALL_TIME, + ustate.getFirstInstallTimeMillis()); + if (ustate.getUninstallReason() + != PackageManager.UNINSTALL_REASON_UNKNOWN) { + serializer.attributeInt(null, ATTR_UNINSTALL_REASON, + ustate.getUninstallReason()); + } + if (ustate.getHarmfulAppWarning() != null) { + serializer.attribute(null, ATTR_HARMFUL_APP_WARNING, + ustate.getHarmfulAppWarning()); + } + if (ustate.getSplashScreenTheme() != null) { + serializer.attribute(null, ATTR_SPLASH_SCREEN_THEME, + ustate.getSplashScreenTheme()); + } + if (ustate.getMinAspectRatio() + != PackageManager.USER_MIN_ASPECT_RATIO_UNSET) { + serializer.attributeInt(null, ATTR_MIN_ASPECT_RATIO, + ustate.getMinAspectRatio()); + } + if (ustate.isSuspended()) { + for (int i = 0; i < ustate.getSuspendParams().size(); i++) { + final UserPackage suspendingPackage = + ustate.getSuspendParams().keyAt(i); + serializer.startTag(null, TAG_SUSPEND_PARAMS); + serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, + suspendingPackage.packageName); + final SuspendParams params = + ustate.getSuspendParams().valueAt(i); + if (params != null) { + params.saveToXml(serializer); + } + serializer.endTag(null, TAG_SUSPEND_PARAMS); + } + } + final ArraySet enabledComponents = ustate.getEnabledComponents(); + if (enabledComponents != null && enabledComponents.size() > 0) { + serializer.startTag(null, TAG_ENABLED_COMPONENTS); + for (int i = 0; i < enabledComponents.size(); i++) { + serializer.startTag(null, TAG_ITEM); + serializer.attribute(null, ATTR_NAME, + enabledComponents.valueAt(i)); + serializer.endTag(null, TAG_ITEM); + } + serializer.endTag(null, TAG_ENABLED_COMPONENTS); + } + final ArraySet disabledComponents = ustate.getDisabledComponents(); + if (disabledComponents != null && disabledComponents.size() > 0) { + serializer.startTag(null, TAG_DISABLED_COMPONENTS); + for (int i = 0; i < disabledComponents.size(); i++) { + serializer.startTag(null, TAG_ITEM); + serializer.attribute(null, ATTR_NAME, + disabledComponents.valueAt(i)); + serializer.endTag(null, TAG_ITEM); + } + serializer.endTag(null, TAG_DISABLED_COMPONENTS); + } + writeArchiveStateLPr(serializer, ustate.getArchiveState()); + + serializer.endTag(null, TAG_PACKAGE); + } + + writePreferredActivitiesLPr(serializer, userId, true); + writePersistentPreferredActivitiesLPr(serializer, userId); + writeCrossProfileIntentFiltersLPr(serializer, userId); + writeDefaultAppsLPr(serializer, userId); + writeBlockUninstallPackagesLPr(serializer, userId); + + serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS); + + serializer.endDocument(); + } + + atomicFile.finishWrite(str); + + if (DEBUG_MU) { + Log.i(TAG, "New package restrictions successfully written for user=" + userId + + ": " + atomicFile); + } + + com.android.internal.logging.EventLogTags.writeCommitSysConfigFile( + "package-user-" + userId, SystemClock.uptimeMillis() - startTime); + + // Done, all is good! + return; + } catch (java.io.IOException e) { + Slog.wtf(PackageManagerService.TAG, + "Unable to write package manager package restrictions, " + + " current changes will be lost at reboot", e); + if (str != null) { + atomicFile.failWrite(str); + } + } + } + } + + private void writeArchiveStateLPr(TypedXmlSerializer serializer, ArchiveState archiveState) + throws IOException { + if (archiveState == null) { + return; + } + + serializer.startTag(null, TAG_ARCHIVE_STATE); + serializer.attribute(null, ATTR_ARCHIVE_INSTALLER_TITLE, archiveState.getInstallerTitle()); + serializer.attributeLongHex(null, ATTR_ARCHIVE_TIME, archiveState.getArchiveTimeMillis()); + for (ArchiveState.ArchiveActivityInfo activityInfo : archiveState.getActivityInfos()) { + serializer.startTag(null, TAG_ARCHIVE_ACTIVITY_INFO); + serializer.attribute(null, ATTR_ARCHIVE_ACTIVITY_TITLE, activityInfo.getTitle()); + serializer.attribute( + null, + ATTR_ARCHIVE_ORIGINAL_COMPONENT_NAME, + activityInfo.getOriginalComponentName().flattenToString()); + if (activityInfo.getIconBitmap() != null) { + serializer.attribute(null, ATTR_ARCHIVE_ICON_PATH, + activityInfo.getIconBitmap().toAbsolutePath().toString()); + } + if (activityInfo.getMonochromeIconBitmap() != null) { + serializer.attribute(null, ATTR_ARCHIVE_MONOCHROME_ICON_PATH, + activityInfo.getMonochromeIconBitmap().toAbsolutePath().toString()); + } + serializer.endTag(null, TAG_ARCHIVE_ACTIVITY_INFO); + } + serializer.endTag(null, TAG_ARCHIVE_STATE); + } + + void readInstallPermissionsLPr(TypedXmlPullParser parser, + LegacyPermissionState permissionsState, List users) + throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + 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(TAG_ITEM)) { + String name = parser.getAttributeValue(null, ATTR_NAME); + final boolean granted = parser.getAttributeBoolean(null, ATTR_GRANTED, true); + final int flags = parser.getAttributeIntHex(null, ATTR_FLAGS, 0); + for (final UserInfo user : users) { + permissionsState.putPermissionState(new PermissionState(name, false, granted, + flags), user.id); + } + } else { + Slog.w(PackageManagerService.TAG, "Unknown element under : " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } + + void readUsesSdkLibLPw(TypedXmlPullParser parser, PackageSetting outPs) + throws IOException, XmlPullParserException { + String libName = parser.getAttributeValue(null, ATTR_NAME); + long libVersion = parser.getAttributeLong(null, ATTR_VERSION, -1); + boolean optional = parser.getAttributeBoolean(null, ATTR_OPTIONAL, true); + + if (libName != null && libVersion >= 0) { + outPs.setUsesSdkLibraries(ArrayUtils.appendElement(String.class, + outPs.getUsesSdkLibraries(), libName)); + outPs.setUsesSdkLibrariesVersionsMajor(ArrayUtils.appendLong( + outPs.getUsesSdkLibrariesVersionsMajor(), libVersion)); + outPs.setUsesSdkLibrariesOptional(ArrayUtils.appendBoolean( + outPs.getUsesSdkLibrariesOptional(), optional)); + } + + XmlUtils.skipCurrentTag(parser); + } + + void readUsesStaticLibLPw(TypedXmlPullParser parser, PackageSetting outPs) + throws IOException, XmlPullParserException { + String libName = parser.getAttributeValue(null, ATTR_NAME); + long libVersion = parser.getAttributeLong(null, ATTR_VERSION, -1); + + if (libName != null && libVersion >= 0) { + outPs.setUsesStaticLibraries(ArrayUtils.appendElement(String.class, + outPs.getUsesStaticLibraries(), libName)); + outPs.setUsesStaticLibrariesVersions(ArrayUtils.appendLong( + outPs.getUsesStaticLibrariesVersions(), libVersion)); + } + + XmlUtils.skipCurrentTag(parser); + } + + void writeUsesSdkLibLPw(TypedXmlSerializer serializer, String[] usesSdkLibraries, + long[] usesSdkLibraryVersions, boolean[] usesSdkLibrariesOptional) throws IOException { + if (ArrayUtils.isEmpty(usesSdkLibraries) || ArrayUtils.isEmpty(usesSdkLibraryVersions) + || usesSdkLibraries.length != usesSdkLibraryVersions.length) { + return; + } + final int libCount = usesSdkLibraries.length; + for (int i = 0; i < libCount; i++) { + final String libName = usesSdkLibraries[i]; + final long libVersion = usesSdkLibraryVersions[i]; + boolean libOptional = usesSdkLibrariesOptional[i]; + serializer.startTag(null, TAG_USES_SDK_LIB); + serializer.attribute(null, ATTR_NAME, libName); + serializer.attributeLong(null, ATTR_VERSION, libVersion); + serializer.attributeBoolean(null, ATTR_OPTIONAL, libOptional); + serializer.endTag(null, TAG_USES_SDK_LIB); + } + } + + void writeUsesStaticLibLPw(TypedXmlSerializer serializer, String[] usesStaticLibraries, + long[] usesStaticLibraryVersions) throws IOException { + if (ArrayUtils.isEmpty(usesStaticLibraries) || ArrayUtils.isEmpty(usesStaticLibraryVersions) + || usesStaticLibraries.length != usesStaticLibraryVersions.length) { + return; + } + final int libCount = usesStaticLibraries.length; + for (int i = 0; i < libCount; i++) { + final String libName = usesStaticLibraries[i]; + final long libVersion = usesStaticLibraryVersions[i]; + serializer.startTag(null, TAG_USES_STATIC_LIB); + serializer.attribute(null, ATTR_NAME, libName); + serializer.attributeLong(null, ATTR_VERSION, libVersion); + serializer.endTag(null, TAG_USES_STATIC_LIB); + } + } + + // Note: assumed "stopped" field is already cleared in all packages. + // Legacy reader, used to read in the old file format after an upgrade. Not used after that. + void readStoppedLPw() { + FileInputStream str = null; + if (mBackupStoppedPackagesFilename.exists()) { + try { + str = new FileInputStream(mBackupStoppedPackagesFilename); + mReadMessages.append("Reading from backup stopped packages file\n"); + PackageManagerService.reportSettingsProblem(Log.INFO, + "Need to read from backup stopped packages file"); + if (mStoppedPackagesFilename.exists()) { + // If both the backup and normal file exist, we + // ignore the normal one since it might have been + // corrupted. + Slog.w(PackageManagerService.TAG, "Cleaning up stopped packages file " + + mStoppedPackagesFilename); + mStoppedPackagesFilename.delete(); + } + } catch (java.io.IOException e) { + // We'll try for the normal settings file. + } + } + + try { + if (str == null) { + if (!mStoppedPackagesFilename.exists()) { + mReadMessages.append("No stopped packages file found\n"); + PackageManagerService.reportSettingsProblem(Log.INFO, + "No stopped packages file file; assuming all started"); + // At first boot, make sure no packages are stopped. + // We usually want to have third party apps initialize + // in the stopped state, but not at first boot. + for (PackageSetting pkg : mPackages.values()) { + pkg.setStopped(false, 0); + pkg.setNotLaunched(false, 0); + } + return; + } + str = new FileInputStream(mStoppedPackagesFilename); + } + final TypedXmlPullParser parser = Xml.resolvePullParser(str); + + int type; + while ((type=parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + mReadMessages.append("No start tag found in stopped packages file\n"); + PackageManagerService.reportSettingsProblem(Log.WARN, + "No start tag found in package manager stopped packages"); + return; + } + + int outerDepth = parser.getDepth(); + 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(TAG_PACKAGE)) { + String name = parser.getAttributeValue(null, ATTR_NAME); + PackageSetting ps = mPackages.get(name); + if (ps != null) { + ps.setStopped(true, 0); + if ("1".equals(parser.getAttributeValue(null, ATTR_NOT_LAUNCHED))) { + ps.setNotLaunched(true, 0); + } + } else { + Slog.w(PackageManagerService.TAG, + "No package known for stopped package " + name); + } + XmlUtils.skipCurrentTag(parser); + } else { + Slog.w(PackageManagerService.TAG, "Unknown element under : " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + str.close(); + + } catch (XmlPullParserException e) { + mReadMessages.append("Error reading: " + e.toString()); + PackageManagerService.reportSettingsProblem(Log.ERROR, + "Error reading stopped packages: " + e); + Slog.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages", + e); + + } catch (java.io.IOException e) { + mReadMessages.append("Error reading: " + e.toString()); + PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e); + Slog.wtf(PackageManagerService.TAG, "Error reading package manager stopped packages", + e); + + } + } + + void writeLPr(@NonNull Computer computer, boolean sync) { + //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024); + + final long startTime = SystemClock.uptimeMillis(); + + // Whenever package manager changes something on the system, it writes out whatever it + // changed in the form of a settings object change, and it does so under its internal + // lock --- so if we invalidate the package cache here, we end up invalidating at the + // right time. + invalidatePackageCache(); + + ArrayList writtenSignatures = new ArrayList<>(); + + try (ResilientAtomicFile atomicFile = getSettingsFile()) { + FileOutputStream str = null; + try { + str = atomicFile.startWrite(); + + final TypedXmlSerializer serializer = Xml.resolveSerializer(str); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", + true); + + serializer.startTag(null, "packages"); + + for (int i = 0; i < mVersion.size(); i++) { + final String volumeUuid = mVersion.keyAt(i); + final VersionInfo ver = mVersion.valueAt(i); + + serializer.startTag(null, TAG_VERSION); + XmlUtils.writeStringAttribute(serializer, ATTR_VOLUME_UUID, volumeUuid); + serializer.attributeInt(null, ATTR_SDK_VERSION, ver.sdkVersion); + serializer.attributeInt(null, ATTR_DATABASE_VERSION, ver.databaseVersion); + XmlUtils.writeStringAttribute(serializer, ATTR_BUILD_FINGERPRINT, + ver.buildFingerprint); + XmlUtils.writeStringAttribute(serializer, ATTR_FINGERPRINT, ver.fingerprint); + serializer.endTag(null, TAG_VERSION); + } + + if (mVerifierDeviceIdentity != null) { + serializer.startTag(null, "verifier"); + serializer.attribute(null, "device", mVerifierDeviceIdentity.toString()); + serializer.endTag(null, "verifier"); + } + + serializer.startTag(null, "permission-trees"); + mPermissions.writePermissionTrees(serializer); + serializer.endTag(null, "permission-trees"); + + serializer.startTag(null, "permissions"); + mPermissions.writePermissions(serializer); + serializer.endTag(null, "permissions"); + + for (final PackageSetting pkg : mPackages.values()) { + if (pkg.getPkg() != null && pkg.getPkg().isApex()) { + // Don't persist APEX which doesn't have a valid app id and will fail to + // load + continue; + } + writePackageLPr(serializer, writtenSignatures, pkg); + } + + for (final PackageSetting pkg : mDisabledSysPackages.values()) { + if (pkg.getPkg() != null && pkg.getPkg().isApex()) { + // Don't persist APEX which doesn't have a valid app id and will fail to + // load + continue; + } + writeDisabledSysPackageLPr(serializer, pkg); + } + + for (final SharedUserSetting usr : mSharedUsers.values()) { + serializer.startTag(null, "shared-user"); + serializer.attribute(null, ATTR_NAME, usr.name); + serializer.attributeInt(null, "userId", usr.mAppId); + usr.signatures.writeXml(serializer, "sigs", writtenSignatures); + serializer.endTag(null, "shared-user"); + } + + if (mRenamedPackages.size() > 0) { + for (Map.Entry e : mRenamedPackages.entrySet()) { + serializer.startTag(null, "renamed-package"); + serializer.attribute(null, "new", e.getKey()); + serializer.attribute(null, "old", e.getValue()); + serializer.endTag(null, "renamed-package"); + } + } + + mDomainVerificationManager.writeSettings(computer, serializer, + false /* includeSignatures */, UserHandle.USER_ALL); + + mKeySetManagerService.writeKeySetManagerServiceLPr(serializer); + + serializer.endTag(null, "packages"); + + serializer.endDocument(); + + atomicFile.finishWrite(str); + + writeKernelMappingLPr(); + writePackageListLPr(); + writeAllUsersPackageRestrictionsLPr(sync); + writeAllRuntimePermissionsLPr(); + com.android.internal.logging.EventLogTags.writeCommitSysConfigFile( + "package", SystemClock.uptimeMillis() - startTime); + return; + + } catch (java.io.IOException e) { + Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, " + + "current changes will be lost at reboot", e); + if (str != null) { + atomicFile.failWrite(str); + } + } + } + + //Debug.stopMethodTracing(); + } + + private void writeKernelRemoveUserLPr(int userId) { + if (mKernelMappingFilename == null) return; + + File removeUserIdFile = new File(mKernelMappingFilename, "remove_userid"); + if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + userId + " to " + removeUserIdFile + .getAbsolutePath()); + writeIntToFile(removeUserIdFile, userId); + } + + void writeKernelMappingLPr() { + if (mKernelMappingFilename == null) return; + + final String[] known = mKernelMappingFilename.list(); + final ArraySet knownSet = new ArraySet<>(known.length); + for (String name : known) { + knownSet.add(name); + } + + for (final PackageSetting ps : mPackages.values()) { + // Package is actively claimed + knownSet.remove(ps.getPackageName()); + writeKernelMappingLPr(ps); + } + + // Remove any unclaimed mappings + for (int i = 0; i < knownSet.size(); i++) { + final String name = knownSet.valueAt(i); + if (DEBUG_KERNEL) Slog.d(TAG, "Dropping mapping " + name); + + mKernelMapping.remove(name); + new File(mKernelMappingFilename, name).delete(); + } + } + + void writeKernelMappingLPr(PackageSetting ps) { + if (mKernelMappingFilename == null || ps == null || ps.getPackageName() == null) return; + + writeKernelMappingLPr(ps.getPackageName(), ps.getAppId(), ps.getNotInstalledUserIds()); + } + + void writeKernelMappingLPr(String name, int appId, int[] excludedUserIds) { + KernelPackageState cur = mKernelMapping.get(name); + final boolean firstTime = cur == null; + final boolean userIdsChanged = firstTime + || !Arrays.equals(excludedUserIds, cur.excludedUserIds); + + // Package directory + final File dir = new File(mKernelMappingFilename, name); + + if (firstTime) { + dir.mkdir(); + // Create a new mapping state + cur = new KernelPackageState(); + mKernelMapping.put(name, cur); + } + + // If mapping is incorrect or non-existent, write the appid file + if (cur.appId != appId) { + final File appIdFile = new File(dir, "appid"); + writeIntToFile(appIdFile, appId); + if (DEBUG_KERNEL) Slog.d(TAG, "Mapping " + name + " to " + appId); + } + + if (userIdsChanged) { + // Build the exclusion list -- the ids to add to the exclusion list + for (int i = 0; i < excludedUserIds.length; i++) { + if (cur.excludedUserIds == null || !ArrayUtils.contains(cur.excludedUserIds, + excludedUserIds[i])) { + writeIntToFile(new File(dir, "excluded_userids"), excludedUserIds[i]); + if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + excludedUserIds[i] + " to " + + name + "/excluded_userids"); + } + } + // Build the inclusion list -- the ids to remove from the exclusion list + if (cur.excludedUserIds != null) { + for (int i = 0; i < cur.excludedUserIds.length; i++) { + if (!ArrayUtils.contains(excludedUserIds, cur.excludedUserIds[i])) { + writeIntToFile(new File(dir, "clear_userid"), + cur.excludedUserIds[i]); + if (DEBUG_KERNEL) Slog.d(TAG, "Writing " + cur.excludedUserIds[i] + " to " + + name + "/clear_userid"); + + } + } + } + cur.excludedUserIds = excludedUserIds; + } + } + + private void writeIntToFile(File file, int value) { + try { + FileUtils.bytesToFile(file.getAbsolutePath(), + Integer.toString(value).getBytes(StandardCharsets.US_ASCII)); + } catch (IOException ignored) { + Slog.w(TAG, "Couldn't write " + value + " to " + file.getAbsolutePath()); + } + } + + void writePackageListLPr() { + writePackageListLPr(-1); + } + + void writePackageListLPr(int creatingUserId) { + String filename = mPackageListFilename.getAbsolutePath(); + String ctx = SELinux.fileSelabelLookup(filename); + if (ctx == null) { + Slog.wtf(TAG, "Failed to get SELinux context for " + + mPackageListFilename.getAbsolutePath()); + } + + if (!SELinux.setFSCreateContext(ctx)) { + Slog.wtf(TAG, "Failed to set packages.list SELinux context"); + } + try { + writePackageListLPrInternal(creatingUserId); + } finally { + SELinux.setFSCreateContext(null); + } + } + + private void writePackageListLPrInternal(int creatingUserId) { + // Only derive GIDs for active users (not dying) + final List users = getActiveUsers(UserManagerService.getInstance(), true); + int[] userIds = new int[users.size()]; + for (int i = 0; i < userIds.length; i++) { + userIds[i] = users.get(i).id; + } + if (creatingUserId != -1) { + userIds = ArrayUtils.appendInt(userIds, creatingUserId); + } + + // Write package list file now, use a JournaledFile. + File tempFile = new File(mPackageListFilename.getAbsolutePath() + ".tmp"); + JournaledFile journal = new JournaledFile(mPackageListFilename, tempFile); + + final File writeTarget = journal.chooseForWrite(); + FileOutputStream fstr; + BufferedWriter writer = null; + try { + fstr = new FileOutputStream(writeTarget); + writer = new BufferedWriter(new OutputStreamWriter(fstr, Charset.defaultCharset())); + FileUtils.setPermissions(fstr.getFD(), 0640, SYSTEM_UID, PACKAGE_INFO_GID); + + StringBuilder sb = new StringBuilder(); + for (final PackageSetting ps : mPackages.values()) { + if (ps.getPkg() == null) { + if (!"android".equals(ps.getPackageName())) { + Slog.w(TAG, "Skipping " + ps + " due to missing metadata"); + } + continue; + } + if (ps.getPkg().isApex()) { + // Don't persist APEX which doesn't have a valid app id and will cause parsing + // error in libpackagelistparser + continue; + } + + // TODO(b/135203078): This doesn't handle multiple users + final File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.USER_SYSTEM); + final String dataPath = dataDir == null ? "null" : dataDir.getAbsolutePath(); + + final boolean isDebug = ps.getPkg().isDebuggable(); + final IntArray gids = new IntArray(); + for (final int userId : userIds) { + gids.addAll(mPermissionDataProvider.getGidsForUid(UserHandle.getUid(userId, + ps.getAppId()))); + } + + // Avoid any application that has a space in its path. + if (dataPath.indexOf(' ') >= 0) + continue; + + // we store on each line the following information for now: + // + // pkgName - package name + // userId - application-specific user id + // debugFlag - 0 or 1 if the package is debuggable. + // dataPath - path to package's data path + // seinfo - seinfo label for the app (assigned at install time) + // gids - supplementary gids this app launches with + // profileableFromShellFlag - 0 or 1 if the package is profileable from shell. + // longVersionCode - integer version of the package. + // profileable - 0 or 1 if the package is profileable by the platform. + // packageInstaller - the package that installed this app, or @system, @product or + // @null. + // + // NOTE: We prefer not to expose all ApplicationInfo flags for now. + // + // DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS + // FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES: + // system/core/libpackagelistparser + // + sb.setLength(0); + sb.append(ps.getPkg().getPackageName()); + sb.append(" "); + sb.append(ps.getPkg().getUid()); + sb.append(isDebug ? " 1 " : " 0 "); + sb.append(dataPath); + sb.append(" "); + sb.append(ps.getSeInfo()); + sb.append(" "); + final int gidsSize = gids.size(); + if (gids.size() > 0) { + sb.append(gids.get(0)); + for (int i = 1; i < gidsSize; i++) { + sb.append(","); + sb.append(gids.get(i)); + } + } else { + sb.append("none"); + } + sb.append(" "); + sb.append(ps.getPkg().isProfileableByShell() ? "1" : "0"); + sb.append(" "); + sb.append(ps.getPkg().getLongVersionCode()); + sb.append(" "); + sb.append(ps.getPkg().isProfileable() ? "1" : "0"); + sb.append(" "); + if (ps.isSystem()) { + sb.append("@system"); + } else if (ps.isProduct()) { + sb.append("@product"); + } else if (ps.getInstallSource().mInstallerPackageName != null + && !ps.getInstallSource().mInstallerPackageName.isEmpty()) { + sb.append(ps.getInstallSource().mInstallerPackageName); + } else { + sb.append("@null"); + } + sb.append("\n"); + writer.append(sb); + } + writer.flush(); + FileUtils.sync(fstr); + writer.close(); + journal.commit(); + } catch (Exception e) { + Slog.wtf(TAG, "Failed to write packages.list", e); + IoUtils.closeQuietly(writer); + journal.rollback(); + } + } + + void writeDisabledSysPackageLPr(TypedXmlSerializer serializer, final PackageSetting pkg) + throws java.io.IOException { + serializer.startTag(null, "updated-package"); + serializer.attribute(null, ATTR_NAME, pkg.getPackageName()); + if (pkg.getRealName() != null) { + serializer.attribute(null, "realName", pkg.getRealName()); + } + serializer.attribute(null, "codePath", pkg.getPathString()); + serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime()); + serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime()); + serializer.attributeLong(null, "version", pkg.getVersionCode()); + serializer.attributeInt(null, "targetSdkVersion", pkg.getTargetSdkVersion()); + if (pkg.getRestrictUpdateHash() != null) { + serializer.attributeBytesBase64(null, "restrictUpdateHash", + pkg.getRestrictUpdateHash()); + } + serializer.attributeBoolean(null, "scannedAsStoppedSystemApp", + pkg.isScannedAsStoppedSystemApp()); + if (pkg.getLegacyNativeLibraryPath() != null) { + serializer.attribute(null, "nativeLibraryPath", pkg.getLegacyNativeLibraryPath()); + } + if (pkg.getPrimaryCpuAbiLegacy() != null) { + serializer.attribute(null, "primaryCpuAbi", pkg.getPrimaryCpuAbiLegacy()); + } + if (pkg.getSecondaryCpuAbiLegacy() != null) { + serializer.attribute(null, "secondaryCpuAbi", pkg.getSecondaryCpuAbiLegacy()); + } + if (pkg.getCpuAbiOverride() != null) { + serializer.attribute(null, "cpuAbiOverride", pkg.getCpuAbiOverride()); + } + + if (!pkg.hasSharedUser()) { + serializer.attributeInt(null, "userId", pkg.getAppId()); + } else { + serializer.attributeInt(null, "sharedUserId", pkg.getAppId()); + } + serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress()); + serializer.attributeLongHex(null, "loadingCompletedTime", + pkg.getLoadingCompletedTime()); + + if (pkg.getAppMetadataFilePath() != null) { + serializer.attribute(null, "appMetadataFilePath", + pkg.getAppMetadataFilePath()); + } + + serializer.attributeInt(null, "appMetadataSource", + pkg.getAppMetadataSource()); + + writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(), + pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesOptional()); + + + writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(), + pkg.getUsesStaticLibrariesVersions()); + + serializer.endTag(null, "updated-package"); + } + + void writePackageLPr(TypedXmlSerializer serializer, ArrayList writtenSignatures, + PackageSetting pkg) + throws java.io.IOException { + serializer.startTag(null, "package"); + serializer.attribute(null, ATTR_NAME, pkg.getPackageName()); + if (pkg.getRealName() != null) { + serializer.attribute(null, "realName", pkg.getRealName()); + } + serializer.attribute(null, "codePath", pkg.getPathString()); + + if (pkg.getLegacyNativeLibraryPath() != null) { + serializer.attribute(null, "nativeLibraryPath", pkg.getLegacyNativeLibraryPath()); + } + if (pkg.getPrimaryCpuAbiLegacy() != null) { + serializer.attribute(null, "primaryCpuAbi", pkg.getPrimaryCpuAbiLegacy()); + } + if (pkg.getSecondaryCpuAbiLegacy() != null) { + serializer.attribute(null, "secondaryCpuAbi", pkg.getSecondaryCpuAbiLegacy()); + } + if (pkg.getCpuAbiOverride() != null) { + serializer.attribute(null, "cpuAbiOverride", pkg.getCpuAbiOverride()); + } + + serializer.attributeInt(null, "publicFlags", pkg.getFlags()); + serializer.attributeInt(null, "privateFlags", pkg.getPrivateFlags()); + serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime()); + serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime()); + serializer.attributeLong(null, "version", pkg.getVersionCode()); + serializer.attributeInt(null, "targetSdkVersion", pkg.getTargetSdkVersion()); + if (pkg.getRestrictUpdateHash() != null) { + serializer.attributeBytesBase64(null, "restrictUpdateHash", + pkg.getRestrictUpdateHash()); + } + serializer.attributeBoolean(null, "scannedAsStoppedSystemApp", + pkg.isScannedAsStoppedSystemApp()); + if (!pkg.hasSharedUser()) { + serializer.attributeInt(null, "userId", pkg.getAppId()); + + serializer.attributeBoolean(null, "isSdkLibrary", + pkg.getAndroidPackage() != null && pkg.getAndroidPackage().isSdkLibrary()); + } else { + serializer.attributeInt(null, "sharedUserId", pkg.getAppId()); + } + InstallSource installSource = pkg.getInstallSource(); + if (installSource.mInstallerPackageName != null) { + serializer.attribute(null, "installer", installSource.mInstallerPackageName); + } + if (installSource.mInstallerPackageUid != INVALID_UID) { + serializer.attributeInt(null, "installerUid", installSource.mInstallerPackageUid); + } + if (installSource.mUpdateOwnerPackageName != null) { + serializer.attribute(null, "updateOwner", installSource.mUpdateOwnerPackageName); + } + if (installSource.mInstallerAttributionTag != null) { + serializer.attribute(null, "installerAttributionTag", + installSource.mInstallerAttributionTag); + } + serializer.attributeInt(null, "packageSource", + installSource.mPackageSource); + if (installSource.mIsOrphaned) { + serializer.attributeBoolean(null, "isOrphaned", true); + } + if (installSource.mInitiatingPackageName != null) { + serializer.attribute(null, "installInitiator", installSource.mInitiatingPackageName); + } + if (installSource.mIsInitiatingPackageUninstalled) { + serializer.attributeBoolean(null, "installInitiatorUninstalled", true); + } + if (installSource.mOriginatingPackageName != null) { + serializer.attribute(null, "installOriginator", installSource.mOriginatingPackageName); + } + if (pkg.getVolumeUuid() != null) { + serializer.attribute(null, "volumeUuid", pkg.getVolumeUuid()); + } + if (pkg.getCategoryOverride() != ApplicationInfo.CATEGORY_UNDEFINED) { + serializer.attributeInt(null, "categoryHint", pkg.getCategoryOverride()); + } + if (pkg.isUpdateAvailable()) { + serializer.attributeBoolean(null, "updateAvailable", true); + } + if (pkg.isForceQueryableOverride()) { + serializer.attributeBoolean(null, "forceQueryable", true); + } + if (pkg.isLoading()) { + serializer.attributeBoolean(null, "isLoading", true); + } + serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress()); + serializer.attributeLongHex(null, "loadingCompletedTime", pkg.getLoadingCompletedTime()); + + serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString()); + + if (pkg.getAppMetadataFilePath() != null) { + serializer.attribute(null, "appMetadataFilePath", pkg.getAppMetadataFilePath()); + } + serializer.attributeInt(null, "appMetadataSource", + pkg.getAppMetadataSource()); + + + writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(), + pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesOptional()); + + writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(), + pkg.getUsesStaticLibrariesVersions()); + + pkg.getSignatures().writeXml(serializer, "sigs", writtenSignatures); + + if (installSource.mInitiatingPackageSignatures != null) { + installSource.mInitiatingPackageSignatures.writeXml( + serializer, "install-initiator-sigs", writtenSignatures); + } + + writeSigningKeySetLPr(serializer, pkg.getKeySetData()); + writeUpgradeKeySetsLPr(serializer, pkg.getKeySetData()); + writeKeySetAliasesLPr(serializer, pkg.getKeySetData()); + writeMimeGroupLPr(serializer, pkg.getMimeGroups()); + + serializer.endTag(null, "package"); + } + + void writeSigningKeySetLPr(TypedXmlSerializer serializer, + PackageKeySetData data) throws IOException { + serializer.startTag(null, "proper-signing-keyset"); + serializer.attributeLong(null, "identifier", data.getProperSigningKeySet()); + serializer.endTag(null, "proper-signing-keyset"); + } + + void writeUpgradeKeySetsLPr(TypedXmlSerializer serializer, + PackageKeySetData data) throws IOException { + if (data.isUsingUpgradeKeySets()) { + for (long id : data.getUpgradeKeySets()) { + serializer.startTag(null, "upgrade-keyset"); + serializer.attributeLong(null, "identifier", id); + serializer.endTag(null, "upgrade-keyset"); + } + } + } + + void writeKeySetAliasesLPr(TypedXmlSerializer serializer, + PackageKeySetData data) throws IOException { + for (Map.Entry e: data.getAliases().entrySet()) { + serializer.startTag(null, "defined-keyset"); + serializer.attribute(null, "alias", e.getKey()); + serializer.attributeLong(null, "identifier", e.getValue()); + serializer.endTag(null, "defined-keyset"); + } + } + + boolean readSettingsLPw(@NonNull Computer computer, @NonNull List users, + ArrayMap originalFirstInstallTimes) { + mPendingPackages.clear(); + mInstallerPackages.clear(); + originalFirstInstallTimes.clear(); + + ArrayMap keySetRefs = new ArrayMap<>(); + ArrayList readSignatures = new ArrayList<>(); + + try (ResilientAtomicFile atomicFile = getSettingsFile()) { + FileInputStream str = null; + try { + str = atomicFile.openRead(); + if (str == null) { + // Not necessary, but will avoid wtf-s in the "finally" section. + findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent(); + findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent(); + return false; + } + final TypedXmlPullParser parser = Xml.resolvePullParser(str); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + // nothing + } + + if (type != XmlPullParser.START_TAG) { + mReadMessages.append("No start tag found in settings file\n"); + PackageManagerService.reportSettingsProblem(Log.WARN, + "No start tag found in package manager settings"); + Slog.wtf(PackageManagerService.TAG, + "No start tag found in package manager settings"); + return false; + } + + int outerDepth = parser.getDepth(); + 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")) { + readPackageLPw(parser, readSignatures, keySetRefs, users, + originalFirstInstallTimes); + } else if (tagName.equals("permissions")) { + mPermissions.readPermissions(parser); + } else if (tagName.equals("permission-trees")) { + mPermissions.readPermissionTrees(parser); + } else if (tagName.equals("shared-user")) { + readSharedUserLPw(parser, readSignatures, users); + } else if (tagName.equals("preferred-packages")) { + // no longer used. + } else if (tagName.equals("preferred-activities")) { + // Upgrading from old single-user implementation; + // these are the preferred activities for user 0. + readPreferredActivitiesLPw(parser, 0); + } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) { + // TODO: check whether this is okay! as it is very + // similar to how preferred-activities are treated + readPersistentPreferredActivitiesLPw(parser, 0); + } else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) { + // TODO: check whether this is okay! as it is very + // similar to how preferred-activities are treated + readCrossProfileIntentFiltersLPw(parser, 0); + } else if (tagName.equals(TAG_DEFAULT_BROWSER)) { + readDefaultAppsLPw(parser, 0); + } else if (tagName.equals("updated-package")) { + readDisabledSysPackageLPw(parser, users); + } else if (tagName.equals("renamed-package")) { + String nname = parser.getAttributeValue(null, "new"); + String oname = parser.getAttributeValue(null, "old"); + if (nname != null && oname != null) { + mRenamedPackages.put(nname, oname); + } + } else if (tagName.equals("last-platform-version")) { + // Upgrade from older XML schema + final VersionInfo internal = findOrCreateVersion( + StorageManager.UUID_PRIVATE_INTERNAL); + final VersionInfo external = findOrCreateVersion( + StorageManager.UUID_PRIMARY_PHYSICAL); + + internal.sdkVersion = parser.getAttributeInt(null, "internal", 0); + external.sdkVersion = parser.getAttributeInt(null, "external", 0); + internal.buildFingerprint = external.buildFingerprint = + XmlUtils.readStringAttribute(parser, "buildFingerprint"); + internal.fingerprint = external.fingerprint = + XmlUtils.readStringAttribute(parser, "fingerprint"); + + } else if (tagName.equals("database-version")) { + // Upgrade from older XML schema + final VersionInfo internal = findOrCreateVersion( + StorageManager.UUID_PRIVATE_INTERNAL); + final VersionInfo external = findOrCreateVersion( + StorageManager.UUID_PRIMARY_PHYSICAL); + + internal.databaseVersion = parser.getAttributeInt(null, "internal", 0); + external.databaseVersion = parser.getAttributeInt(null, "external", 0); + + } else if (tagName.equals("verifier")) { + final String deviceIdentity = parser.getAttributeValue(null, "device"); + try { + mVerifierDeviceIdentity = VerifierDeviceIdentity.parse(deviceIdentity); + } catch (IllegalArgumentException e) { + Slog.w(PackageManagerService.TAG, "Discard invalid verifier device id: " + + e.getMessage()); + } + } else if (TAG_READ_EXTERNAL_STORAGE.equals(tagName)) { + // No longer used. + } else if (tagName.equals("keyset-settings")) { + mKeySetManagerService.readKeySetsLPw(parser, keySetRefs); + } else if (TAG_VERSION.equals(tagName)) { + final String volumeUuid = XmlUtils.readStringAttribute(parser, + ATTR_VOLUME_UUID); + final VersionInfo ver = findOrCreateVersion(volumeUuid); + ver.sdkVersion = parser.getAttributeInt(null, ATTR_SDK_VERSION); + ver.databaseVersion = parser.getAttributeInt(null, ATTR_DATABASE_VERSION); + ver.buildFingerprint = XmlUtils.readStringAttribute(parser, + ATTR_BUILD_FINGERPRINT); + ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT); + } else if (tagName.equals( + DomainVerificationPersistence.TAG_DOMAIN_VERIFICATIONS)) { + mDomainVerificationManager.readSettings(computer, parser); + } else if (tagName.equals( + DomainVerificationLegacySettings.TAG_DOMAIN_VERIFICATIONS_LEGACY)) { + mDomainVerificationManager.readLegacySettings(parser); + } else { + Slog.w(PackageManagerService.TAG, "Unknown element under : " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + str.close(); + } catch (IOException | XmlPullParserException | ArrayIndexOutOfBoundsException e) { + // Remove corrupted file and retry. + atomicFile.failRead(str, e); + + // Ignore the result to not mark this as a "first boot". + readSettingsLPw(computer, users, originalFirstInstallTimes); + } + } + + return true; + } + + /** + * @return false if settings file is missing (i.e. during first boot), true otherwise + */ + boolean readLPw(@NonNull Computer computer, @NonNull List users) { + // If any user state doesn't have a first install time, e.g., after an OTA, + // use the pre OTA firstInstallTime timestamp. This is because we migrated from per package + // firstInstallTime to per user-state. Without this, OTA can cause this info to be lost. + final ArrayMap originalFirstInstallTimes = new ArrayMap<>(); + + try { + if (!readSettingsLPw(computer, users, originalFirstInstallTimes)) { + return false; + } + } finally { + if (!mVersion.containsKey(StorageManager.UUID_PRIVATE_INTERNAL)) { + Slog.wtf(PackageManagerService.TAG, + "No internal VersionInfo found in settings, using current."); + findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent(); + } + if (!mVersion.containsKey(StorageManager.UUID_PRIMARY_PHYSICAL)) { + Slog.wtf(PackageManagerService.TAG, + "No external VersionInfo found in settings, using current."); + findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent(); + } + } + + final int N = mPendingPackages.size(); + + for (int i = 0; i < N; i++) { + final PackageSetting p = mPendingPackages.get(i); + final int sharedUserAppId = p.getSharedUserAppId(); + 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) { + String msg = "Bad package setting: package " + p.getPackageName() + + " has shared uid " + sharedUserAppId + " that is not a shared uid\n"; + mReadMessages.append(msg); + PackageManagerService.reportSettingsProblem(Log.ERROR, msg); + } else { + String msg = "Bad package setting: package " + p.getPackageName() + + " has shared uid " + sharedUserAppId + " that is not defined\n"; + mReadMessages.append(msg); + PackageManagerService.reportSettingsProblem(Log.ERROR, msg); + } + } + mPendingPackages.clear(); + + if (mBackupStoppedPackagesFilename.exists() + || mStoppedPackagesFilename.exists()) { + // Read old file + readStoppedLPw(); + mBackupStoppedPackagesFilename.delete(); + mStoppedPackagesFilename.delete(); + // Migrate to new file format + writePackageRestrictionsLPr(UserHandle.USER_SYSTEM, /*sync=*/true); + } else { + for (UserInfo user : users) { + readPackageRestrictionsLPr(user.id, originalFirstInstallTimes); + } + } + + for (UserInfo user : users) { + mRuntimePermissionsPersistence.readStateForUserSync(user.id, getInternalVersion(), + mPackages, mSharedUsers, getUserRuntimePermissionsFile(user.id)); + } + + /* + * Make sure all the updated system packages have their shared users + * associated with them. + */ + for (PackageSetting disabledPs : mDisabledSysPackages.values()) { + final Object id = getSettingLPr(disabledPs.getAppId()); + if (id instanceof SharedUserSetting) { + SharedUserSetting sharedUserSetting = (SharedUserSetting) id; + sharedUserSetting.mDisabledPackages.add(disabledPs); + disabledPs.setSharedUserAppId(sharedUserSetting.mAppId); + } + } + + mReadMessages.append("Read completed successfully: ").append(mPackages.size()) + .append(" packages, ").append(mSharedUsers.size()).append(" shared uids\n"); + + writeKernelMappingLPr(); + + return true; + } + + void readPermissionStateForUserSyncLPr(@UserIdInt int userId) { + mRuntimePermissionsPersistence.readStateForUserSync(userId, getInternalVersion(), + mPackages, mSharedUsers, getUserRuntimePermissionsFile(userId)); + } + + RuntimePermissionsState getLegacyPermissionsState(@UserIdInt int userId) { + return mRuntimePermissionsPersistence.getLegacyPermissionsState( + userId, mPackages, mSharedUsers); + } + + void applyDefaultPreferredAppsLPw(int userId) { + // First pull data from any pre-installed apps. + final PackageManagerInternal pmInternal = + LocalServices.getService(PackageManagerInternal.class); + for (PackageSetting ps : mPackages.values()) { + if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0 && ps.getPkg() != null + && !ps.getPkg().getPreferredActivityFilters().isEmpty()) { + List> intents + = ps.getPkg().getPreferredActivityFilters(); + for (int i=0; i pair = intents.get(i); + applyDefaultPreferredActivityLPw(pmInternal, + pair.second.getIntentFilter(), + new ComponentName(ps.getPackageName(), pair.first), userId); + } + } + } + + // Read preferred apps from .../etc/preferred-apps directories. + int size = PackageManagerService.SYSTEM_PARTITIONS.size(); + for (int index = 0; index < size; index++) { + ScanPartition partition = PackageManagerService.SYSTEM_PARTITIONS.get(index); + + File preferredDir = new File(partition.getFolder(), "etc/preferred-apps"); + if (!preferredDir.exists() || !preferredDir.isDirectory()) { + continue; + } + + if (!preferredDir.canRead()) { + Slog.w(TAG, "Directory " + preferredDir + " cannot be read"); + continue; + } + + // Iterate over the files in the directory and scan .xml files + File[] files = preferredDir.listFiles(); + if (ArrayUtils.isEmpty(files)) { + continue; + } + + for (File f : files) { + if (!f.getPath().endsWith(".xml")) { + Slog.i(TAG, "Non-xml file " + f + " in " + preferredDir + + " directory, ignoring"); + continue; + } + if (!f.canRead()) { + Slog.w(TAG, "Preferred apps file " + f + " cannot be read"); + continue; + } + if (PackageManagerService.DEBUG_PREFERRED) { + Log.d(TAG, "Reading default preferred " + f); + } + + try (InputStream str = new FileInputStream(f)) { + final TypedXmlPullParser parser = Xml.resolvePullParser(str); + + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + Slog.w(TAG, "Preferred apps file " + f + " does not have start tag"); + continue; + } + if (!"preferred-activities".equals(parser.getName())) { + Slog.w(TAG, "Preferred apps file " + f + + " does not start with 'preferred-activities'"); + continue; + } + readDefaultPreferredActivitiesLPw(parser, userId); + } catch (XmlPullParserException e) { + Slog.w(TAG, "Error reading apps file " + f, e); + } catch (IOException e) { + Slog.w(TAG, "Error reading apps file " + f, e); + } + } + } + } + + static void removeFilters(@NonNull PreferredIntentResolver pir, + @NonNull WatchedIntentFilter filter, @NonNull List existing) { + if (PackageManagerService.DEBUG_PREFERRED) { + Slog.i(TAG, existing.size() + " preferred matches for:"); + filter.dump(new LogPrinter(Log.INFO, TAG), " "); + } + for (int i = existing.size() - 1; i >= 0; --i) { + final PreferredActivity pa = existing.get(i); + if (PackageManagerService.DEBUG_PREFERRED) { + Slog.i(TAG, "Removing preferred activity " + pa.mPref.mComponent + ":"); + pa.dump(new LogPrinter(Log.INFO, TAG), " "); + } + pir.removeFilter(pa); + } + } + + private void applyDefaultPreferredActivityLPw(PackageManagerInternal pmInternal, + IntentFilter tmpPa, ComponentName cn, int userId) { + // The initial preferences only specify the target activity + // component and intent-filter, not the set of matches. So we + // now need to query for the matches to build the correct + // preferred activity entry. + if (PackageManagerService.DEBUG_PREFERRED) { + Log.d(TAG, "Processing preferred:"); + tmpPa.dump(new LogPrinter(Log.DEBUG, TAG), " "); + } + Intent intent = new Intent(); + int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + intent.setAction(tmpPa.getAction(0)); + for (int i=0; i ri = + pmInternal.queryIntentActivities( + intent, intent.getType(), flags, Binder.getCallingUid(), userId); + if (PackageManagerService.DEBUG_PREFERRED) { + Log.d(TAG, "Queried " + intent + " results: " + ri); + } + int systemMatch = 0; + int thirdPartyMatch = 0; + final int numMatches = (ri == null ? 0 : ri.size()); + if (numMatches < 1) { + Slog.w(TAG, "No potential matches found for " + intent + + " while setting preferred " + cn.flattenToShortString()); + return; + } + boolean haveAct = false; + ComponentName haveNonSys = null; + ComponentName[] set = new ComponentName[ri.size()]; + for (int i = 0; i < numMatches; i++) { + final ActivityInfo ai = ri.get(i).activityInfo; + set[i] = new ComponentName(ai.packageName, ai.name); + if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + if (ri.get(i).match >= thirdPartyMatch) { + // Keep track of the best match we find of all third + // party apps, for use later to determine if we actually + // want to set a preferred app for this intent. + if (PackageManagerService.DEBUG_PREFERRED) { + Log.d(TAG, "Result " + ai.packageName + "/" + ai.name + ": non-system!"); + } + haveNonSys = set[i]; + break; + } + } else if (cn.getPackageName().equals(ai.packageName) + && cn.getClassName().equals(ai.name)) { + if (PackageManagerService.DEBUG_PREFERRED) { + Log.d(TAG, "Result " + ai.packageName + "/" + ai.name + ": default!"); + } + haveAct = true; + systemMatch = ri.get(i).match; + } else { + if (PackageManagerService.DEBUG_PREFERRED) { + Log.d(TAG, "Result " + ai.packageName + "/" + ai.name + ": skipped"); + } + } + } + if (haveNonSys != null && thirdPartyMatch < systemMatch) { + // If we have a matching third party app, but its match is not as + // good as the built-in system app, then we don't want to actually + // consider it a match because presumably the built-in app is still + // the thing we want users to see by default. + haveNonSys = null; + } + if (haveAct && haveNonSys == null) { + WatchedIntentFilter filter = new WatchedIntentFilter(); + if (intent.getAction() != null) { + filter.addAction(intent.getAction()); + } + if (intent.getCategories() != null) { + for (String cat : intent.getCategories()) { + filter.addCategory(cat); + } + } + if ((flags & MATCH_DEFAULT_ONLY) != 0) { + filter.addCategory(Intent.CATEGORY_DEFAULT); + } + if (scheme != null) { + filter.addDataScheme(scheme); + } + if (ssp != null) { + filter.addDataSchemeSpecificPart(ssp.getPath(), ssp.getType()); + } + if (auth != null) { + filter.addDataAuthority(auth); + } + if (path != null) { + filter.addDataPath(path); + } + if (intent.getType() != null) { + try { + filter.addDataType(intent.getType()); + } catch (IntentFilter.MalformedMimeTypeException ex) { + Slog.w(TAG, "Malformed mimetype " + intent.getType() + " for " + cn); + } + } + final PreferredIntentResolver pir = editPreferredActivitiesLPw(userId); + final List existing = pir.findFilters(filter); + if (existing != null) { + removeFilters(pir, filter, existing); + } + PreferredActivity pa = new PreferredActivity(filter, systemMatch, set, cn, true); + pir.addFilter(null, pa); + } else if (haveNonSys == null) { + StringBuilder sb = new StringBuilder(); + sb.append("No component "); + sb.append(cn.flattenToShortString()); + sb.append(" found setting preferred "); + sb.append(intent); + sb.append("; possible matches are "); + for (int i = 0; i < set.length; i++) { + if (i > 0) sb.append(", "); + sb.append(set[i].flattenToShortString()); + } + Slog.w(TAG, sb.toString()); + } else { + Slog.i(TAG, "Not setting preferred " + intent + "; found third party match " + + haveNonSys.flattenToShortString()); + } + } + + private void readDefaultPreferredActivitiesLPw(TypedXmlPullParser parser, int userId) + throws XmlPullParserException, IOException { + final PackageManagerInternal pmInternal = + LocalServices.getService(PackageManagerInternal.class); + int outerDepth = parser.getDepth(); + int type; + 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(TAG_ITEM)) { + PreferredActivity tmpPa = new PreferredActivity(parser); + if (tmpPa.mPref.getParseError() == null) { + applyDefaultPreferredActivityLPw( + pmInternal, tmpPa.getIntentFilter(), tmpPa.mPref.mComponent, userId); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: " + + tmpPa.mPref.getParseError() + " at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under : " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } + + private void readDisabledSysPackageLPw(TypedXmlPullParser parser, List users) + throws XmlPullParserException, IOException { + String name = parser.getAttributeValue(null, ATTR_NAME); + String realName = parser.getAttributeValue(null, "realName"); + String codePathStr = parser.getAttributeValue(null, "codePath"); + + String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi"); + String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); + + String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi"); + String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi"); + String cpuAbiOverrideStr = parser.getAttributeValue(null, "cpuAbiOverride"); + + if (primaryCpuAbiStr == null && legacyCpuAbiStr != null) { + primaryCpuAbiStr = legacyCpuAbiStr; + } + + long versionCode = parser.getAttributeLong(null, "version", 0); + int targetSdkVersion = parser.getAttributeInt(null, "targetSdkVersion", 0); + byte[] restrictUpdateHash = parser.getAttributeBytesBase64(null, "restrictUpdateHash", + null); + boolean isScannedAsStoppedSystemApp = parser.getAttributeBoolean(null, + "scannedAsStoppedSystemApp", false); + + int pkgFlags = 0; + int pkgPrivateFlags = 0; + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + if (codePathStr.contains("/priv-app/")) { + pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; + } + + // When reading a disabled setting, use a disabled domainSetId, which makes it easier to + // debug invalid entries. The actual logic for migrating to a new ID is done in other + // methods that use DomainVerificationManagerInternal#generateNewId + UUID domainSetId = DomainVerificationManagerInternal.DISABLED_ID; + PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr), pkgFlags, + pkgPrivateFlags, domainSetId) + .setLegacyNativeLibraryPath(legacyNativeLibraryPathStr) + .setPrimaryCpuAbi(primaryCpuAbiStr) + .setSecondaryCpuAbi(secondaryCpuAbiStr) + .setCpuAbiOverride(cpuAbiOverrideStr) + .setLongVersionCode(versionCode) + .setTargetSdkVersion(targetSdkVersion) + .setRestrictUpdateHash(restrictUpdateHash) + .setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp); + long timeStamp = parser.getAttributeLongHex(null, "ft", 0); + if (timeStamp == 0) { + timeStamp = parser.getAttributeLong(null, "ts", 0); + } + ps.setLastModifiedTime(timeStamp); + ps.setLastUpdateTime(parser.getAttributeLongHex(null, "ut", 0)); + ps.setAppId(parseAppId(parser)); + if (ps.getAppId() <= 0) { + final int sharedUserAppId = parseSharedUserAppId(parser); + ps.setAppId(sharedUserAppId); + ps.setSharedUserAppId(sharedUserAppId); + } + + ps.setAppMetadataFilePath(parser.getAttributeValue(null, "appMetadataFilePath")); + ps.setAppMetadataSource(parser.getAttributeInt(null, + "appMetadataSource", PackageManager.APP_METADATA_SOURCE_UNKNOWN)); + + int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (parser.getName().equals(TAG_PERMISSIONS)) { + final LegacyPermissionState legacyState; + if (ps.hasSharedUser()) { + final SettingBase sharedUserSettings = getSettingLPr( + ps.getSharedUserAppId()); + legacyState = sharedUserSettings != null + ? sharedUserSettings.getLegacyPermissionState() : null; + } else { + legacyState = ps.getLegacyPermissionState(); + } + if (legacyState != null) { + readInstallPermissionsLPr(parser, legacyState, users); + } + } else if (parser.getName().equals(TAG_USES_STATIC_LIB)) { + readUsesStaticLibLPw(parser, ps); + } else if (parser.getName().equals(TAG_USES_SDK_LIB)) { + readUsesSdkLibLPw(parser, ps); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under : " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + mDisabledSysPackages.put(name, ps); + } + + private static final int PRE_M_APP_INFO_FLAG_HIDDEN = 1 << 27; + private static final int PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE = 1 << 28; + private static final int PRE_M_APP_INFO_FLAG_PRIVILEGED = 1 << 30; + + private void readPackageLPw(TypedXmlPullParser parser, ArrayList readSignatures, + ArrayMap keySetRefs, List users, + ArrayMap originalFirstInstallTimes) + throws XmlPullParserException, IOException { + String name = null; + String realName = null; + int appId = 0; + int sharedUserAppId = 0; + String codePathStr = null; + String legacyCpuAbiString = null; + String legacyNativeLibraryPathStr = null; + String primaryCpuAbiString = null; + String secondaryCpuAbiString = null; + String cpuAbiOverrideString = null; + String systemStr = null; + String installerPackageName = null; + int installerPackageUid = INVALID_UID; + String updateOwnerPackageName = null; + String installerAttributionTag = null; + int packageSource = PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED; + boolean isOrphaned = false; + String installOriginatingPackageName = null; + String installInitiatingPackageName = null; + boolean installInitiatorUninstalled = false; + String volumeUuid = null; + boolean updateAvailable = false; + int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; + int pkgFlags = 0; + int pkgPrivateFlags = 0; + long timeStamp = 0; + long firstInstallTime = 0; + long lastUpdateTime = 0; + PackageSetting packageSetting = null; + long versionCode = 0; + boolean installedForceQueryable = false; + float loadingProgress = 0; + long loadingCompletedTime = 0; + UUID domainSetId; + String appMetadataFilePath = null; + int appMetadataSource = PackageManager.APP_METADATA_SOURCE_UNKNOWN; + int targetSdkVersion = 0; + byte[] restrictUpdateHash = null; + boolean isScannedAsStoppedSystemApp = false; + boolean isSdkLibrary = false; + try { + name = parser.getAttributeValue(null, ATTR_NAME); + realName = parser.getAttributeValue(null, "realName"); + appId = parseAppId(parser); + isSdkLibrary = parser.getAttributeBoolean(null, "isSdkLibrary", false); + sharedUserAppId = parseSharedUserAppId(parser); + codePathStr = parser.getAttributeValue(null, "codePath"); + + legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi"); + + legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath"); + primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi"); + secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi"); + cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride"); + updateAvailable = parser.getAttributeBoolean(null, "updateAvailable", false); + installedForceQueryable = parser.getAttributeBoolean(null, "forceQueryable", false); + loadingProgress = parser.getAttributeFloat(null, "loadingProgress", 0); + loadingCompletedTime = parser.getAttributeLongHex(null, "loadingCompletedTime", 0); + + if (primaryCpuAbiString == null && legacyCpuAbiString != null) { + primaryCpuAbiString = legacyCpuAbiString; + } + + versionCode = parser.getAttributeLong(null, "version", 0); + targetSdkVersion = parser.getAttributeInt(null, "targetSdkVersion", 0); + restrictUpdateHash = parser.getAttributeBytesBase64(null, "restrictUpdateHash", null); + installerPackageName = parser.getAttributeValue(null, "installer"); + installerPackageUid = parser.getAttributeInt(null, "installerUid", INVALID_UID); + updateOwnerPackageName = parser.getAttributeValue(null, "updateOwner"); + installerAttributionTag = parser.getAttributeValue(null, "installerAttributionTag"); + packageSource = parser.getAttributeInt(null, "packageSource", + PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED); + isOrphaned = parser.getAttributeBoolean(null, "isOrphaned", false); + installInitiatingPackageName = parser.getAttributeValue(null, "installInitiator"); + installOriginatingPackageName = parser.getAttributeValue(null, "installOriginator"); + installInitiatorUninstalled = parser.getAttributeBoolean(null, + "installInitiatorUninstalled", false); + volumeUuid = parser.getAttributeValue(null, "volumeUuid"); + categoryHint = parser.getAttributeInt(null, "categoryHint", + ApplicationInfo.CATEGORY_UNDEFINED); + appMetadataFilePath = parser.getAttributeValue(null, "appMetadataFilePath"); + appMetadataSource = parser.getAttributeInt(null, "appMetadataSource", + PackageManager.APP_METADATA_SOURCE_UNKNOWN); + + isScannedAsStoppedSystemApp = parser.getAttributeBoolean(null, + "scannedAsStoppedSystemApp", false); + + String domainSetIdString = parser.getAttributeValue(null, "domainSetId"); + + if (TextUtils.isEmpty(domainSetIdString)) { + // If empty, assume restoring from previous platform version and generate an ID + domainSetId = mDomainVerificationManager.generateNewId(); + } else { + domainSetId = UUID.fromString(domainSetIdString); + } + + systemStr = parser.getAttributeValue(null, "publicFlags"); + if (systemStr != null) { + try { + pkgFlags = Integer.parseInt(systemStr); + } catch (NumberFormatException e) { + } + systemStr = parser.getAttributeValue(null, "privateFlags"); + if (systemStr != null) { + try { + pkgPrivateFlags = Integer.parseInt(systemStr); + } catch (NumberFormatException e) { + } + } + } else { + // Pre-M -- both public and private flags were stored in one "flags" field. + systemStr = parser.getAttributeValue(null, "flags"); + if (systemStr != null) { + try { + pkgFlags = Integer.parseInt(systemStr); + } catch (NumberFormatException e) { + } + if ((pkgFlags & PRE_M_APP_INFO_FLAG_HIDDEN) != 0) { + pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN; + } + if ((pkgFlags & PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE) != 0) { + pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE; + } + if ((pkgFlags & PRE_M_APP_INFO_FLAG_PRIVILEGED) != 0) { + pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; + } + pkgFlags &= ~(PRE_M_APP_INFO_FLAG_HIDDEN + | PRE_M_APP_INFO_FLAG_CANT_SAVE_STATE + | PRE_M_APP_INFO_FLAG_PRIVILEGED); + } else { + // For backward compatibility + systemStr = parser.getAttributeValue(null, "system"); + if (systemStr != null) { + pkgFlags |= ("true".equalsIgnoreCase(systemStr)) ? ApplicationInfo.FLAG_SYSTEM + : 0; + } else { + // Old settings that don't specify system... just treat + // them as system, good enough. + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + } + } + } + timeStamp = parser.getAttributeLongHex(null, "ft", 0); + if (timeStamp == 0) { + timeStamp = parser.getAttributeLong(null, "ts", 0); + } + firstInstallTime = parser.getAttributeLongHex(null, "it", 0); + lastUpdateTime = parser.getAttributeLongHex(null, "ut", 0); + if (PackageManagerService.DEBUG_SETTINGS) + Log.v(PackageManagerService.TAG, "Reading package: " + name + " appId=" + appId + + " sharedUserAppId=" + sharedUserAppId); + if (realName != null) { + realName = realName.intern(); + } + if (name == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: has no name at " + + parser.getPositionDescription()); + } else if (codePathStr == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: has no codePath at " + + parser.getPositionDescription()); + } else if (appId > 0 || (appId == Process.INVALID_UID && isSdkLibrary + && Flags.disallowSdkLibsToBeApps())) { + packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr), + appId, pkgFlags, pkgPrivateFlags, domainSetId); + if (PackageManagerService.DEBUG_SETTINGS) + Log.i(PackageManagerService.TAG, "Reading package " + name + ": appId=" + + appId + " pkg=" + packageSetting); + if (packageSetting == null) { + PackageManagerService.reportSettingsProblem(Log.ERROR, "Failure adding appId " + + appId + " while parsing settings at " + + parser.getPositionDescription()); + } else { + packageSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPathStr); + packageSetting.setPrimaryCpuAbi(primaryCpuAbiString); + packageSetting.setSecondaryCpuAbi(secondaryCpuAbiString); + packageSetting.setCpuAbiOverride(cpuAbiOverrideString); + packageSetting.setLongVersionCode(versionCode); + packageSetting.setLastModifiedTime(timeStamp); + packageSetting.setLastUpdateTime(lastUpdateTime); + } + } else if (sharedUserAppId != 0) { + if (sharedUserAppId > 0) { + packageSetting = new PackageSetting(name.intern(), realName, + new File(codePathStr), pkgFlags, pkgPrivateFlags, domainSetId) + .setLegacyNativeLibraryPath(legacyNativeLibraryPathStr) + .setPrimaryCpuAbi(primaryCpuAbiString) + .setSecondaryCpuAbi(secondaryCpuAbiString) + .setCpuAbiOverride(cpuAbiOverrideString) + .setLongVersionCode(versionCode) + .setSharedUserAppId(sharedUserAppId) + .setLastModifiedTime(timeStamp) + .setLastUpdateTime(lastUpdateTime); + mPendingPackages.add(packageSetting); + if (PackageManagerService.DEBUG_SETTINGS) + Log.i(PackageManagerService.TAG, "Reading package " + name + + ": sharedUserAppId=" + sharedUserAppId + " pkg=" + + packageSetting); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + name + + " has bad sharedUserAppId " + sharedUserAppId + " at " + + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + name + " has bad appId " + + appId + " at " + parser.getPositionDescription()); + } + } catch (NumberFormatException e) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + name + " has bad appId " + + appId + " at " + parser.getPositionDescription()); + } + if (packageSetting != null) { + InstallSource installSource = InstallSource.create( + installInitiatingPackageName, installOriginatingPackageName, + installerPackageName, installerPackageUid, updateOwnerPackageName, + installerAttributionTag, packageSource, isOrphaned, + installInitiatorUninstalled); + packageSetting.setInstallSource(installSource) + .setVolumeUuid(volumeUuid) + .setCategoryOverride(categoryHint) + .setLegacyNativeLibraryPath(legacyNativeLibraryPathStr) + .setPrimaryCpuAbi(primaryCpuAbiString) + .setSecondaryCpuAbi(secondaryCpuAbiString) + .setUpdateAvailable(updateAvailable) + .setForceQueryableOverride(installedForceQueryable) + .setLoadingProgress(loadingProgress) + .setLoadingCompletedTime(loadingCompletedTime) + .setAppMetadataFilePath(appMetadataFilePath) + .setAppMetadataSource(appMetadataSource) + .setTargetSdkVersion(targetSdkVersion) + .setRestrictUpdateHash(restrictUpdateHash) + .setScannedAsStoppedSystemApp(isScannedAsStoppedSystemApp); + // Handle legacy string here for single-user mode + final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED); + if (enabledStr != null) { + try { + packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */, + "settings"); + } catch (NumberFormatException e) { + if (enabledStr.equalsIgnoreCase("true")) { + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 0, "settings"); + } else if (enabledStr.equalsIgnoreCase("false")) { + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, "settings"); + } else if (enabledStr.equalsIgnoreCase("default")) { + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, "settings"); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: package " + name + + " has bad enabled value: " + enabledStr + " at " + + parser.getPositionDescription()); + } + } + } else { + packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, "settings"); + } + + addInstallerPackageNames(installSource); + + int outerDepth = parser.getDepth(); + int type; + 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(); + // Legacy + if (tagName.equals(TAG_DISABLED_COMPONENTS)) { + readDisabledComponentsLPw(packageSetting, parser, 0); + } else if (tagName.equals(TAG_ENABLED_COMPONENTS)) { + readEnabledComponentsLPw(packageSetting, parser, 0); + } else if (tagName.equals("sigs")) { + packageSetting.getSignatures().readXml(parser, readSignatures); + } else if (tagName.equals(TAG_PERMISSIONS)) { + final LegacyPermissionState legacyState; + if (packageSetting.hasSharedUser()) { + final SettingBase sharedUserSettings = getSettingLPr( + packageSetting.getSharedUserAppId()); + legacyState = sharedUserSettings != null + ? sharedUserSettings.getLegacyPermissionState() : null; + } else { + legacyState = packageSetting.getLegacyPermissionState(); + } + if (legacyState != null) { + readInstallPermissionsLPr(parser, legacyState, users); + packageSetting.setInstallPermissionsFixed(true); + } + } else if (tagName.equals("proper-signing-keyset")) { + long id = parser.getAttributeLong(null, "identifier"); + Integer refCt = keySetRefs.get(id); + if (refCt != null) { + keySetRefs.put(id, refCt + 1); + } else { + keySetRefs.put(id, 1); + } + packageSetting.getKeySetData().setProperSigningKeySet(id); + } else if (tagName.equals("signing-keyset")) { + // from v1 of keysetmanagerservice - no longer used + } else if (tagName.equals("upgrade-keyset")) { + long id = parser.getAttributeLong(null, "identifier"); + packageSetting.getKeySetData().addUpgradeKeySetById(id); + } else if (tagName.equals("defined-keyset")) { + long id = parser.getAttributeLong(null, "identifier"); + String alias = parser.getAttributeValue(null, "alias"); + Integer refCt = keySetRefs.get(id); + if (refCt != null) { + keySetRefs.put(id, refCt + 1); + } else { + keySetRefs.put(id, 1); + } + packageSetting.getKeySetData().addDefinedKeySet(id, alias); + } else if (tagName.equals("install-initiator-sigs")) { + final PackageSignatures signatures = new PackageSignatures(); + signatures.readXml(parser, readSignatures); + packageSetting.setInstallSource( + packageSetting.getInstallSource() + .setInitiatingPackageSignatures(signatures)); + } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) { + IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser); + mDomainVerificationManager.addLegacySetting(packageSetting.getPackageName(), + ivi); + if (DEBUG_PARSER) { + Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName()); + } + } else if (tagName.equals(TAG_MIME_GROUP)) { + final Pair> groupToMimeTypes = readMimeGroupLPw(parser); + if (groupToMimeTypes != null) { + packageSetting.addMimeTypes(groupToMimeTypes.first, + groupToMimeTypes.second); + } + } else if (tagName.equals(TAG_USES_STATIC_LIB)) { + readUsesStaticLibLPw(parser, packageSetting); + } else if (tagName.equals(TAG_USES_SDK_LIB)) { + readUsesSdkLibLPw(parser, packageSetting); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under : " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + if (firstInstallTime != 0) { + originalFirstInstallTimes.put(packageSetting.getPackageName(), firstInstallTime); + } + } else { + XmlUtils.skipCurrentTag(parser); + } + } + + /** + * The attribute "appId" was historically called "userId". + * TODO(b/235381248): Fix it when we solve tooling compatibility issues + */ + private static int parseAppId(TypedXmlPullParser parser) { + return parser.getAttributeInt(null, "userId", 0); + } + + /** + * The attribute "sharedUserAppId" was historically called "sharedUserId". + * TODO(b/235381248): Fix it when we solve tooling compatibility issues + */ + private static int parseSharedUserAppId(TypedXmlPullParser parser) { + return parser.getAttributeInt(null, "sharedUserId", 0); + } + + void addInstallerPackageNames(InstallSource installSource) { + if (installSource.mInstallerPackageName != null) { + mInstallerPackages.add(installSource.mInstallerPackageName); + } + if (installSource.mInitiatingPackageName != null) { + mInstallerPackages.add(installSource.mInitiatingPackageName); + } + if (installSource.mOriginatingPackageName != null) { + mInstallerPackages.add(installSource.mOriginatingPackageName); + } + } + + @Nullable + private Pair> readMimeGroupLPw(TypedXmlPullParser parser) + throws XmlPullParserException, IOException { + String groupName = parser.getAttributeValue(null, ATTR_NAME); + if (groupName == null) { + XmlUtils.skipCurrentTag(parser); + return null; + } + + Set mimeTypes = new ArraySet<>(); + int outerDepth = parser.getDepth(); + int type; + 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(TAG_MIME_TYPE)) { + String typeName = parser.getAttributeValue(null, ATTR_VALUE); + if (typeName != null) { + mimeTypes.add(typeName); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under : " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + + return Pair.create(groupName, mimeTypes); + } + + private void writeMimeGroupLPr(TypedXmlSerializer serializer, + Map> mimeGroups) throws IOException { + if (mimeGroups == null) { + return; + } + + for (String mimeGroup: mimeGroups.keySet()) { + serializer.startTag(null, TAG_MIME_GROUP); + serializer.attribute(null, ATTR_NAME, mimeGroup); + + for (String mimeType: mimeGroups.get(mimeGroup)) { + serializer.startTag(null, TAG_MIME_TYPE); + serializer.attribute(null, ATTR_VALUE, mimeType); + serializer.endTag(null, TAG_MIME_TYPE); + } + + serializer.endTag(null, TAG_MIME_GROUP); + } + } + + private void readDisabledComponentsLPw(PackageSetting packageSetting, TypedXmlPullParser parser, + int userId) throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + 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(TAG_ITEM)) { + String name = parser.getAttributeValue(null, ATTR_NAME); + if (name != null) { + packageSetting.addDisabledComponent(name.intern(), userId); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: has" + + " no name at " + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under : " + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readEnabledComponentsLPw(PackageSetting packageSetting, TypedXmlPullParser parser, + int userId) throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + 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(TAG_ITEM)) { + String name = parser.getAttributeValue(null, ATTR_NAME); + if (name != null) { + packageSetting.addEnabledComponent(name.intern(), userId); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: has" + + " no name at " + parser.getPositionDescription()); + } + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under : " + parser.getName()); + } + XmlUtils.skipCurrentTag(parser); + } + } + + private void readSharedUserLPw(TypedXmlPullParser parser, ArrayList readSignatures, + List users) + throws XmlPullParserException, IOException { + String name = null; + int pkgFlags = 0; + int pkgPrivateFlags = 0; + SharedUserSetting su = null; + { + name = parser.getAttributeValue(null, ATTR_NAME); + int appId = parseAppId(parser); + if (parser.getAttributeBoolean(null, "system", false)) { + pkgFlags |= ApplicationInfo.FLAG_SYSTEM; + } + if (name == null) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: has no name at " + + parser.getPositionDescription()); + } else if (appId == 0) { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Error in package manager settings: shared-user " + name + + " has bad appId " + appId + " at " + + parser.getPositionDescription()); + } else { + if ((su = addSharedUserLPw(name.intern(), appId, pkgFlags, pkgPrivateFlags)) + == null) { + PackageManagerService + .reportSettingsProblem(Log.ERROR, "Occurred while parsing settings at " + + parser.getPositionDescription()); + } + } + } + + if (su != null) { + int outerDepth = parser.getDepth(); + int type; + 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("sigs")) { + su.signatures.readXml(parser, readSignatures); + } else if (tagName.equals("perms")) { + readInstallPermissionsLPr(parser, su.getLegacyPermissionState(), users); + } else { + PackageManagerService.reportSettingsProblem(Log.WARN, + "Unknown element under : " + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } else { + XmlUtils.skipCurrentTag(parser); + } + } + + void createNewUserLI(@NonNull PackageManagerService service, @NonNull Installer installer, + @UserIdInt int userHandle, @Nullable Set userTypeInstallablePackages, + String[] disallowedPackages) { + final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", + Trace.TRACE_TAG_PACKAGE_MANAGER); + t.traceBegin("createNewUser-" + userHandle); + Installer.Batch batch = new Installer.Batch(); + final boolean skipPackageAllowList = userTypeInstallablePackages == null; + // Use the same timestamp for all system apps that are to be installed on the new user + final long currentTimeMillis = System.currentTimeMillis(); + synchronized (mLock) { + final int size = mPackages.size(); + for (int i = 0; i < size; i++) { + final PackageSetting ps = mPackages.valueAt(i); + if (ps.getPkg() == null) { + continue; + } + final boolean shouldMaybeInstall = ps.isSystem() && + !ArrayUtils.contains(disallowedPackages, ps.getPackageName()) && + !ps.getPkgState().isHiddenUntilInstalled(); + final boolean shouldReallyInstall = shouldMaybeInstall && + (skipPackageAllowList || userTypeInstallablePackages.contains( + ps.getPackageName())); + // Only system apps are initially installed. + ps.setInstalled(shouldReallyInstall, userHandle); + if (Flags.fixSystemAppsFirstInstallTime() && shouldReallyInstall) { + ps.setFirstInstallTime(currentTimeMillis, userHandle); + } + + // Non-Apex system apps, that are not included in the allowlist in + // initialNonStoppedSystemPackages, should be marked as stopped by default. + boolean shouldBeStopped = service.mShouldStopSystemPackagesByDefault + && ps.isSystem() + && !ps.isApex() + && !service.mInitialNonStoppedSystemPackages.contains(ps.getPackageName()); + if (shouldBeStopped) { + final Intent launcherIntent = new Intent(Intent.ACTION_MAIN); + launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER); + launcherIntent.setPackage(ps.getPackageName()); + final List launcherActivities = + service.snapshotComputer().queryIntentActivitiesInternal(launcherIntent, + null, + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0); + if (launcherActivities.isEmpty()) { + shouldBeStopped = false; + } + } + ps.setStopped(shouldBeStopped, userHandle); + + // If userTypeInstallablePackages is the *only* reason why we're not installing, + // then uninstallReason is USER_TYPE. If there's a different reason, or if we + // actually are installing, put UNKNOWN. + final int uninstallReason = (shouldMaybeInstall && !shouldReallyInstall) ? + UNINSTALL_REASON_USER_TYPE : UNINSTALL_REASON_UNKNOWN; + ps.setUninstallReason(uninstallReason, userHandle); + if (shouldReallyInstall) { + if (ps.getAppId() < 0) { + // No need to create data directories for packages with invalid app id + // such as APEX + continue; + } + // We need to create the DE data directory for all apps installed for this user. + // (CE storage is not ready yet; the CE data directories will be created later, + // when the user is "unlocked".) Accumulate all required args, and call the + // installer after the mPackages lock has been released. + final String seInfo = ps.getSeInfo(); + final boolean usesSdk = !ps.getPkg().getUsesSdkLibraries().isEmpty(); + final CreateAppDataArgs args = Installer.buildCreateAppDataArgs( + ps.getVolumeUuid(), ps.getPackageName(), userHandle, + StorageManager.FLAG_STORAGE_DE, ps.getAppId(), seInfo, + ps.getPkg().getTargetSdkVersion(), usesSdk); + batch.createAppData(args); + } else { + // Make sure the app is excluded from storage mapping for this user + writeKernelMappingLPr(ps); + } + } + } + t.traceBegin("createAppData"); + try { + batch.execute(installer); + } catch (InstallerException e) { + Slog.w(TAG, "Failed to prepare app data", e); + } + t.traceEnd(); // createAppData + synchronized (mLock) { + applyDefaultPreferredAppsLPw(userHandle); + } + t.traceEnd(); // createNewUser + } + + void removeUserLPw(int userId) { + Set> entries = mPackages.entrySet(); + for (Entry entry : entries) { + entry.getValue().removeUser(userId); + } + mPreferredActivities.remove(userId); + + synchronized (mPackageRestrictionsLock) { + getUserPackagesStateFile(userId).delete(); + mPendingAsyncPackageRestrictionsWrites.delete(userId); + } + + removeCrossProfileIntentFiltersLPw(userId); + + mRuntimePermissionsPersistence.onUserRemoved(userId); + mDomainVerificationManager.clearUser(userId); + + writePackageListLPr(); + + // Inform kernel that the user was removed, so that packages are marked uninstalled + // for sdcardfs + writeKernelRemoveUserLPr(userId); + } + + void removeCrossProfileIntentFiltersLPw(int userId) { + synchronized (mCrossProfileIntentResolvers) { + // userId is the source user + if (mCrossProfileIntentResolvers.get(userId) != null) { + mCrossProfileIntentResolvers.remove(userId); + writePackageRestrictionsLPr(userId); + } + // userId is the target user + int count = mCrossProfileIntentResolvers.size(); + for (int i = 0; i < count; i++) { + int sourceUserId = mCrossProfileIntentResolvers.keyAt(i); + CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId); + boolean needsWriting = false; + ArraySet cpifs = + new ArraySet(cpir.filterSet()); + for (CrossProfileIntentFilter cpif : cpifs) { + if (cpif.getTargetUserId() == userId) { + needsWriting = true; + cpir.removeFilter(cpif); + } + } + if (needsWriting) { + writePackageRestrictionsLPr(sourceUserId); + } + } + } + } + + public VerifierDeviceIdentity getVerifierDeviceIdentityLPw(@NonNull Computer computer) { + if (mVerifierDeviceIdentity == null) { + mVerifierDeviceIdentity = VerifierDeviceIdentity.generate(); + + writeLPr(computer, /*sync=*/false); + } + + return mVerifierDeviceIdentity; + } + + /** + * Returns the disabled {@link PackageSetting} for the provided package name if one exists, + * {@code null} otherwise. + */ + @Nullable + public PackageSetting getDisabledSystemPkgLPr(String name) { + PackageSetting ps = mDisabledSysPackages.get(name); + return ps; + } + + /** + * Returns the disabled {@link PackageSetting} for the provided enabled {@link PackageSetting} + * if one exists, {@code null} otherwise. + */ + @Nullable + public PackageSetting getDisabledSystemPkgLPr(PackageSetting enabledPackageSetting) { + if (enabledPackageSetting == null) { + return null; + } + return getDisabledSystemPkgLPr(enabledPackageSetting.getPackageName()); + } + + int getApplicationEnabledSettingLPr(String packageName, int userId) + throws PackageManager.NameNotFoundException { + final PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new PackageManager.NameNotFoundException(packageName); + } + return pkg.getEnabled(userId); + } + + int getComponentEnabledSettingLPr(ComponentName componentName, int userId) + throws PackageManager.NameNotFoundException { + final String packageName = componentName.getPackageName(); + final PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new PackageManager.NameNotFoundException(componentName.getPackageName()); + } + final String classNameStr = componentName.getClassName(); + return pkg.getCurrentEnabledStateLPr(classNameStr, userId); + } + + SharedUserSetting getSharedUserSettingLPr(String packageName) { + final PackageSetting ps = mPackages.get(packageName); + return getSharedUserSettingLPr(ps); + } + + @Nullable + SharedUserSetting getSharedUserSettingLPr(PackageSetting ps) { + if (ps == null || !ps.hasSharedUser()) { + return null; + } + return (SharedUserSetting) getSettingLPr(ps.getSharedUserAppId()); + } + + /** + * Returns all users on the device, including pre-created and dying users. + * + * @param userManager UserManagerService instance + * @return the list of users + */ + private static List getAllUsers(UserManagerService userManager) { + return getUsers(userManager, /* excludeDying= */ false, /* excludePreCreated= */ false); + } + + /** + * Returns the list of users on the device, excluding pre-created ones. + * + * @param userManager UserManagerService instance + * @param excludeDying Indicates whether to exclude any users marked for deletion. + * + * @return the list of users + */ + private static List getActiveUsers(UserManagerService userManager, + boolean excludeDying) { + return getUsers(userManager, excludeDying, /* excludePreCreated= */ true); + } + + /** + * Returns the list of users on the device. + * + * @param userManager UserManagerService instance + * @param excludeDying Indicates whether to exclude any users marked for deletion. + * @param excludePreCreated Indicates whether to exclude any pre-created users. + * + * @return the list of users + */ + private static List getUsers(UserManagerService userManager, boolean excludeDying, + boolean excludePreCreated) { + final long id = Binder.clearCallingIdentity(); + try { + return userManager.getUsers(/* excludePartial= */ true, excludeDying, + excludePreCreated); + } catch (NullPointerException npe) { + // packagemanager not yet initialized + } finally { + Binder.restoreCallingIdentity(id); + } + return null; + } + + /** + * Return all {@link PackageSetting} that are actively installed on the + * given {@link VolumeInfo#fsUuid}. + */ + List getVolumePackagesLPr(String volumeUuid) { + ArrayList res = new ArrayList<>(); + for (int i = 0; i < mPackages.size(); i++) { + final PackageSetting setting = mPackages.valueAt(i); + if (Objects.equals(volumeUuid, setting.getVolumeUuid())) { + res.add(setting); + } + } + return res; + } + + static void printFlags(PrintWriter pw, int val, Object[] spec) { + pw.print("[ "); + for (int i=0; i permissionNames, @NonNull PackageSetting ps, + LegacyPermissionState permissionsState, SimpleDateFormat sdf, Date date, + List users, boolean dumpAll, boolean dumpAllComponents) { + AndroidPackage pkg = ps.getPkg(); + if (checkinTag != null) { + pw.print(checkinTag); + pw.print(","); + pw.print(ps.getRealName() != null ? ps.getRealName() : ps.getPackageName()); + pw.print(","); + pw.print(ps.getAppId()); + pw.print(","); + pw.print(ps.getVersionCode()); + pw.print(","); + pw.print(ps.getLastUpdateTime()); + pw.print(","); + pw.print(ps.getInstallSource().mInstallerPackageName != null + ? ps.getInstallSource().mInstallerPackageName : "?"); + pw.print(ps.getInstallSource().mInstallerPackageUid); + pw.print(ps.getInstallSource().mUpdateOwnerPackageName != null + ? ps.getInstallSource().mUpdateOwnerPackageName : "?"); + pw.print(ps.getInstallSource().mInstallerAttributionTag != null + ? "(" + ps.getInstallSource().mInstallerAttributionTag + ")" : ""); + pw.print(","); + pw.print(ps.getInstallSource().mPackageSource); + pw.println(); + if (pkg != null) { + pw.print(checkinTag); pw.print("-"); pw.print("splt,"); + pw.print("base,"); + pw.println(pkg.getBaseRevisionCode()); + int[] splitRevisionCodes = pkg.getSplitRevisionCodes(); + for (int i = 0; i < pkg.getSplitNames().length; i++) { + pw.print(checkinTag); pw.print("-"); pw.print("splt,"); + pw.print(pkg.getSplitNames()[i]); pw.print(","); + pw.println(splitRevisionCodes[i]); + } + } + for (UserInfo user : users) { + final PackageUserStateInternal userState = ps.getUserStateOrDefault(user.id); + pw.print(checkinTag); + pw.print("-"); + pw.print("usr"); + pw.print(","); + pw.print(user.id); + pw.print(","); + pw.print(userState.isInstalled() ? "I" : "i"); + pw.print(userState.isHidden() ? "B" : "b"); + pw.print(userState.isSuspended() ? "SU" : "su"); + pw.print(userState.isStopped() ? "S" : "s"); + pw.print(userState.isNotLaunched() ? "l" : "L"); + pw.print(userState.isInstantApp() ? "IA" : "ia"); + pw.print(userState.isVirtualPreload() ? "VPI" : "vpi"); + pw.print(userState.isQuarantined() ? "Q" : "q"); + String harmfulAppWarning = userState.getHarmfulAppWarning(); + pw.print(harmfulAppWarning != null ? "HA" : "ha"); + pw.print(","); + pw.print(userState.getEnabledState()); + String lastDisabledAppCaller = userState.getLastDisableAppCaller(); + pw.print(","); + pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?"); + pw.print(","); + pw.print(ps.readUserState(user.id).getFirstInstallTimeMillis()); + pw.print(","); + pw.println(); + } + return; + } + + pw.print(prefix); pw.print("Package ["); + pw.print(ps.getRealName() != null ? ps.getRealName() : ps.getPackageName()); + pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(ps))); + pw.println("):"); + + if (ps.getRealName() != null) { + pw.print(prefix); pw.print(" compat name="); + pw.println(ps.getPackageName()); + } + + pw.print(prefix); pw.print(" appId="); pw.println(ps.getAppId()); + + SharedUserSetting sharedUserSetting = getSharedUserSettingLPr(ps); + if (sharedUserSetting != null) { + pw.print(prefix); pw.print(" sharedUser="); pw.println(sharedUserSetting); + } + pw.print(prefix); pw.print(" pkg="); pw.println(pkg); + pw.print(prefix); pw.print(" codePath="); pw.println(ps.getPathString()); + if (ps.getOldPaths() != null && ps.getOldPaths().size() > 0) { + for (File oldPath : ps.getOldPaths()) { + pw.print(prefix); pw.println(" oldCodePath=" + oldPath.getAbsolutePath()); + } + } + if (permissionNames == null) { + pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.getPathString()); + pw.print(prefix); pw.print(" legacyNativeLibraryDir="); + pw.println(ps.getLegacyNativeLibraryPath()); + pw.print(prefix); pw.print(" extractNativeLibs="); + pw.println((ps.getFlags() & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0 + ? "true" : "false"); + pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.getPrimaryCpuAbiLegacy()); + pw.print(prefix); pw.print(" secondaryCpuAbi="); pw.println(ps.getSecondaryCpuAbiLegacy()); + pw.print(prefix); pw.print(" cpuAbiOverride="); pw.println(ps.getCpuAbiOverride()); + } + pw.print(prefix); pw.print(" versionCode="); pw.print(ps.getVersionCode()); + if (pkg != null) { + pw.print(" minSdk="); + pw.print(pkg.getMinSdkVersion()); + } + pw.print(" targetSdk="); pw.println(ps.getTargetSdkVersion()); + if (pkg != null) { + SparseIntArray minExtensionVersions = pkg.getMinExtensionVersions(); + + pw.print(prefix); pw.print(" minExtensionVersions=["); + if (minExtensionVersions != null) { + List minExtVerStrings = new ArrayList<>(); + int size = minExtensionVersions.size(); + for (int index = 0; index < size; index++) { + int key = minExtensionVersions.keyAt(index); + int value = minExtensionVersions.valueAt(index); + minExtVerStrings.add(key + "=" + value); + } + + pw.print(TextUtils.join(", ", minExtVerStrings)); + } + pw.print("]"); + } + pw.println(); + if (pkg != null) { + pw.print(prefix); pw.print(" versionName="); pw.println(pkg.getVersionName()); + pw.print(prefix); pw.print(" hiddenApiEnforcementPolicy="); pw.println( + ps.getHiddenApiEnforcementPolicy()); + pw.print(prefix); pw.print(" usesNonSdkApi="); pw.println(pkg.isNonSdkApiRequested()); + pw.print(prefix); pw.print(" splits="); dumpSplitNames(pw, pkg); pw.println(); + final int apkSigningVersion = pkg.getSigningDetails().getSignatureSchemeVersion(); + pw.print(prefix); pw.print(" apkSigningVersion="); pw.println(apkSigningVersion); + pw.print(prefix); pw.print(" flags="); + printFlags(pw, PackageInfoUtils.appInfoFlags(pkg, ps), FLAG_DUMP_SPEC); pw.println(); + int privateFlags = PackageInfoUtils.appInfoPrivateFlags(pkg, ps); + if (privateFlags != 0) { + pw.print(prefix); pw.print(" privateFlags="); printFlags(pw, + privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println(); + } + if (ps.isPendingRestore()) { + pw.print(prefix); pw.print(" pendingRestore=true"); + pw.println(); + } + if (!pkg.isUpdatableSystem()) { + pw.print(prefix); pw.print(" updatableSystem=false"); + pw.println(); + } + if (pkg.getEmergencyInstaller() != null) { + pw.print(prefix); pw.print(" emergencyInstaller="); + pw.println(pkg.getEmergencyInstaller()); + } + if (pkg.hasPreserveLegacyExternalStorage()) { + pw.print(prefix); pw.print(" hasPreserveLegacyExternalStorage=true"); + pw.println(); + } + pw.print(prefix); pw.print(" forceQueryable="); + pw.print(ps.getPkg().isForceQueryable()); + if (ps.isForceQueryableOverride()) { + pw.print(" (override=true)"); + } + pw.println(); + if (!ps.getPkg().getQueriesPackages().isEmpty()) { + pw.append(prefix).append(" queriesPackages=") + .println(ps.getPkg().getQueriesPackages()); + } + if (!ps.getPkg().getQueriesIntents().isEmpty()) { + pw.append(prefix).append(" queriesIntents=") + .println(ps.getPkg().getQueriesIntents()); + } + pw.print(prefix); pw.print(" scannedAsStoppedSystemApp="); + pw.println(ps.isScannedAsStoppedSystemApp()); + pw.print(prefix); pw.print(" supportsScreens=["); + boolean first = true; + if (pkg.isSmallScreensSupported()) { + if (!first) + pw.print(", "); + first = false; + pw.print("small"); + } + if (pkg.isNormalScreensSupported()) { + if (!first) + pw.print(", "); + first = false; + pw.print("medium"); + } + if (pkg.isLargeScreensSupported()) { + if (!first) + pw.print(", "); + first = false; + pw.print("large"); + } + if (pkg.isExtraLargeScreensSupported()) { + if (!first) + pw.print(", "); + first = false; + pw.print("xlarge"); + } + if (pkg.isResizeable()) { + if (!first) + pw.print(", "); + first = false; + pw.print("resizeable"); + } + if (pkg.isAnyDensity()) { + if (!first) + pw.print(", "); + first = false; + pw.print("anyDensity"); + } + pw.println("]"); + final List libraryNames = pkg.getLibraryNames(); + if (libraryNames != null && libraryNames.size() > 0) { + pw.print(prefix); pw.println(" dynamic libraries:"); + for (int i = 0; i< libraryNames.size(); i++) { + pw.print(prefix); pw.print(" "); + pw.println(libraryNames.get(i)); + } + } + if (pkg.getStaticSharedLibraryName() != null) { + pw.print(prefix); pw.println(" static library:"); + pw.print(prefix); pw.print(" "); + pw.print("name:"); pw.print(pkg.getStaticSharedLibraryName()); + pw.print(" version:"); pw.println(pkg.getStaticSharedLibraryVersion()); + } + + if (pkg.getSdkLibraryName() != null) { + pw.print(prefix); pw.println(" SDK library:"); + pw.print(prefix); pw.print(" "); + pw.print("name:"); pw.print(pkg.getSdkLibraryName()); + pw.print(" versionMajor:"); pw.println(pkg.getSdkLibVersionMajor()); + } + + List usesLibraries = pkg.getUsesLibraries(); + if (usesLibraries.size() > 0) { + pw.print(prefix); pw.println(" usesLibraries:"); + for (int i=0; i< usesLibraries.size(); i++) { + pw.print(prefix); pw.print(" "); pw.println(usesLibraries.get(i)); + } + } + + List usesStaticLibraries = pkg.getUsesStaticLibraries(); + long[] usesStaticLibrariesVersions = pkg.getUsesStaticLibrariesVersions(); + if (usesStaticLibraries.size() > 0) { + pw.print(prefix); pw.println(" usesStaticLibraries:"); + for (int i=0; i< usesStaticLibraries.size(); i++) { + pw.print(prefix); pw.print(" "); + pw.print(usesStaticLibraries.get(i)); pw.print(" version:"); + pw.println(usesStaticLibrariesVersions[i]); + } + } + + List usesSdkLibraries = pkg.getUsesSdkLibraries(); + long[] usesSdkLibrariesVersionsMajor = pkg.getUsesSdkLibrariesVersionsMajor(); + boolean[] usesSdkLibrariesOptional = pkg.getUsesSdkLibrariesOptional(); + if (usesSdkLibraries.size() > 0) { + pw.print(prefix); pw.println(" usesSdkLibraries:"); + for (int i = 0, size = usesSdkLibraries.size(); i < size; ++i) { + pw.print(prefix); pw.print(" "); + pw.print(usesSdkLibraries.get(i)); pw.print(" version:"); + pw.println(usesSdkLibrariesVersionsMajor[i]); pw.print(" optional:"); + pw.println(usesSdkLibrariesOptional[i]); + } + } + + List usesOptionalLibraries = pkg.getUsesOptionalLibraries(); + if (usesOptionalLibraries.size() > 0) { + pw.print(prefix); pw.println(" usesOptionalLibraries:"); + for (int i=0; i< usesOptionalLibraries.size(); i++) { + pw.print(prefix); pw.print(" "); + pw.println(usesOptionalLibraries.get(i)); + } + } + + List usesNativeLibraries = pkg.getUsesNativeLibraries(); + if (usesNativeLibraries.size() > 0) { + pw.print(prefix); pw.println(" usesNativeLibraries:"); + for (int i=0; i< usesNativeLibraries.size(); i++) { + pw.print(prefix); pw.print(" "); pw.println(usesNativeLibraries.get(i)); + } + } + + List usesOptionalNativeLibraries = pkg.getUsesOptionalNativeLibraries(); + if (usesOptionalNativeLibraries.size() > 0) { + pw.print(prefix); pw.println(" usesOptionalNativeLibraries:"); + for (int i=0; i< usesOptionalNativeLibraries.size(); i++) { + pw.print(prefix); pw.print(" "); + pw.println(usesOptionalNativeLibraries.get(i)); + } + } + + List usesLibraryFiles = ps.getPkgState().getUsesLibraryFiles(); + if (usesLibraryFiles.size() > 0) { + pw.print(prefix); pw.println(" usesLibraryFiles:"); + for (int i=0; i< usesLibraryFiles.size(); i++) { + pw.print(prefix); pw.print(" "); pw.println(usesLibraryFiles.get(i)); + } + } + final Map procs = pkg.getProcesses(); + if (!procs.isEmpty()) { + pw.print(prefix); pw.println(" processes:"); + for (ParsedProcess proc : procs.values()) { + pw.print(prefix); pw.print(" "); pw.println(proc.getName()); + if (proc.getDeniedPermissions() != null) { + for (String deniedPermission : proc.getDeniedPermissions()) { + pw.print(prefix); pw.print(" deny: "); + pw.println(deniedPermission); + } + } + } + } + } + pw.print(prefix); pw.print(" timeStamp="); + date.setTime(ps.getLastModifiedTime()); + pw.println(sdf.format(date)); + pw.print(prefix); pw.print(" lastUpdateTime="); + date.setTime(ps.getLastUpdateTime()); + pw.println(sdf.format(date)); + pw.print(prefix); pw.print(" installerPackageName="); + pw.println(ps.getInstallSource().mInstallerPackageName); + pw.print(prefix); pw.print(" installerPackageUid="); + pw.println(ps.getInstallSource().mInstallerPackageUid); + pw.print(prefix); pw.print(" initiatingPackageName="); + pw.println(ps.getInstallSource().mInitiatingPackageName); + pw.print(prefix); pw.print(" originatingPackageName="); + pw.println(ps.getInstallSource().mOriginatingPackageName); + + if (ps.getInstallSource().mUpdateOwnerPackageName != null) { + pw.print(prefix); pw.print(" updateOwnerPackageName="); + pw.println(ps.getInstallSource().mUpdateOwnerPackageName); + } + if (ps.getInstallSource().mInstallerAttributionTag != null) { + pw.print(prefix); pw.print(" installerAttributionTag="); + pw.println(ps.getInstallSource().mInstallerAttributionTag); + } + pw.print(prefix); pw.print(" packageSource="); + pw.println(ps.getInstallSource().mPackageSource); + if (ps.isIncremental()) { + pw.print(prefix); pw.println(" loadingProgress=" + + (int) (ps.getLoadingProgress() * 100) + "%"); + date.setTime(ps.getLoadingCompletedTime()); + pw.print(prefix); pw.println(" loadingCompletedTime=" + sdf.format(date)); + } + pw.print(prefix); pw.print(" appMetadataFilePath="); + pw.println(ps.getAppMetadataFilePath()); + pw.print(prefix); pw.print(" appMetadataSource="); + pw.println(ps.getAppMetadataSource()); + if (ps.getVolumeUuid() != null) { + pw.print(prefix); pw.print(" volumeUuid="); + pw.println(ps.getVolumeUuid()); + } + pw.print(prefix); pw.print(" signatures="); pw.println(ps.getSignatures()); + pw.print(prefix); pw.print(" installPermissionsFixed="); + pw.print(ps.isInstallPermissionsFixed()); + pw.println(); + pw.print(prefix); pw.print(" pkgFlags="); printFlags(pw, ps.getFlags(), FLAG_DUMP_SPEC); + pw.println(); + pw.print(prefix); pw.print(" privatePkgFlags="); printFlags(pw, ps.getPrivateFlags(), + PRIVATE_FLAG_DUMP_SPEC); + pw.println(); + if (ps.isPendingRestore()) { + pw.print(prefix); pw.println(" pendingRestore=true"); + } + pw.print(prefix); pw.print(" apexModuleName="); pw.println(ps.getApexModuleName()); + + if (pkg != null && pkg.getOverlayTarget() != null) { + pw.print(prefix); pw.print(" overlayTarget="); pw.println(pkg.getOverlayTarget()); + pw.print(prefix); pw.print(" overlayCategory="); pw.println(pkg.getOverlayCategory()); + } + + if (pkg != null && !pkg.getPermissions().isEmpty()) { + final List perms = pkg.getPermissions(); + pw.print(prefix); pw.println(" declared permissions:"); + for (int i=0; i 0) { + final Set perms = pkg.getRequestedPermissions(); + pw.print(prefix); pw.println(" requested permissions:"); + for (String perm : perms) { + if (permissionNames != null + && !permissionNames.contains(perm)) { + continue; + } + pw.print(prefix); pw.print(" "); pw.println(perm); + } + } + + if (!ps.hasSharedUser() || permissionNames != null || dumpAll) { + dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState, users); + } + + if (dumpAllComponents) { + dumpComponents(pw, prefix + " ", ps); + } + + for (UserInfo user : users) { + final PackageUserStateInternal userState = ps.getUserStateOrDefault(user.id); + pw.print(prefix); pw.print(" User "); pw.print(user.id); pw.print(": "); + pw.print("ceDataInode="); + pw.print(userState.getCeDataInode()); + pw.print(" deDataInode="); + pw.print(userState.getDeDataInode()); + pw.print(" installed="); + pw.print(userState.isInstalled()); + pw.print(" hidden="); + pw.print(userState.isHidden()); + pw.print(" suspended="); + pw.print(userState.isSuspended()); + pw.print(" distractionFlags="); + pw.print(userState.getDistractionFlags()); + pw.print(" stopped="); + pw.print(userState.isStopped()); + pw.print(" notLaunched="); + pw.print(userState.isNotLaunched()); + pw.print(" enabled="); + pw.print(userState.getEnabledState()); + pw.print(" instant="); + pw.print(userState.isInstantApp()); + pw.print(" virtual="); + pw.print(userState.isVirtualPreload()); + pw.print(" quarantined="); + pw.print(userState.isQuarantined()); + + // Dump install state with additional indentation on their own lines. + pw.println(); + pw.print(" installReason="); + pw.println(userState.getInstallReason()); + + File dataDir = PackageInfoUtils.getDataDir(ps, user.id); + pw.print(" dataDir="); + pw.println(dataDir == null ? "null" : dataDir.getAbsolutePath()); + + final PackageUserStateInternal pus = ps.readUserState(user.id); + pw.print(" firstInstallTime="); + date.setTime(pus.getFirstInstallTimeMillis()); + pw.println(sdf.format(date)); + + if (pus.getArchiveState() != null) { + final ArchiveState archiveState = pus.getArchiveState(); + pw.print(" archiveTime="); + date.setTime(archiveState.getArchiveTimeMillis()); + pw.println(sdf.format(date)); + pw.print(" unarchiveInstallerTitle="); + pw.println(archiveState.getInstallerTitle()); + for (ArchiveState.ArchiveActivityInfo activity : archiveState.getActivityInfos()) { + pw.print(" archiveActivityInfo="); + pw.println(activity.toString()); + } + } + + pw.print(" uninstallReason="); + pw.println(userState.getUninstallReason()); + + if (userState.isSuspended()) { + pw.print(prefix); + pw.println(" Suspend params:"); + for (int i = 0; i < userState.getSuspendParams().size(); i++) { + pw.print(prefix); + pw.print(" suspendingPackage="); + pw.print(userState.getSuspendParams().keyAt(i)); + final SuspendParams params = userState.getSuspendParams().valueAt(i); + if (params != null) { + pw.print(" dialogInfo="); + pw.print(params.getDialogInfo()); + pw.print(" quarantined="); + pw.println(params.isQuarantined()); + } + pw.println(); + } + } + + final OverlayPaths overlayPaths = userState.getOverlayPaths(); + if (overlayPaths != null) { + if (!overlayPaths.getOverlayPaths().isEmpty()) { + pw.print(prefix); + pw.println(" overlay paths:"); + for (String path : overlayPaths.getOverlayPaths()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } + } + if (!overlayPaths.getResourceDirs().isEmpty()) { + pw.print(prefix); + pw.println(" legacy overlay paths:"); + for (String path : overlayPaths.getResourceDirs()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } + } + } + + final Map sharedLibraryOverlayPaths = + userState.getSharedLibraryOverlayPaths(); + if (sharedLibraryOverlayPaths != null) { + for (Map.Entry libOverlayPaths : + sharedLibraryOverlayPaths.entrySet()) { + final OverlayPaths paths = libOverlayPaths.getValue(); + if (paths == null) { + continue; + } + if (!paths.getOverlayPaths().isEmpty()) { + pw.print(prefix); + pw.println(" "); + pw.print(libOverlayPaths.getKey()); + pw.println(" overlay paths:"); + for (String path : paths.getOverlayPaths()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } + } + if (!paths.getResourceDirs().isEmpty()) { + pw.print(prefix); + pw.println(" "); + pw.print(libOverlayPaths.getKey()); + pw.println(" legacy overlay paths:"); + for (String path : paths.getResourceDirs()) { + pw.print(prefix); + pw.print(" "); + pw.println(path); + } + } + } + } + + String lastDisabledAppCaller = userState.getLastDisableAppCaller(); + if (lastDisabledAppCaller != null) { + pw.print(prefix); pw.print(" lastDisabledCaller: "); + pw.println(lastDisabledAppCaller); + } + + if (!ps.hasSharedUser()) { + dumpGidsLPr(pw, prefix + " ", mPermissionDataProvider.getGidsForUid( + UserHandle.getUid(user.id, ps.getAppId()))); + dumpRuntimePermissionsLPr(pw, prefix + " ", permissionNames, permissionsState + .getPermissionStates(user.id), dumpAll); + } + + String harmfulAppWarning = userState.getHarmfulAppWarning(); + if (harmfulAppWarning != null) { + pw.print(prefix); pw.print(" harmfulAppWarning: "); + pw.println(harmfulAppWarning); + } + + if (permissionNames == null) { + WatchedArraySet cmp = userState.getDisabledComponentsNoCopy(); + if (cmp != null && cmp.size() > 0) { + pw.print(prefix); pw.println(" disabledComponents:"); + for (int i = 0; i < cmp.size(); i++) { + pw.print(prefix); pw.print(" "); pw.println(cmp.valueAt(i)); + } + } + cmp = userState.getEnabledComponentsNoCopy(); + if (cmp != null && cmp.size() > 0) { + pw.print(prefix); pw.println(" enabledComponents:"); + for (int i = 0; i < cmp.size(); i++) { + pw.print(prefix); pw.print(" "); pw.println(cmp.valueAt(i)); + } + } + } + } + } + + void dumpPackagesLPr(PrintWriter pw, String packageName, ArraySet permissionNames, + DumpState dumpState, boolean checkin) { + final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + final Date date = new Date(); + boolean printedSomething = false; + final boolean dumpAllComponents = + dumpState.isOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS); + List users = getAllUsers(UserManagerService.getInstance()); + for (final PackageSetting ps : mPackages.values()) { + if (packageName != null && !packageName.equals(ps.getRealName()) + && !packageName.equals(ps.getPackageName())) { + continue; + } + if (ps.getPkg() != null && ps.getPkg().isApex() + && !dumpState.isOptionEnabled(DumpState.OPTION_INCLUDE_APEX)) { + // Filter APEX packages which will be dumped in the APEX section + continue; + } + final LegacyPermissionState permissionsState = + mPermissionDataProvider.getLegacyPermissionState(ps.getAppId()); + if (permissionNames != null + && !permissionsState.hasPermissionState(permissionNames)) { + continue; + } + + if (!checkin && packageName != null) { + dumpState.setSharedUser(getSharedUserSettingLPr(ps)); + } + + if (!checkin && !printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(); + pw.println("Packages:"); + printedSomething = true; + } + dumpPackageLPr(pw, " ", checkin ? "pkg" : null, permissionNames, ps, permissionsState, + sdf, date, users, packageName != null, dumpAllComponents); + } + + printedSomething = false; + if (mRenamedPackages.size() > 0 && permissionNames == null) { + for (final Map.Entry e : mRenamedPackages.entrySet()) { + if (packageName != null && !packageName.equals(e.getKey()) + && !packageName.equals(e.getValue())) { + continue; + } + if (!checkin) { + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(); + pw.println("Renamed packages:"); + printedSomething = true; + } + pw.print(" "); + } else { + pw.print("ren,"); + } + pw.print(e.getKey()); + pw.print(checkin ? " -> " : ","); + pw.println(e.getValue()); + } + } + + printedSomething = false; + if (mDisabledSysPackages.size() > 0 && permissionNames == null) { + for (final PackageSetting ps : mDisabledSysPackages.values()) { + if (packageName != null && !packageName.equals(ps.getRealName()) + && !packageName.equals(ps.getPackageName())) { + continue; + } + if (ps.getPkg() != null && ps.getPkg().isApex() + && !dumpState.isOptionEnabled(DumpState.OPTION_INCLUDE_APEX)) { + // Filter APEX packages which will be dumped in the APEX section + continue; + } + if (!checkin && !printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(); + pw.println("Hidden system packages:"); + printedSomething = true; + } + final LegacyPermissionState permissionsState = + mPermissionDataProvider.getLegacyPermissionState(ps.getAppId()); + dumpPackageLPr(pw, " ", checkin ? "dis" : null, permissionNames, ps, + permissionsState, sdf, date, users, packageName != null, dumpAllComponents); + } + } + } + + void dumpPackagesProto(ProtoOutputStream proto) { + List users = getAllUsers(UserManagerService.getInstance()); + + final int count = mPackages.size(); + for (int i = 0; i < count; i++) { + final PackageSetting ps = mPackages.valueAt(i); + ps.dumpDebug(proto, PackageServiceDumpProto.PACKAGES, users, mPermissionDataProvider); + } + } + + void dumpPermissions(PrintWriter pw, String packageName, ArraySet permissionNames, + DumpState dumpState) { + LegacyPermissionSettings.dumpPermissions(pw, packageName, permissionNames, + mPermissionDataProvider.getLegacyPermissions(), + mPermissionDataProvider.getAllAppOpPermissionPackages(), true, dumpState); + } + + void dumpSharedUsersLPr(PrintWriter pw, String packageName, ArraySet permissionNames, + DumpState dumpState, boolean checkin) { + boolean printedSomething = false; + for (SharedUserSetting su : mSharedUsers.values()) { + if (packageName != null && su != dumpState.getSharedUser()) { + continue; + } + final LegacyPermissionState permissionsState = + mPermissionDataProvider.getLegacyPermissionState(su.mAppId); + if (permissionNames != null + && !permissionsState.hasPermissionState(permissionNames)) { + continue; + } + if (!checkin) { + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(); + pw.println("Shared users:"); + printedSomething = true; + } + + pw.print(" SharedUser ["); + pw.print(su.name); + pw.print("] ("); + pw.print(Integer.toHexString(System.identityHashCode(su))); + pw.println("):"); + + String prefix = " "; + pw.print(prefix); pw.print("appId="); pw.println(su.mAppId); + + pw.print(prefix); pw.println("Packages"); + final ArraySet susPackageStates = + (ArraySet) su.getPackageStates(); + final int numPackages = susPackageStates.size(); + for (int i = 0; i < numPackages; i++) { + final PackageStateInternal ps = susPackageStates.valueAt(i); + if (ps != null) { + pw.print(prefix + " "); pw.println(ps); + } else { + pw.print(prefix + " "); pw.println("NULL?!"); + } + } + + if (dumpState.isOptionEnabled(DumpState.OPTION_SKIP_PERMISSIONS)) { + continue; + } + + List users = getAllUsers(UserManagerService.getInstance()); + + dumpInstallPermissionsLPr(pw, prefix, permissionNames, permissionsState, users); + + for (UserInfo user : users) { + final int userId = user.id; + final int[] gids = mPermissionDataProvider.getGidsForUid(UserHandle.getUid( + userId, su.mAppId)); + final Collection permissions = + permissionsState.getPermissionStates(userId); + if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) { + pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": "); + dumpGidsLPr(pw, prefix + " ", gids); + dumpRuntimePermissionsLPr(pw, prefix + " ", permissionNames, + permissions, packageName != null); + } + } + } else { + pw.print("suid,"); pw.print(su.mAppId); pw.print(","); pw.println(su.name); + } + } + } + + void dumpSharedUsersProto(ProtoOutputStream proto) { + final int count = mSharedUsers.size(); + for (int i = 0; i < count; i++) { + mSharedUsers.valueAt(i).dumpDebug(proto, PackageServiceDumpProto.SHARED_USERS); + } + } + + void dumpReadMessages(PrintWriter pw, DumpState dumpState) { + pw.println("Settings parse messages:"); + pw.print(mReadMessages.toString()); + } + + private static void dumpSplitNames(PrintWriter pw, AndroidPackage pkg) { + if (pkg == null) { + pw.print("unknown"); + } else { + // [base:10, config.mdpi, config.xhdpi:12] + pw.print("["); + pw.print("base"); + if (pkg.getBaseRevisionCode() != 0) { + pw.print(":"); pw.print(pkg.getBaseRevisionCode()); + } + String[] splitNames = pkg.getSplitNames(); + int[] splitRevisionCodes = pkg.getSplitRevisionCodes(); + for (int i = 0; i < splitNames.length; i++) { + pw.print(", "); + pw.print(splitNames[i]); + if (splitRevisionCodes[i] != 0) { + pw.print(":"); pw.print(splitRevisionCodes[i]); + } + } + pw.print("]"); + } + } + + void dumpGidsLPr(PrintWriter pw, String prefix, int[] gids) { + if (!ArrayUtils.isEmpty(gids)) { + pw.print(prefix); + pw.print("gids="); pw.println( + PackageManagerServiceUtils.arrayToString(gids)); + } + } + + void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, ArraySet permissionNames, + Collection permissionStates, boolean dumpAll) { + boolean hasRuntimePermissions = false; + for (PermissionState permissionState : permissionStates) { + if (permissionState.isRuntime()) { + hasRuntimePermissions = true; + break; + } + } + if (hasRuntimePermissions || dumpAll) { + pw.print(prefix); pw.println("runtime permissions:"); + for (PermissionState permissionState : permissionStates) { + if (!permissionState.isRuntime()) { + continue; + } + if (permissionNames != null + && !permissionNames.contains(permissionState.getName())) { + continue; + } + pw.print(prefix); pw.print(" "); pw.print(permissionState.getName()); + pw.print(": granted="); pw.print(permissionState.isGranted()); + pw.println(permissionFlagsToString(", flags=", + permissionState.getFlags())); + } + } + } + + private static String permissionFlagsToString(String prefix, int flags) { + StringBuilder flagsString = null; + while (flags != 0) { + if (flagsString == null) { + flagsString = new StringBuilder(); + flagsString.append(prefix); + flagsString.append("[ "); + } + final int flag = 1 << Integer.numberOfTrailingZeros(flags); + flags &= ~flag; + flagsString.append(PackageManager.permissionFlagToString(flag)); + if (flags != 0) { + flagsString.append('|'); + } + + } + if (flagsString != null) { + flagsString.append(']'); + return flagsString.toString(); + } else { + return ""; + } + } + + void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, + ArraySet filterPermissionNames, LegacyPermissionState permissionsState, + List users) { + ArraySet dumpPermissionNames = new ArraySet<>(); + for (UserInfo user : users) { + int userId = user.id; + Collection permissionStates = permissionsState.getPermissionStates( + userId); + for (PermissionState permissionState : permissionStates) { + if (permissionState.isRuntime()) { + continue; + } + String permissionName = permissionState.getName(); + if (filterPermissionNames != null + && !filterPermissionNames.contains(permissionName)) { + continue; + } + dumpPermissionNames.add(permissionName); + } + } + boolean printedSomething = false; + for (String permissionName : dumpPermissionNames) { + PermissionState systemPermissionState = permissionsState.getPermissionState( + permissionName, UserHandle.USER_SYSTEM); + for (UserInfo user : users) { + int userId = user.id; + PermissionState permissionState; + if (userId == UserHandle.USER_SYSTEM) { + permissionState = systemPermissionState; + } else { + permissionState = permissionsState.getPermissionState(permissionName, userId); + if (Objects.equals(permissionState, systemPermissionState)) { + continue; + } + } + if (!printedSomething) { + pw.print(prefix); pw.println("install permissions:"); + printedSomething = true; + } + pw.print(prefix); pw.print(" "); pw.print(permissionName); + pw.print(": granted="); pw.print( + permissionState != null && permissionState.isGranted()); + pw.print(permissionFlagsToString(", flags=", + permissionState != null ? permissionState.getFlags() : 0)); + if (userId == UserHandle.USER_SYSTEM) { + pw.println(); + } else { + pw.print(", userId="); pw.println(userId); + } + } + } + } + + void dumpComponents(PrintWriter pw, String prefix, PackageSetting ps) { + dumpComponents(pw, prefix, "activities:", ps.getPkg().getActivities()); + dumpComponents(pw, prefix, "services:", ps.getPkg().getServices()); + dumpComponents(pw, prefix, "receivers:", ps.getPkg().getReceivers()); + dumpComponents(pw, prefix, "providers:", ps.getPkg().getProviders()); + dumpComponents(pw, prefix, "instrumentations:", ps.getPkg().getInstrumentations()); + } + + void dumpComponents(PrintWriter pw, String prefix, String label, + List list) { + final int size = CollectionUtils.size(list); + if (size == 0) { + return; + } + pw.print(prefix);pw.println(label); + for (int i = 0; i < size; i++) { + final ParsedComponent component = list.get(i); + pw.print(prefix);pw.print(" "); + pw.println(component.getComponentName().flattenToShortString()); + } + } + + public void writePermissionStateForUserLPr(int userId, boolean sync) { + if (sync) { + mRuntimePermissionsPersistence.writeStateForUser(userId, mPermissionDataProvider, + mPackages, mSharedUsers, /*handler=*/null, mLock, /*sync=*/true); + } else { + mRuntimePermissionsPersistence.writeStateForUserAsync(userId); + } + } + + private static class KeySetToValueMap implements Map { + @NonNull + private final Set mKeySet; + private final V mValue; + + KeySetToValueMap(@NonNull Set keySet, V value) { + mKeySet = keySet; + mValue = value; + } + + @Override + public int size() { + return mKeySet.size(); + } + + @Override + public boolean isEmpty() { + return mKeySet.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return mKeySet.contains(key); + } + + @Override + public boolean containsValue(Object value) { + return mValue == value; + } + + @Override + public V get(Object key) { + return mValue; + } + + @Override + public V put(K key, V value) { + throw new UnsupportedOperationException(); + } + + @Override + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Set keySet() { + return mKeySet; + } + + @Override + public Collection values() { + throw new UnsupportedOperationException(); + } + + @Override + public Set> entrySet() { + throw new UnsupportedOperationException(); + } + } + + private static final class RuntimePermissionPersistence { + // 700-1300ms delay to avoid monopolizing PMS lock when written for multiple users. + private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 1000; + private static final double WRITE_PERMISSIONS_DELAY_JITTER = 0.3; + + private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000; + + private static final int UPGRADE_VERSION = -1; + private static final int INITIAL_VERSION = 0; + + private static final Random sRandom = new Random(); + + private String mExtendedFingerprint; + + @GuardedBy("mPersistenceLock") + private final RuntimePermissionsPersistence mPersistence; + private final Object mPersistenceLock = new Object(); + + // Low-priority handlers running on SystemBg thread. + private final Handler mAsyncHandler = new MyHandler(); + private final Handler mPersistenceHandler = new PersistenceHandler(); + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray(); + + @GuardedBy("mLock") + // The mapping keys are user ids. + private final SparseLongArray mLastNotWrittenMutationTimesMillis = new SparseLongArray(); + + @GuardedBy("mLock") + // Tracking the mutations that haven't yet been written to legacy state. + // This avoids unnecessary work when writing settings for multiple users. + private final AtomicBoolean mIsLegacyPermissionStateStale = new AtomicBoolean(false); + + @GuardedBy("mLock") + // The mapping keys are user ids. + private final SparseIntArray mVersions = new SparseIntArray(); + + @GuardedBy("mLock") + // The mapping keys are user ids. + private final SparseArray mFingerprints = new SparseArray<>(); + + @GuardedBy("mLock") + // The mapping keys are user ids. + private final SparseBooleanArray mPermissionUpgradeNeeded = new SparseBooleanArray(); + + @GuardedBy("mLock") + // Staging area for states prepared to be written. + private final SparseArray mPendingStatesToWrite = + new SparseArray<>(); + + // This is a hack to allow this class to invoke a write using Settings's data structures, + // to facilitate moving to a finer scoped lock without a significant refactor. + private final Consumer mInvokeWriteUserStateAsyncCallback; + + public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence, + Consumer invokeWriteUserStateAsyncCallback) { + mPersistence = persistence; + mInvokeWriteUserStateAsyncCallback = invokeWriteUserStateAsyncCallback; + } + + int getVersion(int userId) { + synchronized (mLock) { + return mVersions.get(userId, INITIAL_VERSION); + } + } + + void setVersion(int version, int userId) { + synchronized (mLock) { + mVersions.put(userId, version); + writeStateForUserAsync(userId); + } + } + + public boolean isPermissionUpgradeNeeded(int userId) { + synchronized (mLock) { + return mPermissionUpgradeNeeded.get(userId, true); + } + } + + public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) { + synchronized (mLock) { + if (mExtendedFingerprint == null) { + throw new RuntimeException( + "The version of the permission controller hasn't been " + + "set before trying to update the fingerprint."); + } + mFingerprints.put(userId, mExtendedFingerprint); + mPermissionUpgradeNeeded.put(userId, false); + writeStateForUserAsync(userId); + } + } + + public void setPermissionControllerVersion(long version) { + synchronized (mLock) { + int numUser = mFingerprints.size(); + mExtendedFingerprint = getExtendedFingerprint(version); + + for (int i = 0; i < numUser; i++) { + int userId = mFingerprints.keyAt(i); + String fingerprint = mFingerprints.valueAt(i); + mPermissionUpgradeNeeded.put(userId, + !TextUtils.equals(mExtendedFingerprint, fingerprint)); + } + } + } + + private String getExtendedFingerprint(long version) { + return PackagePartitions.FINGERPRINT + "?pc_version=" + version; + } + + private static long uniformRandom(double low, double high) { + double mag = high - low; + return (long) (sRandom.nextDouble() * mag + low); + } + + private static long nextWritePermissionDelayMillis() { + final long delay = WRITE_PERMISSIONS_DELAY_MILLIS; + final double jitter = WRITE_PERMISSIONS_DELAY_JITTER; + return delay + uniformRandom(-jitter * delay, jitter * delay); + } + + public void writeStateForUserAsync(int userId) { + mIsLegacyPermissionStateStale.set(true); + synchronized (mLock) { + final long currentTimeMillis = SystemClock.uptimeMillis(); + final long writePermissionDelayMillis = nextWritePermissionDelayMillis(); + + if (mWriteScheduled.get(userId)) { + mAsyncHandler.removeMessages(userId); + + // If enough time passed, write without holding off anymore. + final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis + .get(userId); + final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis + - lastNotWrittenMutationTimeMillis; + if (timeSinceLastNotWrittenMutationMillis + >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) { + mAsyncHandler.obtainMessage(userId).sendToTarget(); + return; + } + + // Hold off a bit more as settings are frequently changing. + final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis + + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0); + final long writeDelayMillis = Math.min(writePermissionDelayMillis, + maxDelayMillis); + + Message message = mAsyncHandler.obtainMessage(userId); + mAsyncHandler.sendMessageDelayed(message, writeDelayMillis); + } else { + mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis); + Message message = mAsyncHandler.obtainMessage(userId); + mAsyncHandler.sendMessageDelayed(message, writePermissionDelayMillis); + mWriteScheduled.put(userId, true); + } + } + } + + public void writeStateForUser(int userId, @NonNull LegacyPermissionDataProvider + legacyPermissionDataProvider, + @NonNull WatchedArrayMap packageStates, + @NonNull WatchedArrayMap sharedUsers, + @Nullable Handler pmHandler, @NonNull PackageManagerTracedLock pmLock, + boolean sync) { + synchronized (mLock) { + mAsyncHandler.removeMessages(userId); + mWriteScheduled.delete(userId); + } + + Runnable writer = () -> { + boolean isLegacyPermissionStateStale = mIsLegacyPermissionStateStale.getAndSet( + false); + Map> packagePermissions; + Map> sharedUserPermissions; + + synchronized (pmLock) { + if (sync || isLegacyPermissionStateStale) { + legacyPermissionDataProvider.writeLegacyPermissionStateTEMP(); + } + + packagePermissions = getPackagePermissions(userId, packageStates); + sharedUserPermissions = getShareUsersPermissions(userId, sharedUsers); + } + synchronized (mLock) { + int version = mVersions.get(userId, INITIAL_VERSION); + String fingerprint = mFingerprints.get(userId); + + RuntimePermissionsState runtimePermissions = new RuntimePermissionsState( + version, fingerprint, packagePermissions, sharedUserPermissions); + mPendingStatesToWrite.put(userId, runtimePermissions); + } + if (pmHandler != null) { + // Async version. + mPersistenceHandler.obtainMessage(userId).sendToTarget(); + } else { + // Sync version. + writePendingStates(); + } + }; + + if (pmHandler != null) { + // Async version, use pmHandler. + pmHandler.post(writer); + } else { + // Sync version, use caller's thread. + writer.run(); + } + } + + @NonNull + RuntimePermissionsState getLegacyPermissionsState(int userId, + @NonNull WatchedArrayMap packageStates, + @NonNull WatchedArrayMap sharedUsers) { + int version; + String fingerprint; + synchronized (mLock) { + version = mVersions.get(userId, INITIAL_VERSION); + fingerprint = mFingerprints.get(userId); + } + + return new RuntimePermissionsState( + version, fingerprint, getPackagePermissions(userId, packageStates), + getShareUsersPermissions(userId, sharedUsers)); + } + + @NonNull + private Map> getPackagePermissions( + int userId, + @NonNull WatchedArrayMap packageStates) { + final Map> + packagePermissions = new ArrayMap<>(); + + final int packagesSize = packageStates.size(); + for (int i = 0; i < packagesSize; i++) { + String packageName = packageStates.keyAt(i); + PackageStateInternal packageState = packageStates.valueAt(i); + if (!packageState.hasSharedUser()) { + List permissions = + getPermissionsFromPermissionsState( + packageState.getLegacyPermissionState(), userId); + if (permissions.isEmpty() + && !packageState.isInstallPermissionsFixed()) { + // Storing an empty state means the package is known to the + // system and its install permissions have been granted and fixed. + // If this is not the case, we should not store anything. + continue; + } + packagePermissions.put(packageName, permissions); + } + } + return packagePermissions; + } + + @NonNull + private Map> getShareUsersPermissions( + int userId, @NonNull WatchedArrayMap sharedUsers) { + final Map> + sharedUserPermissions = new ArrayMap<>(); + + final int sharedUsersSize = sharedUsers.size(); + for (int i = 0; i < sharedUsersSize; i++) { + String sharedUserName = sharedUsers.keyAt(i); + SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i); + List permissions = + getPermissionsFromPermissionsState( + sharedUserSetting.getLegacyPermissionState(), userId); + sharedUserPermissions.put(sharedUserName, permissions); + } + return sharedUserPermissions; + } + + private void writePendingStates() { + while (true) { + final RuntimePermissionsState runtimePermissions; + final int userId; + synchronized (mLock) { + if (mPendingStatesToWrite.size() == 0) { + break; + } + userId = mPendingStatesToWrite.keyAt(0); + runtimePermissions = mPendingStatesToWrite.valueAt(0); + mPendingStatesToWrite.removeAt(0); + } + synchronized (mPersistenceLock) { + mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId)); + } + } + } + + @NonNull + private List getPermissionsFromPermissionsState( + @NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) { + Collection permissionStates = + permissionsState.getPermissionStates(userId); + List permissions = new ArrayList<>(); + for (PermissionState permissionState : permissionStates) { + RuntimePermissionsState.PermissionState permission = + new RuntimePermissionsState.PermissionState(permissionState.getName(), + permissionState.isGranted(), permissionState.getFlags()); + permissions.add(permission); + } + return permissions; + } + + private void onUserRemoved(int userId) { + synchronized (mLock) { + // Make sure we do not + mAsyncHandler.removeMessages(userId); + + mPermissionUpgradeNeeded.delete(userId); + mVersions.delete(userId); + mFingerprints.remove(userId); + } + } + + public void deleteUserRuntimePermissionsFile(int userId) { + synchronized (mPersistenceLock) { + mPersistence.deleteForUser(UserHandle.of(userId)); + } + } + + public void readStateForUserSync(int userId, @NonNull VersionInfo internalVersion, + @NonNull WatchedArrayMap packageSettings, + @NonNull WatchedArrayMap sharedUsers, + @NonNull File userRuntimePermissionsFile) { + final RuntimePermissionsState runtimePermissions; + synchronized (mPersistenceLock) { + runtimePermissions = mPersistence.readForUser(UserHandle.of(userId)); + } + if (runtimePermissions == null) { + readLegacyStateForUserSync(userId, userRuntimePermissionsFile, packageSettings, + sharedUsers); + writeStateForUserAsync(userId); + return; + } + synchronized (mLock) { + // If the runtime permissions file exists but the version is not set this is + // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION. + int version = runtimePermissions.getVersion(); + if (version == RuntimePermissionsState.NO_VERSION) { + version = UPGRADE_VERSION; + } + mVersions.put(userId, version); + + String fingerprint = runtimePermissions.getFingerprint(); + mFingerprints.put(userId, fingerprint); + + boolean isUpgradeToR = internalVersion.sdkVersion < Build.VERSION_CODES.R; + + Map> packagePermissions = + runtimePermissions.getPackagePermissions(); + int packagesSize = packageSettings.size(); + for (int i = 0; i < packagesSize; i++) { + String packageName = packageSettings.keyAt(i); + PackageSetting packageSetting = packageSettings.valueAt(i); + + List permissions = + packagePermissions.get(packageName); + if (permissions != null) { + readPermissionsState(permissions, + packageSetting.getLegacyPermissionState(), + userId); + packageSetting.setInstallPermissionsFixed(true); + } else if (!packageSetting.hasSharedUser() && !isUpgradeToR) { + Slogf.w(TAG, "Missing permission state for package %s on user %d", + packageName, userId); + packageSetting.getLegacyPermissionState().setMissing(true, userId); + } + } + + Map> sharedUserPermissions = + runtimePermissions.getSharedUserPermissions(); + int sharedUsersSize = sharedUsers.size(); + for (int i = 0; i < sharedUsersSize; i++) { + String sharedUserName = sharedUsers.keyAt(i); + SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i); + + List permissions = + sharedUserPermissions.get(sharedUserName); + if (permissions != null) { + readPermissionsState(permissions, + sharedUserSetting.getLegacyPermissionState(), userId); + } else if (!isUpgradeToR) { + Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName); + sharedUserSetting.getLegacyPermissionState().setMissing(true, userId); + } + } + } + } + + private void readPermissionsState( + @NonNull List permissions, + @NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) { + int permissionsSize = permissions.size(); + for (int i = 0; i < permissionsSize; i++) { + RuntimePermissionsState.PermissionState permission = permissions.get(i); + String name = permission.getName(); + boolean granted = permission.isGranted(); + int flags = permission.getFlags(); + permissionsState.putPermissionState(new PermissionState(name, true, granted, + flags), userId); + } + } + + private void readLegacyStateForUserSync(int userId, @NonNull File permissionsFile, + @NonNull WatchedArrayMap packageStates, + @NonNull WatchedArrayMap sharedUsers) { + synchronized (mLock) { + if (!permissionsFile.exists()) { + return; + } + + FileInputStream in; + try { + in = new AtomicFile(permissionsFile).openRead(); + } catch (FileNotFoundException fnfe) { + Slog.i(PackageManagerService.TAG, "No permissions state"); + return; + } + + try { + final TypedXmlPullParser parser = Xml.resolvePullParser(in); + parseLegacyRuntimePermissions(parser, userId, packageStates, sharedUsers); + + } catch (XmlPullParserException | IOException e) { + throw new IllegalStateException("Failed parsing permissions file: " + + permissionsFile, e); + } finally { + IoUtils.closeQuietly(in); + } + } + } + + private void parseLegacyRuntimePermissions(TypedXmlPullParser parser, int userId, + @NonNull WatchedArrayMap packageStates, + @NonNull WatchedArrayMap sharedUsers) + throws IOException, XmlPullParserException { + synchronized (mLock) { + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + switch (parser.getName()) { + case TAG_RUNTIME_PERMISSIONS: { + // If the permisions settings file exists but the version is not set this is + // an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION + int version = parser.getAttributeInt(null, ATTR_VERSION, + UPGRADE_VERSION); + mVersions.put(userId, version); + String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT); + mFingerprints.put(userId, fingerprint); + } + break; + + case TAG_PACKAGE: { + String name = parser.getAttributeValue(null, ATTR_NAME); + PackageStateInternal ps = packageStates.get(name); + if (ps == null) { + Slog.w(PackageManagerService.TAG, "Unknown package:" + name); + XmlUtils.skipCurrentTag(parser); + continue; + } + parseLegacyPermissionsLPr(parser, ps.getLegacyPermissionState(), + userId); + } + break; + + case TAG_SHARED_USER: { + String name = parser.getAttributeValue(null, ATTR_NAME); + SharedUserSetting sus = sharedUsers.get(name); + if (sus == null) { + Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name); + XmlUtils.skipCurrentTag(parser); + continue; + } + parseLegacyPermissionsLPr(parser, sus.getLegacyPermissionState(), + userId); + } + break; + } + } + } + } + + private void parseLegacyPermissionsLPr(TypedXmlPullParser parser, + LegacyPermissionState permissionsState, int userId) + throws IOException, XmlPullParserException { + synchronized (mLock) { + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + switch (parser.getName()) { + case TAG_ITEM: { + String name = parser.getAttributeValue(null, ATTR_NAME); + final boolean granted = + parser.getAttributeBoolean(null, ATTR_GRANTED, true); + final int flags = + parser.getAttributeIntHex(null, ATTR_FLAGS, 0); + permissionsState.putPermissionState(new PermissionState(name, true, + granted, flags), userId); + } + break; + } + } + } + } + + private final class MyHandler extends Handler { + public MyHandler() { + super(BackgroundThread.getHandler().getLooper()); + } + + @Override + public void handleMessage(Message message) { + final int userId = message.what; + Runnable callback = (Runnable) message.obj; + mInvokeWriteUserStateAsyncCallback.accept(userId); + if (callback != null) { + callback.run(); + } + } + } + + private final class PersistenceHandler extends Handler { + PersistenceHandler() { + super(BackgroundThread.getHandler().getLooper()); + } + + @Override + public void handleMessage(Message message) { + writePendingStates(); + } + } + } + + /** + * Accessor for preferred activities + */ + PersistentPreferredIntentResolver getPersistentPreferredActivities(int userId) { + return mPersistentPreferredActivities.get(userId); + } + + PreferredIntentResolver getPreferredActivities(int userId) { + return mPreferredActivities.get(userId); + } + + @Nullable + CrossProfileIntentResolver getCrossProfileIntentResolver(int userId) { + return mCrossProfileIntentResolvers.get(userId); + } + + /** This method takes a specific user id as well as UserHandle.USER_ALL. */ + void clearPackagePreferredActivities(String packageName, + @NonNull SparseBooleanArray outUserChanged, int userId) { + boolean changed = false; + ArrayList removed = null; + for (int i = 0; i < mPreferredActivities.size(); i++) { + final int thisUserId = mPreferredActivities.keyAt(i); + PreferredIntentResolver pir = mPreferredActivities.valueAt(i); + if (userId != UserHandle.USER_ALL && userId != thisUserId) { + continue; + } + Iterator it = pir.filterIterator(); + while (it.hasNext()) { + PreferredActivity pa = it.next(); + // Mark entry for removal only if it matches the package name + // and the entry is of type "always". + if (packageName == null + || (pa.mPref.mComponent.getPackageName().equals(packageName) + && pa.mPref.mAlways)) { + if (removed == null) { + removed = new ArrayList<>(); + } + removed.add(pa); + } + } + if (removed != null) { + for (int j = 0; j < removed.size(); j++) { + PreferredActivity pa = removed.get(j); + pir.removeFilter(pa); + } + outUserChanged.put(thisUserId, true); + changed = true; + } + } + if (changed) { + onChanged(); + } + } + + boolean clearPackagePersistentPreferredActivities(String packageName, int userId) { + ArrayList removed = null; + boolean changed = false; + for (int i = 0; i < mPersistentPreferredActivities.size(); i++) { + final int thisUserId = mPersistentPreferredActivities.keyAt(i); + PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.valueAt(i); + if (userId != thisUserId) { + continue; + } + Iterator it = ppir.filterIterator(); + while (it.hasNext()) { + PersistentPreferredActivity ppa = it.next(); + // Mark entry for removal only if it matches the package name. + if (ppa.mComponent.getPackageName().equals(packageName)) { + if (removed == null) { + removed = new ArrayList<>(); + } + removed.add(ppa); + } + } + if (removed != null) { + for (int j = 0; j < removed.size(); j++) { + PersistentPreferredActivity ppa = removed.get(j); + ppir.removeFilter(ppa); + } + changed = true; + } + } + if (changed) { + onChanged(); + } + return changed; + } + + boolean clearPersistentPreferredActivity(IntentFilter filter, int userId) { + ArrayList removed = null; + PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId); + Iterator it = ppir.filterIterator(); + boolean changed = false; + while (it.hasNext()) { + PersistentPreferredActivity ppa = it.next(); + if (IntentFilter.filterEquals(ppa.getIntentFilter(), filter)) { + if (removed == null) { + removed = new ArrayList<>(); + } + removed.add(ppa); + } + } + if (removed != null) { + for (int i = 0; i < removed.size(); i++) { + PersistentPreferredActivity ppa = removed.get(i); + ppir.removeFilter(ppa); + } + changed = true; + } + if (changed) { + onChanged(); + } + return changed; + } + + ArrayList systemReady(ComponentResolver resolver) { + // Verify that all of the preferred activity components actually + // exist. It is possible for applications to be updated and at + // that point remove a previously declared activity component that + // had been set as a preferred activity. We try to clean this up + // the next time we encounter that preferred activity, but it is + // possible for the user flow to never be able to return to that + // situation so here we do a validity check to make sure we haven't + // left any junk around. + ArrayList changed = new ArrayList<>(); + ArrayList removed = new ArrayList<>(); + for (int i = 0; i < mPreferredActivities.size(); i++) { + PreferredIntentResolver pir = mPreferredActivities.valueAt(i); + removed.clear(); + for (PreferredActivity pa : pir.filterSet()) { + if (!resolver.isActivityDefined(pa.mPref.mComponent)) { + removed.add(pa); + } + } + if (removed.size() > 0) { + for (int r = 0; r < removed.size(); r++) { + PreferredActivity pa = removed.get(r); + Slog.w(TAG, "Removing dangling preferred activity: " + + pa.mPref.mComponent); + pir.removeFilter(pa); + } + changed.add(mPreferredActivities.keyAt(i)); + } + } + onChanged(); + return changed; + } + + void dumpPreferred(PrintWriter pw, DumpState dumpState, String packageName) { + for (int i = 0; i < mPreferredActivities.size(); i++) { + PreferredIntentResolver pir = mPreferredActivities.valueAt(i); + int user = mPreferredActivities.keyAt(i); + if (pir.dump(pw, + dumpState.getTitlePrinted() + ? "\nPreferred Activities User " + user + ":" + : "Preferred Activities User " + user + ":", " ", + packageName, true, false)) { + dumpState.setTitlePrinted(true); + } + } + } + + boolean isInstallerPackage(@NonNull String packageName) { + return mInstallerPackages.contains(packageName); + } +} diff --git a/aosp/frameworks/native/cmds/installd/InstalldNativeService.cpp b/aosp/frameworks/native/cmds/installd/InstalldNativeService.cpp index 9be17d458..5021d0dd6 100644 --- a/aosp/frameworks/native/cmds/installd/InstalldNativeService.cpp +++ b/aosp/frameworks/native/cmds/installd/InstalldNativeService.cpp @@ -828,11 +828,13 @@ void fix_app_profile_path(const std::string &packageName, uid_t userId, uid_t ui static binder::Status createAppDataDirs(const std::string& path, int32_t uid, int32_t gid, int32_t previousUid, int32_t cacheGid, const std::string& seInfo, mode_t targetMode, - long projectIdApp, long projectIdCache) { + long projectIdApp, long projectIdCache, int flags) { ScopedTrace tracer("create-dirs"); struct stat st{}; bool parent_dir_exists = (stat(path.c_str(), &st) == 0); + bool skip_selinux_cheack = parent_dir_exists ? (flags & FLAG_SKIP_SELINUX_CHECK) : false; + auto cache_path = StringPrintf("%s/%s", path.c_str(), "cache"); auto code_cache_path = StringPrintf("%s/%s", path.c_str(), "code_cache"); bool cache_exists = (access(cache_path.c_str(), F_OK) == 0); @@ -855,20 +857,20 @@ static binder::Status createAppDataDirs(const std::string& path, int32_t uid, in } // Consider restorecon over contents if label changed - if (is_selinux_enabled() && restorecon_app_data_lazy(path, seInfo, uid, parent_dir_exists)) { + if (!skip_selinux_cheack && is_selinux_enabled() && restorecon_app_data_lazy(path, seInfo, uid, parent_dir_exists)) { return error("Failed to restorecon " + path); } // If the parent dir exists, the restorecon would already have been done // as a part of the recursive restorecon above - if (parent_dir_exists && !cache_exists + if (parent_dir_exists && !cache_exists && !skip_selinux_cheack && is_selinux_enabled() && restorecon_app_data_lazy(cache_path, seInfo, uid, false)) { return error("Failed to restorecon " + cache_path); } // If the parent dir exists, the restorecon would already have been done // as a part of the recursive restorecon above - if (parent_dir_exists && !code_cache_exists + if (parent_dir_exists && !code_cache_exists && !skip_selinux_cheack && is_selinux_enabled() && restorecon_app_data_lazy(code_cache_path, seInfo, uid, false)) { return error("Failed to restorecon " + code_cache_path); } @@ -913,7 +915,7 @@ binder::Status InstalldNativeService::createAppDataLocked( auto path = create_data_user_ce_package_path(uuid_, userId, pkgname); auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, - projectIdApp, projectIdCache); + projectIdApp, projectIdCache, flags); if (!status.isOk()) { return status; } @@ -940,7 +942,7 @@ binder::Status InstalldNativeService::createAppDataLocked( auto path = create_data_user_de_package_path(uuid_, userId, pkgname); auto status = createAppDataDirs(path, uid, uid, previousUid, cacheGid, seInfo, targetMode, - projectIdApp, projectIdCache); + projectIdApp, projectIdCache, flags); if (!status.isOk()) { return status; } @@ -1168,7 +1170,7 @@ binder::Status InstalldNativeService::reconcileSdkData(const std::optional