diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2020-04-28 04:01:56 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-04-28 04:01:56 +0000 |
commit | 36ea45baa1ca93f323c551b53360dada73b0ea81 (patch) | |
tree | b5ec8e0f19dbe87b5b52ba0476f77e3797425422 /packages/SettingsProvider/src | |
parent | 61b20fd6e74e6debe5f0baa399db2e5de1b6c931 (diff) | |
parent | ce18c5e1f69e139386b06e7b41e9e07fde20fbe0 (diff) |
Merge "[SettingsState] fix boot loop due to corrupted database file" into rvc-dev
Diffstat (limited to 'packages/SettingsProvider/src')
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; + } + +} |