summaryrefslogtreecommitdiff
path: root/packages/SettingsProvider/src
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2020-04-28 04:01:56 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-04-28 04:01:56 +0000
commit36ea45baa1ca93f323c551b53360dada73b0ea81 (patch)
treeb5ec8e0f19dbe87b5b52ba0476f77e3797425422 /packages/SettingsProvider/src
parent61b20fd6e74e6debe5f0baa399db2e5de1b6c931 (diff)
parentce18c5e1f69e139386b06e7b41e9e07fde20fbe0 (diff)
Merge "[SettingsState] fix boot loop due to corrupted database file" into rvc-dev
Diffstat (limited to 'packages/SettingsProvider/src')
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java82
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java49
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java58
3 files changed, 179 insertions, 10 deletions
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2245ee4f2821..94509ddcc407 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -23,12 +23,16 @@ import static android.os.Process.SYSTEM_UID;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
+import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.backup.BackupManager;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -55,12 +59,14 @@ import android.os.Build;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IUserRestrictionsListener;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -95,6 +101,7 @@ import libcore.util.HexEncoding;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
@@ -154,9 +161,11 @@ public class SettingsProvider extends ContentProvider {
private static final String LOG_TAG = "SettingsProvider";
- private static final String TABLE_SYSTEM = "system";
- private static final String TABLE_SECURE = "secure";
- private static final String TABLE_GLOBAL = "global";
+ public static final String TABLE_SYSTEM = "system";
+ public static final String TABLE_SECURE = "secure";
+ public static final String TABLE_GLOBAL = "global";
+ public static final String TABLE_SSAID = "ssaid";
+ public static final String TABLE_CONFIG = "config";
// Old tables no longer exist.
private static final String TABLE_FAVORITES = "favorites";
@@ -205,6 +214,10 @@ public class SettingsProvider extends ContentProvider {
public static final String RESULT_ROWS_DELETED = "result_rows_deleted";
public static final String RESULT_SETTINGS_LIST = "result_settings_list";
+ // Used for scheduling jobs to make a copy for the settings files
+ public static final int WRITE_FALLBACK_SETTINGS_FILES_JOB_ID = 1;
+ public static final long ONE_DAY_INTERVAL_MILLIS = 24 * 60 * 60 * 1000L;
+
// Overlay specified settings whitelisted for Instant Apps
private static final Set<String> OVERLAY_ALLOWED_GLOBAL_INSTANT_APP_SETTINGS = new ArraySet<>();
private static final Set<String> OVERLAY_ALLOWED_SYSTEM_INSTANT_APP_SETTINGS = new ArraySet<>();
@@ -2484,6 +2497,68 @@ public class SettingsProvider extends ContentProvider {
}
}
+ /**
+ * Schedule the job service to make a copy of all the settings files.
+ */
+ public void scheduleWriteFallbackFilesJob() {
+ final Context context = getContext();
+ final JobScheduler jobScheduler =
+ (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ if (jobScheduler == null) {
+ // Might happen: SettingsProvider is created before JobSchedulerService in system server
+ return;
+ }
+ // Check if the job is already scheduled. If so, skip scheduling another one
+ if (jobScheduler.getPendingJob(WRITE_FALLBACK_SETTINGS_FILES_JOB_ID) != null) {
+ return;
+ }
+ // Back up all settings files
+ final PersistableBundle bundle = new PersistableBundle();
+ final File globalSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM));
+ final File systemSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_SYSTEM, UserHandle.USER_SYSTEM));
+ final File secureSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_SECURE, UserHandle.USER_SYSTEM));
+ final File ssaidSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_SSAID, UserHandle.USER_SYSTEM));
+ final File configSettingsFile = mSettingsRegistry.getSettingsFile(
+ makeKey(SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM));
+ bundle.putString(TABLE_GLOBAL, globalSettingsFile.getAbsolutePath());
+ bundle.putString(TABLE_SYSTEM, systemSettingsFile.getAbsolutePath());
+ bundle.putString(TABLE_SECURE, secureSettingsFile.getAbsolutePath());
+ bundle.putString(TABLE_SSAID, ssaidSettingsFile.getAbsolutePath());
+ bundle.putString(TABLE_CONFIG, configSettingsFile.getAbsolutePath());
+ // Schedule the job to write the fallback files, once daily when phone is charging
+ jobScheduler.schedule(new JobInfo.Builder(WRITE_FALLBACK_SETTINGS_FILES_JOB_ID,
+ new ComponentName(context, WriteFallbackSettingsFilesJobService.class))
+ .setExtras(bundle)
+ .setPeriodic(ONE_DAY_INTERVAL_MILLIS)
+ .setRequiresCharging(true)
+ .setPersisted(true)
+ .build());
+ }
+
+ /**
+ * For each file in the given list, if it exists, copy it to a back up file. Ignore failures.
+ * @param filePaths List of paths of files that need to be backed up
+ */
+ public static void writeFallBackSettingsFiles(List<String> filePaths) {
+ final int numFiles = filePaths.size();
+ for (int i = 0; i < numFiles; i++) {
+ final String filePath = filePaths.get(i);
+ final File originalFile = new File(filePath);
+ if (SettingsState.stateFileExists(originalFile)) {
+ final File fallBackFile = new File(filePath + FALLBACK_FILE_SUFFIX);
+ try {
+ FileUtils.copy(originalFile, fallBackFile);
+ } catch (IOException ex) {
+ Slog.w(LOG_TAG, "Failed to write fallback file for: " + filePath);
+ }
+ }
+ }
+ }
+
final class SettingsRegistry {
private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
@@ -3431,6 +3506,7 @@ public class SettingsProvider extends ContentProvider {
case MSG_NOTIFY_DATA_CHANGED: {
mBackupManager.dataChanged();
+ scheduleWriteFallbackFilesJob();
} break;
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 6b8219ea9c70..6678cf6f1033 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -27,6 +27,7 @@ import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Binder;
import android.os.Build;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -101,6 +102,8 @@ final class SettingsState {
public static final int VERSION_UNDEFINED = -1;
+ public static final String FALLBACK_FILE_SUFFIX = ".fallback";
+
private static final String TAG_SETTINGS = "settings";
private static final String TAG_SETTING = "setting";
private static final String ATTR_PACKAGE = "package";
@@ -266,7 +269,7 @@ final class SettingsState {
public SettingsState(Context context, Object lock, File file, int key,
int maxBytesPerAppPackage, Looper looper) {
// It is important that we use the same lock as the settings provider
- // to ensure multiple mutations on this state are atomicaly persisted
+ // to ensure multiple mutations on this state are atomically persisted
// as the async persistence should be blocked while we make changes.
mContext = context;
mLock = lock;
@@ -998,24 +1001,56 @@ final class SettingsState {
}
@GuardedBy("mLock")
- private void readStateSyncLocked() {
+ private void readStateSyncLocked() throws IllegalStateException {
FileInputStream in;
+ AtomicFile file = new AtomicFile(mStatePersistFile);
try {
- in = new AtomicFile(mStatePersistFile).openRead();
+ in = file.openRead();
} catch (FileNotFoundException fnfe) {
- Slog.i(LOG_TAG, "No settings state " + mStatePersistFile);
+ Slog.w(LOG_TAG, "No settings state " + mStatePersistFile);
logSettingsDirectoryInformation(mStatePersistFile);
addHistoricalOperationLocked(HISTORICAL_OPERATION_INITIALIZE, null);
return;
}
+ if (parseStateFromXmlStreamLocked(in)) {
+ return;
+ }
+
+ // Settings file exists but is corrupted. Retry with the fallback file
+ final File statePersistFallbackFile = new File(
+ mStatePersistFile.getAbsolutePath() + FALLBACK_FILE_SUFFIX);
+ Slog.i(LOG_TAG, "Failed parsing settings file: " + mStatePersistFile
+ + ", retrying with fallback file: " + statePersistFallbackFile);
+ try {
+ in = new AtomicFile(statePersistFallbackFile).openRead();
+ } catch (FileNotFoundException fnfe) {
+ final String message = "No fallback file found for: " + mStatePersistFile;
+ Slog.wtf(LOG_TAG, message);
+ throw new IllegalStateException(message);
+ }
+ if (parseStateFromXmlStreamLocked(in)) {
+ // Parsed state from fallback file. Restore original file with fallback file
+ try {
+ FileUtils.copy(statePersistFallbackFile, mStatePersistFile);
+ } catch (IOException ignored) {
+ // Failed to copy, but it's okay because we already parsed states from fallback file
+ }
+ } else {
+ final String message = "Failed parsing settings file: " + mStatePersistFile;
+ Slog.wtf(LOG_TAG, message);
+ throw new IllegalStateException(message);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean parseStateFromXmlStreamLocked(FileInputStream in) {
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, StandardCharsets.UTF_8.name());
parseStateLocked(parser);
+ return true;
} catch (XmlPullParserException | IOException e) {
- String message = "Failed parsing settings file: " + mStatePersistFile;
- Slog.wtf(LOG_TAG, message);
- throw new IllegalStateException(message, e);
+ return false;
} finally {
IoUtils.closeQuietly(in);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
new file mode 100644
index 000000000000..6e5b8890438d
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 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.providers.settings;
+
+import static com.android.providers.settings.SettingsProvider.TABLE_CONFIG;
+import static com.android.providers.settings.SettingsProvider.TABLE_GLOBAL;
+import static com.android.providers.settings.SettingsProvider.TABLE_SECURE;
+import static com.android.providers.settings.SettingsProvider.TABLE_SSAID;
+import static com.android.providers.settings.SettingsProvider.TABLE_SYSTEM;
+import static com.android.providers.settings.SettingsProvider.WRITE_FALLBACK_SETTINGS_FILES_JOB_ID;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * JobService to make a copy of a list of files, given their paths.
+ */
+public class WriteFallbackSettingsFilesJobService extends JobService {
+ @Override
+ public boolean onStartJob(final JobParameters params) {
+ switch (params.getJobId()) {
+ case WRITE_FALLBACK_SETTINGS_FILES_JOB_ID:
+ final List<String> settingsFiles = new ArrayList<>();
+ settingsFiles.add(params.getExtras().getString(TABLE_GLOBAL, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_SYSTEM, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_SECURE, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_SSAID, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_CONFIG, ""));
+ SettingsProvider.writeFallBackSettingsFiles(settingsFiles);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ return false;
+ }
+
+}