Loading core/java/android/app/backup/BackupTransport.java +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading core/java/android/app/backup/RecentsBackupHelper.java +16 −0 Original line number Diff line number Diff line /* * 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; Loading core/java/android/content/pm/IPackageManager.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -268,6 +268,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. Loading core/java/com/android/server/backup/PreferredActivityBackupHelper.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * 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); } } core/java/com/android/server/backup/SystemBackupAgent.java +16 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.server.backup; import android.app.ActivityManagerNative; import android.app.IWallpaperManager; import android.app.backup.BackupDataInput; Loading @@ -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. Loading Loading @@ -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); } Loading @@ -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); Loading Loading
core/java/android/app/backup/BackupTransport.java +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading
core/java/android/app/backup/RecentsBackupHelper.java +16 −0 Original line number Diff line number Diff line /* * 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; Loading
core/java/android/content/pm/IPackageManager.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -268,6 +268,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. Loading
core/java/com/android/server/backup/PreferredActivityBackupHelper.java 0 → 100644 +175 −0 Original line number Diff line number Diff line /* * 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); } }
core/java/com/android/server/backup/SystemBackupAgent.java +16 −10 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.server.backup; import android.app.ActivityManagerNative; import android.app.IWallpaperManager; import android.app.backup.BackupDataInput; Loading @@ -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. Loading Loading @@ -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); } Loading @@ -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); Loading