Protect package-restrictions from corruption.
+ refactor to encapulate all logic into ResilientAtomicFile
Bug: 253568736
Test: atest PackageManagerSettingsTests com.android.server.pm.test.SettingsTest
Change-Id: I2bba7d623c56f40bc8ed93a21098c9184698609d
diff --git a/services/core/java/com/android/server/pm/ResilientAtomicFile.java b/services/core/java/com/android/server/pm/ResilientAtomicFile.java
new file mode 100644
index 0000000..19aa4f8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ResilientAtomicFile.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2023 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 android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.security.FileIntegrity;
+
+import libcore.io.IoUtils;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+final class ResilientAtomicFile implements Closeable {
+ private static final String LOG_TAG = "ResilientAtomicFile";
+
+ private final File mFile;
+
+ private final File mTemporaryBackup;
+
+ private final File mReserveCopy;
+
+ private final int mFileMode;
+
+ private final String mDebugName;
+
+ private final ReadEventLogger mReadEventLogger;
+
+ // Write state.
+ private FileOutputStream mMainOutStream = null;
+ private FileInputStream mMainInStream = null;
+ private FileOutputStream mReserveOutStream = null;
+ private FileInputStream mReserveInStream = null;
+
+ // Read state.
+ private File mCurrentFile = null;
+ private FileInputStream mCurrentInStream = null;
+
+ private void finalizeOutStream(FileOutputStream str) throws IOException {
+ // Flash/sync + set permissions.
+ str.flush();
+ FileUtils.sync(str);
+ FileUtils.setPermissions(str.getFD(), mFileMode, -1, -1);
+ }
+
+ ResilientAtomicFile(@NonNull File file, @NonNull File temporaryBackup,
+ @NonNull File reserveCopy, int fileMode, String debugName,
+ @Nullable ReadEventLogger readEventLogger) {
+ mFile = file;
+ mTemporaryBackup = temporaryBackup;
+ mReserveCopy = reserveCopy;
+ mFileMode = fileMode;
+ mDebugName = debugName;
+ mReadEventLogger = readEventLogger;
+ }
+
+ public File getBaseFile() {
+ return mFile;
+ }
+
+ public FileOutputStream startWrite() throws IOException {
+ if (mMainOutStream != null) {
+ throw new IllegalStateException("Duplicate startWrite call?");
+ }
+
+ new File(mFile.getParent()).mkdirs();
+
+ if (mFile.exists()) {
+ // Presence of backup settings file indicates that we failed
+ // to persist packages earlier. So preserve the older
+ // backup for future reference since the current packages
+ // might have been corrupted.
+ if (!mTemporaryBackup.exists()) {
+ if (!mFile.renameTo(mTemporaryBackup)) {
+ throw new IOException("Unable to backup " + mDebugName
+ + " file, current changes will be lost at reboot");
+ }
+ } else {
+ mFile.delete();
+ Slog.w(LOG_TAG, "Preserving older " + mDebugName + " backup");
+ }
+ }
+ // Reserve copy is not valid anymore.
+ mReserveCopy.delete();
+
+ // In case of MT access, it's possible the files get overwritten during write.
+ // Let's open all FDs we need now.
+ mMainOutStream = new FileOutputStream(mFile);
+ mMainInStream = new FileInputStream(mFile);
+ mReserveOutStream = new FileOutputStream(mReserveCopy);
+ mReserveInStream = new FileInputStream(mReserveCopy);
+
+ return mMainOutStream;
+ }
+
+ public void finishWrite(FileOutputStream str) throws IOException {
+ if (mMainOutStream != str) {
+ throw new IllegalStateException("Invalid incoming stream.");
+ }
+
+ // Flush and set permissions.
+ try (FileOutputStream mainOutStream = mMainOutStream) {
+ mMainOutStream = null;
+ finalizeOutStream(mainOutStream);
+ }
+ // New file successfully written, old one are no longer needed.
+ mTemporaryBackup.delete();
+
+ try (FileInputStream mainInStream = mMainInStream;
+ FileInputStream reserveInStream = mReserveInStream) {
+ mMainInStream = null;
+ mReserveInStream = null;
+
+ // Copy main file to reserve.
+ try (FileOutputStream reserveOutStream = mReserveOutStream) {
+ mReserveOutStream = null;
+ FileUtils.copy(mainInStream, reserveOutStream);
+ finalizeOutStream(reserveOutStream);
+ }
+
+ // Protect both main and reserve using fs-verity.
+ try (ParcelFileDescriptor mainPfd = ParcelFileDescriptor.dup(mainInStream.getFD());
+ ParcelFileDescriptor copyPfd = ParcelFileDescriptor.dup(reserveInStream.getFD())) {
+ FileIntegrity.setUpFsVerity(mainPfd);
+ FileIntegrity.setUpFsVerity(copyPfd);
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Failed to verity-protect " + mDebugName, e);
+ }
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "Failed to write reserve copy " + mDebugName + ": " + mReserveCopy, e);
+ }
+ }
+
+ public void failWrite(FileOutputStream str) {
+ if (mMainOutStream != str) {
+ throw new IllegalStateException("Invalid incoming stream.");
+ }
+
+ // Close all FDs.
+ close();
+
+ // Clean up partially written files
+ if (mFile.exists()) {
+ if (!mFile.delete()) {
+ Slog.i(LOG_TAG, "Failed to clean up mangled file: " + mFile);
+ }
+ }
+ }
+
+ public FileInputStream openRead() throws IOException {
+ if (mTemporaryBackup.exists()) {
+ try {
+ mCurrentFile = mTemporaryBackup;
+ mCurrentInStream = new FileInputStream(mCurrentFile);
+ if (mReadEventLogger != null) {
+ mReadEventLogger.logEvent(Log.INFO,
+ "Need to read from backup " + mDebugName + " file");
+ }
+ if (mFile.exists()) {
+ // If both the backup and normal file exist, we
+ // ignore the normal one since it might have been
+ // corrupted.
+ Slog.w(LOG_TAG, "Cleaning up " + mDebugName + " file " + mFile);
+ mFile.delete();
+ }
+ // Ignore reserve copy as well.
+ mReserveCopy.delete();
+ } catch (java.io.IOException e) {
+ // We'll try for the normal settings file.
+ }
+ }
+
+ if (mCurrentInStream != null) {
+ return mCurrentInStream;
+ }
+
+ if (mFile.exists()) {
+ mCurrentFile = mFile;
+ mCurrentInStream = new FileInputStream(mCurrentFile);
+ } else if (mReserveCopy.exists()) {
+ mCurrentFile = mReserveCopy;
+ mCurrentInStream = new FileInputStream(mCurrentFile);
+ if (mReadEventLogger != null) {
+ mReadEventLogger.logEvent(Log.INFO,
+ "Need to read from reserve copy " + mDebugName + " file");
+ }
+ }
+
+ if (mCurrentInStream == null) {
+ if (mReadEventLogger != null) {
+ mReadEventLogger.logEvent(Log.INFO, "No " + mDebugName + " file");
+ }
+ }
+
+ return mCurrentInStream;
+ }
+
+ public void failRead(FileInputStream str, Exception e) {
+ if (mCurrentInStream != str) {
+ throw new IllegalStateException("Invalid incoming stream.");
+ }
+ mCurrentInStream = null;
+ IoUtils.closeQuietly(str);
+
+ if (mReadEventLogger != null) {
+ mReadEventLogger.logEvent(Log.ERROR,
+ "Error reading " + mDebugName + ", removing " + mCurrentFile + '\n'
+ + Log.getStackTraceString(e));
+ }
+
+ mCurrentFile.delete();
+ mCurrentFile = null;
+ }
+
+ public void delete() {
+ mFile.delete();
+ mTemporaryBackup.delete();
+ mReserveCopy.delete();
+ }
+
+ @Override
+ public void close() {
+ IoUtils.closeQuietly(mMainOutStream);
+ IoUtils.closeQuietly(mMainInStream);
+ IoUtils.closeQuietly(mReserveOutStream);
+ IoUtils.closeQuietly(mReserveInStream);
+ IoUtils.closeQuietly(mCurrentInStream);
+ mMainOutStream = null;
+ mMainInStream = null;
+ mReserveOutStream = null;
+ mReserveInStream = null;
+ mCurrentInStream = null;
+ mCurrentFile = null;
+ }
+
+ public String toString() {
+ return mFile.getPath();
+ }
+
+ interface ReadEventLogger {
+ void logEvent(int priority, String msg);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f1998f7..9501367 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -119,7 +119,6 @@
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.security.FileIntegrity;
import com.android.server.utils.Slogf;
import com.android.server.utils.Snappable;
import com.android.server.utils.SnapshotCache;
@@ -172,7 +171,7 @@
/**
* Holds information about dynamic settings.
*/
-public final class Settings implements Watchable, Snappable {
+public final class Settings implements Watchable, Snappable, ResilientAtomicFile.ReadEventLogger {
private static final String TAG = "PackageSettings";
/**
@@ -344,7 +343,7 @@
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_VERIFICATON_STATE = "domainVerificationStatus";
+ 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";
@@ -1511,16 +1510,22 @@
return new File(new File(mSystemDir, "users"), Integer.toString(userId));
}
- // The method itself does not have to be guarded, but the file does.
- @GuardedBy("mPackageRestrictionsLock")
- private File getUserPackagesStateFile(int userId) {
- return new File(getUserSystemDirectory(userId), "package-restrictions.xml");
+ 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);
}
- // The method itself does not have to be guarded, but the file does.
- @GuardedBy("mPackageRestrictionsLock")
- private File getUserPackagesStateBackupFile(int userId) {
- return new File(getUserSystemDirectory(userId), "package-restrictions-backup.xml");
+ 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) {
@@ -1730,272 +1735,243 @@
}
}
+ @Override
+ public void logEvent(int priority, String msg) {
+ mReadMessages.append(msg + "\n");
+ PackageManagerService.reportSettingsProblem(priority, msg);
+ }
+
+
void readPackageRestrictionsLPr(int userId,
@NonNull ArrayMap<String, Long> origFirstInstallTimes) {
if (DEBUG_MU) {
Log.i(TAG, "Reading package restrictions for user=" + userId);
}
- FileInputStream str = null;
- synchronized (mPackageRestrictionsLock) {
- File userPackagesStateFile = getUserPackagesStateFile(userId);
- File backupFile = getUserPackagesStateBackupFile(userId);
- if (backupFile.exists()) {
- try {
- str = new FileInputStream(backupFile);
- mReadMessages.append("Reading from backup stopped packages file\n");
- PackageManagerService.reportSettingsProblem(Log.INFO,
- "Need to read from backup stopped packages file");
- if (userPackagesStateFile.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 "
- + userPackagesStateFile);
- userPackagesStateFile.delete();
+ 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, 0, 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*/
+ );
+ }
+ return;
}
- } catch (java.io.IOException e) {
- // We'll try for the normal settings file.
- }
- }
-
- if (str == null && userPackagesStateFile.exists()) {
- try {
- str = new FileInputStream(userPackagesStateFile);
- if (DEBUG_MU) Log.i(TAG, "Reading " + userPackagesStateFile);
- } catch (java.io.IOException e) {
- mReadMessages.append("Error reading: " + e.toString());
- PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Error reading settings: " + e);
- Slog.wtf(TAG, "Error reading package manager stopped packages", e);
- }
- }
- }
-
- if (str == null) {
- mReadMessages.append("No stopped packages file found\n");
- PackageManagerService.reportSettingsProblem(Log.INFO,
- "No stopped packages 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. Also
- // consider all applications to be installed.
- for (PackageSetting pkg : mPackages.values()) {
- pkg.setUserState(userId, 0, 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*/
- );
- }
- return;
- }
-
- try {
- 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 package restrictions file\n");
- PackageManagerService.reportSettingsProblem(Log.WARN,
- "No start tag found in package manager stopped packages");
- 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 stopped package "
- + name);
- XmlUtils.skipCurrentTag(parser);
+ 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;
}
- final long ceDataInode =
- parser.getAttributeLong(null, ATTR_CE_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_VERIFICATON_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);
-
- ArraySet<String> enabledComponents = null;
- ArraySet<String> disabledComponents = null;
- PersistableBundle suspendedAppExtras = null;
- PersistableBundle suspendedLauncherExtras = null;
- SuspendDialogInfo oldSuspendDialogInfo = null;
-
- int packageDepth = parser.getDepth();
- ArrayMap<String, SuspendParams> suspendParamsMap = null;
- while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
- && (type != XmlPullParser.END_TAG
- || parser.getDepth() > packageDepth)) {
- if (type == XmlPullParser.END_TAG
- || type == XmlPullParser.TEXT) {
+ 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;
}
- switch (parser.getName()) {
- case TAG_ENABLED_COMPONENTS:
- enabledComponents = readComponentsLPr(parser);
- break;
- case TAG_DISABLED_COMPONENTS:
- disabledComponents = readComponentsLPr(parser);
- break;
- case TAG_SUSPENDED_APP_EXTRAS:
- suspendedAppExtras = PersistableBundle.restoreFromXml(parser);
- break;
- case TAG_SUSPENDED_LAUNCHER_EXTRAS:
- suspendedLauncherExtras = PersistableBundle.restoreFromXml(parser);
- break;
- case TAG_SUSPENDED_DIALOG_INFO:
- oldSuspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser);
- break;
- case TAG_SUSPEND_PARAMS:
- final String suspendingPackage = parser.getAttributeValue(null,
- ATTR_SUSPENDING_PACKAGE);
- if (suspendingPackage == null) {
- Slog.wtf(TAG, "No suspendingPackage found inside tag "
- + TAG_SUSPEND_PARAMS);
- continue;
- }
- if (suspendParamsMap == null) {
- suspendParamsMap = new ArrayMap<>();
- }
- suspendParamsMap.put(suspendingPackage,
- SuspendParams.restoreFromXml(parser));
- break;
- default:
- Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag "
- + TAG_PACKAGE);
+
+ final long ceDataInode =
+ parser.getAttributeLong(null, ATTR_CE_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);
}
- }
- if (oldSuspendDialogInfo == null && !TextUtils.isEmpty(dialogMessage)) {
- oldSuspendDialogInfo = new SuspendDialogInfo.Builder()
- .setMessage(dialogMessage)
- .build();
- }
- if (suspended && suspendParamsMap == null) {
- final SuspendParams suspendParams = new SuspendParams(
- oldSuspendDialogInfo,
- suspendedAppExtras,
- suspendedLauncherExtras);
- suspendParamsMap = new ArrayMap<>();
- suspendParamsMap.put(oldSuspendingPackage, suspendParams);
- }
- if (blockUninstall) {
- setBlockUninstallLPw(userId, name, true);
- }
- ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
- hidden, distractionFlags, suspendParamsMap, instantApp, virtualPreload,
- enabledCaller, enabledComponents, disabledComponents, installReason,
- uninstallReason, harmfulAppWarning, splashScreenTheme,
- firstInstallTime != 0 ? firstInstallTime :
- origFirstInstallTimes.getOrDefault(name, 0L));
+ 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;
+ }
- 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 <stopped-packages>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
+ 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);
+
+ ArraySet<String> enabledComponents = null;
+ ArraySet<String> disabledComponents = null;
+ PersistableBundle suspendedAppExtras = null;
+ PersistableBundle suspendedLauncherExtras = null;
+ SuspendDialogInfo oldSuspendDialogInfo = null;
+
+ int packageDepth = parser.getDepth();
+ ArrayMap<String, SuspendParams> 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_APP_EXTRAS:
+ suspendedAppExtras = PersistableBundle.restoreFromXml(parser);
+ break;
+ case TAG_SUSPENDED_LAUNCHER_EXTRAS:
+ suspendedLauncherExtras = PersistableBundle.restoreFromXml(
+ parser);
+ break;
+ case TAG_SUSPENDED_DIALOG_INFO:
+ oldSuspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser);
+ break;
+ case TAG_SUSPEND_PARAMS:
+ final String suspendingPackage = parser.getAttributeValue(null,
+ ATTR_SUSPENDING_PACKAGE);
+ if (suspendingPackage == null) {
+ Slog.wtf(TAG, "No suspendingPackage found inside tag "
+ + TAG_SUSPEND_PARAMS);
+ continue;
+ }
+ if (suspendParamsMap == null) {
+ suspendParamsMap = new ArrayMap<>();
+ }
+ suspendParamsMap.put(suspendingPackage,
+ SuspendParams.restoreFromXml(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,
+ suspendedAppExtras,
+ suspendedLauncherExtras);
+ suspendParamsMap = new ArrayMap<>();
+ suspendParamsMap.put(oldSuspendingPackage, suspendParams);
+ }
+
+ if (blockUninstall) {
+ setBlockUninstallLPw(userId, name, true);
+ }
+ ps.setUserState(userId, ceDataInode, enabled, installed, stopped,
+ notLaunched,
+ hidden, distractionFlags, suspendParamsMap, instantApp,
+ virtualPreload,
+ enabledCaller, enabledComponents, disabledComponents, installReason,
+ uninstallReason, harmfulAppWarning, splashScreenTheme,
+ firstInstallTime != 0 ? firstInstallTime :
+ origFirstInstallTimes.getOrDefault(name, 0L));
+
+ 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 <stopped-packages>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
}
+ } catch (IOException | XmlPullParserException e) {
+ // Remove corrupted file and retry.
+ atomicFile.failRead(str, e);
+
+ readPackageRestrictionsLPr(userId, origFirstInstallTimes);
}
-
- 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);
}
}
@@ -2165,219 +2141,176 @@
Log.i(TAG, "Writing package restrictions for user=" + userId);
}
- final File userPackagesStateFile;
- final File backupFile;
- final FileOutputStream fstr;
+ 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);
+ }
- 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);
- }
-
- // Keep the old stopped packages around until we know the new ones have
- // been successfully written.
- userPackagesStateFile = getUserPackagesStateFile(userId);
- backupFile = getUserPackagesStateBackupFile(userId);
- new File(userPackagesStateFile.getParent()).mkdirs();
- if (userPackagesStateFile.exists()) {
- // Presence of backup settings file indicates that we failed
- // to persist packages earlier. So preserve the older
- // backup for future reference since the current packages
- // might have been corrupted.
- if (!backupFile.exists()) {
- if (!userPackagesStateFile.renameTo(backupFile)) {
+ try {
+ str = atomicFile.startWrite();
+ } catch (java.io.IOException e) {
Slog.wtf(PackageManagerService.TAG,
- "Unable to backup user packages state file, "
- + "current changes will be lost at reboot");
+ "Unable to write package manager package restrictions, "
+ + " current changes will be lost at reboot", e);
return;
}
- } else {
- userPackagesStateFile.delete();
- Slog.w(PackageManagerService.TAG, "Preserving older stopped packages backup");
}
- }
- try {
- fstr = new FileOutputStream(userPackagesStateFile);
- // File is created, set permissions.
- FileUtils.setPermissions(userPackagesStateFile.toString(),
- FileUtils.S_IRUSR | FileUtils.S_IWUSR
- | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
- -1, -1);
- } catch (java.io.IOException e) {
- Slog.wtf(PackageManagerService.TAG,
- "Unable to write package manager user packages state, "
- + " 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);
- try {
- synchronized (mLock) {
- final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr);
- serializer.startDocument(null, true);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
- true);
+ serializer.startTag(null, TAG_PACKAGE_RESTRICTIONS);
- 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.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.isSuspended()) {
+ for (int i = 0; i < ustate.getSuspendParams().size(); i++) {
+ final String suspendingPackage = ustate.getSuspendParams().keyAt(i);
+ serializer.startTag(null, TAG_SUSPEND_PARAMS);
+ serializer.attribute(null, ATTR_SUSPENDING_PACKAGE,
+ suspendingPackage);
+ final SuspendParams params =
+ ustate.getSuspendParams().valueAt(i);
+ if (params != null) {
+ params.saveToXml(serializer);
+ }
+ serializer.endTag(null, TAG_SUSPEND_PARAMS);
+ }
+ }
+ final ArraySet<String> 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<String> 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);
+ }
+
+ 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) {
- Slogf.i(TAG, "Writing %s (%d packages)", userPackagesStateFile,
- 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.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.isSuspended()) {
- for (int i = 0; i < ustate.getSuspendParams().size(); i++) {
- final String suspendingPackage = ustate.getSuspendParams().keyAt(i);
- serializer.startTag(null, TAG_SUSPEND_PARAMS);
- serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, suspendingPackage);
- final SuspendParams params =
- ustate.getSuspendParams().valueAt(i);
- if (params != null) {
- params.saveToXml(serializer);
- }
- serializer.endTag(null, TAG_SUSPEND_PARAMS);
- }
- }
- final ArraySet<String> 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<String> 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);
- }
-
- serializer.endTag(null, TAG_PACKAGE);
+ Log.i(TAG, "New package restrictions successfully written for user=" + userId
+ + ": " + atomicFile);
}
- writePreferredActivitiesLPr(serializer, userId, true);
- writePersistentPreferredActivitiesLPr(serializer, userId);
- writeCrossProfileIntentFiltersLPr(serializer, userId);
- writeDefaultAppsLPr(serializer, userId);
- writeBlockUninstallPackagesLPr(serializer, userId);
+ com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
+ "package-user-" + userId, SystemClock.uptimeMillis() - startTime);
- serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
-
- serializer.endDocument();
- }
-
- fstr.flush();
- FileUtils.sync(fstr);
- IoUtils.closeQuietly(fstr);
-
- synchronized (mPackageRestrictionsLock) {
- // File is created, set permissions.
- FileUtils.setPermissions(userPackagesStateFile.toString(),
- FileUtils.S_IRUSR | FileUtils.S_IWUSR
- | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
- -1, -1);
- // New settings successfully written, old ones are no longer needed.
- backupFile.delete();
- }
-
- if (DEBUG_MU) {
- Log.i(TAG, "New settings successfully written for user=" + userId + ": "
- + userPackagesStateFile);
- }
-
- 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 user packages state, "
- + " current changes will be lost at reboot", e);
- }
-
- // Clean up partially written files
- if (userPackagesStateFile.exists()) {
- if (!userPackagesStateFile.delete()) {
- Log.i(PackageManagerService.TAG, "Failed to clean up mangled file: "
- + mStoppedPackagesFilename);
+ // 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);
+ }
}
}
}
@@ -2589,153 +2522,108 @@
// right time.
invalidatePackageCache();
- // Keep the old settings around until we know the new ones have
- // been successfully written.
- if (mSettingsFilename.exists()) {
- // Presence of backup settings file indicates that we failed
- // to persist settings earlier. So preserve the older
- // backup for future reference since the current settings
- // might have been corrupted.
- if (!mPreviousSettingsFilename.exists()) {
- if (!mSettingsFilename.renameTo(mPreviousSettingsFilename)) {
- Slog.wtf(PackageManagerService.TAG,
- "Unable to store older package manager settings, "
- + " current changes will be lost at reboot");
- return;
- }
- } else {
- mSettingsFilename.delete();
- Slog.w(PackageManagerService.TAG, "Preserving older settings backup");
- }
- }
-
mPastSignatures.clear();
- try {
- final FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
- final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr);
- 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, 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", mPastSignatures.untrackedStorage());
- serializer.endTag(null, "shared-user");
- }
-
- if (mRenamedPackages.size() > 0) {
- for (Map.Entry<String, String> 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();
-
- fstr.flush();
- FileUtils.sync(fstr);
- fstr.close();
-
- // New settings successfully written, old ones are no longer needed.
- mPreviousSettingsFilename.delete();
- mSettingsReserveCopyFilename.delete();
-
- FileUtils.setPermissions(mSettingsFilename.toString(),
- FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
- -1, -1);
-
- try (FileInputStream in = new FileInputStream(mSettingsFilename);
- FileOutputStream out = new FileOutputStream(mSettingsReserveCopyFilename)) {
- FileUtils.copy(in, out);
- out.flush();
- FileUtils.sync(out);
- } catch (IOException e) {
- Slog.e(TAG,
- "Failed to write reserve copy of settings: " + mSettingsReserveCopyFilename,
- e);
- }
-
+ try (ResilientAtomicFile atomicFile = getSettingsFile()) {
+ FileOutputStream str = null;
try {
- FileIntegrity.setUpFsVerity(mSettingsFilename);
- FileIntegrity.setUpFsVerity(mSettingsReserveCopyFilename);
- } catch (IOException e) {
- Slog.e(TAG, "Failed to verity-protect settings", e);
- }
+ str = atomicFile.startWrite();
- writeKernelMappingLPr();
- writePackageListLPr();
- writeAllUsersPackageRestrictionsLPr(sync);
- writeAllRuntimePermissionsLPr();
- com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
- "package", SystemClock.uptimeMillis() - startTime);
- return;
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(str);
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
+ true);
- } catch(java.io.IOException e) {
- Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
- + "current changes will be lost at reboot", e);
- }
- // Clean up partially written files
- if (mSettingsFilename.exists()) {
- if (!mSettingsFilename.delete()) {
- Slog.wtf(PackageManagerService.TAG, "Failed to clean up mangled file: "
- + mSettingsFilename);
+ 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, 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", mPastSignatures.untrackedStorage());
+ serializer.endTag(null, "shared-user");
+ }
+
+ if (mRenamedPackages.size() > 0) {
+ for (Map.Entry<String, String> 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();
@@ -3160,183 +3048,140 @@
mInstallerPackages.clear();
originalFirstInstallTimes.clear();
- File file = null;
- FileInputStream str = null;
+ 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);
- try {
- // Check if the previous write was incomplete.
- if (mPreviousSettingsFilename.exists()) {
- try {
- file = mPreviousSettingsFilename;
- str = new FileInputStream(file);
- mReadMessages.append("Reading from backup settings file\n");
- PackageManagerService.reportSettingsProblem(Log.INFO,
- "Need to read from backup settings file");
- if (mSettingsFilename.exists()) {
- // If both the previous and current settings files exist,
- // we ignore the current since it might have been corrupted.
- Slog.w(PackageManagerService.TAG, "Cleaning up settings file "
- + mSettingsFilename);
- mSettingsFilename.delete();
+ 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;
}
- // Ignore reserve copy as well.
- mSettingsReserveCopyFilename.delete();
- } catch (java.io.IOException e) {
- // We'll try for the normal settings file.
- }
- }
- if (str == null) {
- if (mSettingsFilename.exists()) {
- // Using packages.xml.
- file = mSettingsFilename;
- str = new FileInputStream(file);
- } else if (mSettingsReserveCopyFilename.exists()) {
- // Using reserve copy.
- file = mSettingsReserveCopyFilename;
- str = new FileInputStream(file);
- mReadMessages.append("Reading from reserve copy settings file\n");
- PackageManagerService.reportSettingsProblem(Log.INFO,
- "Need to read from reserve copy settings file");
- }
- }
- if (str == null) {
- // No available data sources.
- mReadMessages.append("No settings file found\n");
- PackageManagerService.reportSettingsProblem(Log.INFO,
- "No settings file; creating initial state");
- // 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) {
- ;
- }
+ String tagName = parser.getName();
+ if (tagName.equals("package")) {
+ readPackageLPw(parser, 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, 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);
- 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;
- }
+ 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");
- 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;
- }
+ } 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);
- String tagName = parser.getName();
- if (tagName.equals("package")) {
- readPackageLPw(parser, 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, 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);
+ 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,
+ mKeySetRefs.untrackedStorage());
+ } 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 <packages>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
}
- } 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, mKeySetRefs.untrackedStorage());
- } 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 <packages>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
}
+
+ str.close();
+ } catch (IOException | XmlPullParserException 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);
}
-
- str.close();
- } catch (IOException | XmlPullParserException e) {
- mReadMessages.append("Error reading: " + e.toString());
- PackageManagerService.reportSettingsProblem(Log.ERROR, "Error reading settings: " + e);
- Slog.wtf(PackageManagerService.TAG, "Error reading package manager settings", e);
-
- // Remove corrupted file and retry.
- Slog.e(TAG,
- "Error reading package manager settings, removing " + file + " and retrying.",
- e);
- file.delete();
-
- // Ignore the result to not mark this as a "first boot".
- readSettingsLPw(computer, users, originalFirstInstallTimes);
}
return true;
@@ -4488,10 +4333,7 @@
mPreferredActivities.remove(userId);
synchronized (mPackageRestrictionsLock) {
- File file = getUserPackagesStateFile(userId);
- file.delete();
- file = getUserPackagesStateBackupFile(userId);
- file.delete();
+ getUserPackagesStateFile(userId).delete();
mPendingAsyncPackageRestrictionsWrites.delete(userId);
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index e20330d..8b118da 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -27,6 +27,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.security.FileIntegrity;
import org.json.JSONException;
import org.json.JSONObject;
@@ -180,6 +181,12 @@
os.flush();
file.finishWrite(os);
+
+ try {
+ FileIntegrity.setUpFsVerity(path);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to verity-protect " + path, e);
+ }
} catch (XmlPullParserException | IOException e) {
Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
file.failWrite(os);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 372b3bb..20cb485 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -119,7 +119,6 @@
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.security.FileIntegrity;
import com.android.server.uri.UriGrantsManagerInternal;
import org.json.JSONArray;
@@ -1070,57 +1069,38 @@
}
@VisibleForTesting
- final File getUserFile(@UserIdInt int userId) {
- return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
- }
-
- @VisibleForTesting
- final File getReserveCopyUserFile(@UserIdInt int userId) {
- return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES_RESERVE_COPY);
+ final ResilientAtomicFile getUserFile(@UserIdInt int userId) {
+ File mainFile = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
+ File temporaryBackup = new File(injectUserDataPath(userId),
+ FILENAME_USER_PACKAGES + ".backup");
+ File reserveCopy = new File(injectUserDataPath(userId),
+ FILENAME_USER_PACKAGES_RESERVE_COPY);
+ int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
+ return new ResilientAtomicFile(mainFile, temporaryBackup, reserveCopy, fileMode,
+ "user shortcut", null);
}
@GuardedBy("mLock")
private void saveUserLocked(@UserIdInt int userId) {
- final File path = getUserFile(userId);
- if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "Saving to " + path);
- }
+ try (ResilientAtomicFile file = getUserFile(userId)) {
+ FileOutputStream os = null;
+ try {
+ if (DEBUG || DEBUG_REBOOT) {
+ Slog.d(TAG, "Saving to " + file);
+ }
- final File reservePath = getReserveCopyUserFile(userId);
- reservePath.delete();
+ os = file.startWrite();
- path.getParentFile().mkdirs();
- final AtomicFile file = new AtomicFile(path);
- FileOutputStream os = null;
- try {
- os = file.startWrite();
+ saveUserInternalLocked(userId, os, /* forBackup= */ false);
- saveUserInternalLocked(userId, os, /* forBackup= */ false);
+ file.finishWrite(os);
- file.finishWrite(os);
-
- // Remove all dangling bitmap files.
- cleanupDanglingBitmapDirectoriesLocked(userId);
- } catch (XmlPullParserException | IOException e) {
- Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
- file.failWrite(os);
- }
-
- // Store the reserve copy of the file.
- try (FileInputStream in = new FileInputStream(path);
- FileOutputStream out = new FileOutputStream(reservePath)) {
- FileUtils.copy(in, out);
- FileUtils.sync(out);
- } catch (IOException e) {
- Slog.e(TAG, "Failed to write reserve copy: " + path, e);
- }
-
- // Protect both primary and reserve copy with fs-verity.
- try {
- FileIntegrity.setUpFsVerity(path);
- FileIntegrity.setUpFsVerity(reservePath);
- } catch (IOException e) {
- Slog.e(TAG, "Failed to verity-protect", e);
+ // Remove all dangling bitmap files.
+ cleanupDanglingBitmapDirectoriesLocked(userId);
+ } catch (XmlPullParserException | IOException e) {
+ Slog.e(TAG, "Failed to write to file " + file, e);
+ file.failWrite(os);
+ }
}
getUserShortcutsLocked(userId).logSharingShortcutStats(mMetricsLogger);
@@ -1157,29 +1137,26 @@
@Nullable
private ShortcutUser loadUserLocked(@UserIdInt int userId) {
- final File path = getUserFile(userId);
- if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "Loading from " + path);
- }
-
- try (FileInputStream in = new AtomicFile(path).openRead()) {
- return loadUserInternal(userId, in, /* forBackup= */ false);
- } catch (FileNotFoundException e) {
- if (DEBUG || DEBUG_REBOOT) {
- Slog.d(TAG, "Not found " + path);
- }
- } catch (Exception e) {
- final File reservePath = getReserveCopyUserFile(userId);
- Slog.e(TAG, "Reading from reserve copy: " + reservePath, e);
- try (FileInputStream in = new AtomicFile(reservePath).openRead()) {
+ try (ResilientAtomicFile file = getUserFile(userId)) {
+ FileInputStream in = null;
+ try {
+ if (DEBUG || DEBUG_REBOOT) {
+ Slog.d(TAG, "Loading from " + file);
+ }
+ in = file.openRead();
+ if (in == null) {
+ if (DEBUG || DEBUG_REBOOT) {
+ Slog.d(TAG, "Not found " + file);
+ }
+ return null;
+ }
return loadUserInternal(userId, in, /* forBackup= */ false);
- } catch (Exception exceptionReadingReserveFile) {
- Slog.e(TAG, "Failed to read reserve copy: " + reservePath,
- exceptionReadingReserveFile);
+ } catch (Exception e) {
+ // Remove corrupted file and retry.
+ file.failRead(in, e);
+ return loadUserLocked(userId);
}
- Slog.e(TAG, "Failed to read file " + path, e);
}
- return null;
}
private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index a39e021..836f858 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -566,6 +566,22 @@
}
@Test
+ public void testWriteCorruptReadPackageRestrictions() {
+ final Settings settingsUnderTest = makeSettings();
+
+ populateDistractionFlags(settingsUnderTest);
+ settingsUnderTest.writePackageRestrictionsLPr(0, /*sync=*/true);
+
+ // Corrupt primary file.
+ writeCorruptedPackageRestrictions(0);
+
+ // now read and verify
+ populateDefaultSettings(settingsUnderTest);
+ settingsUnderTest.readPackageRestrictionsLPr(0, mOrigFirstInstallTimes);
+ verifyDistractionFlags(settingsUnderTest);
+ }
+
+ @Test
public void testReadWritePackageRestrictionsAsync() {
final Settings settingsWrite = makeSettings();
final Settings settingsRead = makeSettings();
@@ -1811,6 +1827,14 @@
.getBytes());
}
+ private void writeCorruptedPackageRestrictions(final int userId) {
+ writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/users/"
+ + userId + "/package-restrictions.xml"),
+ ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<package-restrictions>\n"
+ + " <pkg name=\"" + PACKAGE_NAME_1 + "\" ").getBytes());
+ }
+
private static void writeStoppedPackagesXml() {
writeFile(new File(InstrumentationRegistry.getContext().getFilesDir(), "system/packages-stopped.xml"),
( "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 0a718e3..861087a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -4096,11 +4096,12 @@
// Save and corrupt the primary files.
mService.saveDirtyInfo();
- try (Writer os = new FileWriter(mService.getUserFile(UserHandle.USER_SYSTEM))) {
+ try (Writer os = new FileWriter(
+ mService.getUserFile(UserHandle.USER_SYSTEM).getBaseFile())) {
os.write("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ "<user locales=\"en\" last-app-scan-time2=\"14400000");
}
- try (Writer os = new FileWriter(mService.getUserFile(USER_10))) {
+ try (Writer os = new FileWriter(mService.getUserFile(USER_10).getBaseFile())) {
os.write("<?xml version='1.0' encoding='utf");
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index 15fd73c..01e56a0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -2349,7 +2349,7 @@
* can still be read.
*/
public void testLoadLegacySavedFile() throws Exception {
- final File path = mService.getUserFile(USER_0);
+ final File path = mService.getUserFile(USER_0).getBaseFile();
path.getParentFile().mkdirs();
try (Writer w = new FileWriter(path)) {
w.write(readTestAsset("shortcut/shortcut_legacy_file.xml"));