Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cd70621f authored by Christopher Tate's avatar Christopher Tate Committed by Android (Google) Code Review
Browse files

Merge "Back up / restore preferred app configuration"

parents 7cf828ca e012a235
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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.
+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;
+6 −0
Original line number Diff line number Diff line
@@ -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.
+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);
    }

}
+16 −10
Original line number Diff line number Diff line
@@ -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);
Loading