diff options
| author | Christopher Tate <ctate@google.com> | 2015-04-07 00:38:39 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-04-07 00:38:40 +0000 |
| commit | cd70621f92869f1a48a0d67d4edf68c3ba456b58 (patch) | |
| tree | ec195f1e6929433848a1e4ed910581773da177b3 /core/java | |
| parent | 7cf828ca64728e41fca0f84f297aa48a21830fd6 (diff) | |
| parent | e012a235569fe307d165dfd0784ae847d0b13739 (diff) | |
| download | frameworks_base-cd70621f92869f1a48a0d67d4edf68c3ba456b58.zip frameworks_base-cd70621f92869f1a48a0d67d4edf68c3ba456b58.tar.gz frameworks_base-cd70621f92869f1a48a0d67d4edf68c3ba456b58.tar.bz2 | |
Merge "Back up / restore preferred app configuration"
Diffstat (limited to 'core/java')
5 files changed, 214 insertions, 11 deletions
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index ca6dc69..1131ff9 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -464,7 +464,7 @@ public class BackupTransport { * transport level). * * <p>After this method returns zero, the system will then call - * {@link #getNextFullRestorePackage()} to begin the restore process for the next + * {@link #nextRestorePackage()} to begin the restore process for the next * application, and the sequence begins again. * * <p>The transport should always close this socket when returning from this method. diff --git a/core/java/android/app/backup/RecentsBackupHelper.java b/core/java/android/app/backup/RecentsBackupHelper.java index fd69d20..1a64da6 100644 --- a/core/java/android/app/backup/RecentsBackupHelper.java +++ b/core/java/android/app/backup/RecentsBackupHelper.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2014 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 android.app.backup; import android.content.Context; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index fb7c96d..649bb47 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -269,6 +269,12 @@ interface IPackageManager { void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage); /** + * Backup/restore support - only the system uid may use these. + */ + byte[] getPreferredActivityBackup(int userId); + void restorePreferredActivities(in byte[] backup, int userId); + + /** * Report the set of 'Home' activity candidates, plus (if any) which of them * is the current "always use this one" setting. */ diff --git a/core/java/com/android/server/backup/PreferredActivityBackupHelper.java b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java new file mode 100644 index 0000000..6ac0d89 --- /dev/null +++ b/core/java/com/android/server/backup/PreferredActivityBackupHelper.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2015 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.backup; + +import android.app.AppGlobals; +import android.app.backup.BackupDataInputStream; +import android.app.backup.BackupDataOutput; +import android.app.backup.BackupHelper; +import android.content.Context; +import android.content.pm.IPackageManager; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Slog; +import android.util.Xml; + +import com.android.internal.util.FastXmlSerializer; +import com.android.org.bouncycastle.util.Arrays; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlSerializer; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +public class PreferredActivityBackupHelper implements BackupHelper { + private static final String TAG = "PreferredBackup"; + private static final boolean DEBUG = true; + + // current schema of the backup state blob + private static final int STATE_VERSION = 1; + + // key under which the preferred-activity state blob is committed to backup + private static final String KEY_PREFERRED = "preferred-activity"; + + final Context mContext; + + public PreferredActivityBackupHelper(Context context) { + mContext = context; + } + + // The fds passed here are shared among all helpers, so we mustn't close them + private void writeState(ParcelFileDescriptor stateFile, byte[] payload) { + try { + FileOutputStream fos = new FileOutputStream(stateFile.getFileDescriptor()); + + // We explicitly don't close 'out' because we must not close the backing fd. + // The FileOutputStream will not close it implicitly. + @SuppressWarnings("resource") + DataOutputStream out = new DataOutputStream(fos); + + out.writeInt(STATE_VERSION); + if (payload == null) { + out.writeInt(0); + } else { + out.writeInt(payload.length); + out.write(payload); + } + } catch (IOException e) { + Slog.e(TAG, "Unable to write updated state", e); + } + } + + private byte[] readState(ParcelFileDescriptor oldStateFd) { + FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor()); + BufferedInputStream bis = new BufferedInputStream(fis); + + @SuppressWarnings("resource") + DataInputStream in = new DataInputStream(bis); + + byte[] oldState = null; + try { + int version = in.readInt(); + if (version == STATE_VERSION) { + int size = in.readInt(); + if (size > 0) { + if (size > 200*1024) { + Slog.w(TAG, "Suspiciously large state blog; ignoring. N=" + size); + } else { + // size looks okay; make the return buffer and fill it + oldState = new byte[size]; + in.read(oldState); + } + } + } else { + Slog.w(TAG, "Prior state from unrecognized version " + version); + } + } catch (EOFException e) { + // Empty file is expected on first backup, so carry on. If the state + // is truncated we just treat it the same way. + oldState = null; + } catch (Exception e) { + Slog.w(TAG, "Error examing prior backup state " + e.getMessage()); + oldState = null; + } + + return oldState; + } + + @Override + public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + byte[] payload = null; + try { + byte[] oldPayload = readState(oldState); + + IPackageManager pm = AppGlobals.getPackageManager(); + byte[] newPayload = pm.getPreferredActivityBackup(UserHandle.USER_OWNER); + if (!Arrays.areEqual(oldPayload, newPayload)) { + if (DEBUG) { + Slog.i(TAG, "State has changed => writing new preferred app payload"); + } + data.writeEntityHeader(KEY_PREFERRED, newPayload.length); + data.writeEntityData(newPayload, newPayload.length); + } else { + if (DEBUG) { + Slog.i(TAG, "No change to state => not writing to wire"); + } + } + + // Always need to re-record the state, even if nothing changed + payload = newPayload; + } catch (Exception e) { + // On failures we'll wind up committing a zero-size state payload. This is + // a forward-safe situation because we know we commit the entire new payload + // on prior-state mismatch. + Slog.w(TAG, "Unable to record preferred activities", e); + } finally { + writeState(newState, payload); + } + } + + @Override + public void restoreEntity(BackupDataInputStream data) { + IPackageManager pm = AppGlobals.getPackageManager(); + try { + byte[] payload = new byte[data.size()]; + data.read(payload); + if (DEBUG) { + Slog.i(TAG, "Restoring preferred activities; size=" + payload.length); + } + pm.restorePreferredActivities(payload, UserHandle.USER_OWNER); + } catch (Exception e) { + Slog.e(TAG, "Exception reading restore data", e); + } + } + + @Override + public void writeNewStateDescription(ParcelFileDescriptor newState) { + writeState(newState, null); + } + +} diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 037fd66..19d9e29 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -16,7 +16,6 @@ package com.android.server.backup; - import android.app.ActivityManagerNative; import android.app.IWallpaperManager; import android.app.backup.BackupDataInput; @@ -43,6 +42,13 @@ import java.io.IOException; public class SystemBackupAgent extends BackupAgentHelper { private static final String TAG = "SystemBackupAgent"; + // Names of the helper tags within the dataset. Changing one of these names will + // break the ability to restore from datasets that predate the change. + private static final String WALLPAPER_HELPER = "wallpaper"; + private static final String RECENTS_HELPER = "recents"; + private static final String SYNC_SETTINGS_HELPER = "account_sync_settings"; + private static final String PREFERRED_HELPER = "preferred_activities"; + // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are // taken to support the legacy backed-up datasets. @@ -84,10 +90,10 @@ public class SystemBackupAgent extends BackupAgentHelper { Slog.e(TAG, "Couldn't get wallpaper name\n" + re); } } - addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys)); - addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this)); - addHelper("account_sync_settings", - new AccountSyncSettingsBackupHelper(SystemBackupAgent.this)); + addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this, files, keys)); + addHelper(RECENTS_HELPER, new RecentsBackupHelper(this)); + addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); + addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this)); super.onBackup(oldState, data, newState); } @@ -113,15 +119,15 @@ public class SystemBackupAgent extends BackupAgentHelper { public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // On restore, we also support a previous data schema "system_files" - addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, + addHelper(WALLPAPER_HELPER, new WallpaperBackupHelper(this, new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }, new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} )); - addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this, + addHelper("system_files", new WallpaperBackupHelper(this, new String[] { WALLPAPER_IMAGE }, new String[] { WALLPAPER_IMAGE_KEY} )); - addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this)); - addHelper("account_sync_settings", - new AccountSyncSettingsBackupHelper(SystemBackupAgent.this)); + addHelper(RECENTS_HELPER, new RecentsBackupHelper(this)); + addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); + addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this)); try { super.onRestore(data, appVersionCode, newState); |
