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

Commit f7cb8a0b authored by Christopher Tate's avatar Christopher Tate
Browse files

Compress the preferred-app backup payloads

They can be Very Very Large, so take advantage of the new BlobBackupHelper
infrastructure to keep them tiny!

Also fix an issue in which the restore path wasn't properly passing
notification payloads along for processing, and an issue in which the
blob helper wasn't handling empty states properly.

Change-Id: I11a7ca3cd2e26f634a8971e874ac97385b0b500c
parent 34ea2cc2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -133,7 +133,7 @@ public abstract class BlobBackupHelper implements BackupHelper {

            out.writeInt(mCurrentBlobVersion);

            final int N = state.size();
            final int N = (state != null) ? state.size() : 0;
            out.writeInt(N);
            for (int i = 0; i < N; i++) {
                out.writeUTF(state.keyAt(i));
+30 −131
Original line number Diff line number Diff line
@@ -17,159 +17,58 @@
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.app.backup.BlobBackupHelper;
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 {
public class PreferredActivityBackupHelper extends BlobBackupHelper {
    private static final String TAG = "PreferredBackup";
    private static final boolean DEBUG = true;
    private static final boolean DEBUG = false;

    // current schema of the backup state blob
    private static final int STATE_VERSION = 1;
    private static final int STATE_VERSION = 2;

    // 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;
    public PreferredActivityBackupHelper() {
        super(STATE_VERSION, KEY_PREFERRED);
    }

    // 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);
        }
    @Override
    protected byte[] getBackupPayload(String key) {
        if (KEY_PREFERRED.equals(key)) {
            if (DEBUG) {
                Slog.v(TAG, "Checking whether to back up");
            }

    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;
            IPackageManager pm = AppGlobals.getPackageManager();
            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);
                    }
                return pm.getPreferredActivityBackup(UserHandle.USER_OWNER);
            } catch (Exception e) {
                Slog.e(TAG, "Unable to store backup payload", e);
                // fall through to report null state
            }
        } else {
                Slog.w(TAG, "Prior state from unrecognized version " + version);
            Slog.w(TAG, "Unexpected backup key " + key);
        }
        } 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;
        return null;
    }

    @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 {
    protected void applyRestoredPayload(String key, byte[] payload) {
        if (KEY_PREFERRED.equals(key)) {
            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);
        }
                Slog.v(TAG, "Restoring");
            }

    @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);
                Slog.e(TAG, "Unable to restore", e);
            }
        } else {
            Slog.w(TAG, "Unexpected restore key " + key);
        }

    @Override
    public void writeNewStateDescription(ParcelFileDescriptor newState) {
        writeState(newState, null);
    }

}
+3 −2
Original line number Diff line number Diff line
@@ -94,7 +94,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
        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));
        addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
        addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));

        super.onBackup(oldState, data, newState);
@@ -129,7 +129,8 @@ public class SystemBackupAgent extends BackupAgentHelper {
                new String[] { WALLPAPER_IMAGE_KEY} ));
        addHelper(RECENTS_HELPER, new RecentsBackupHelper(this));
        addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
        addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this));
        addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
        addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));

        try {
            super.onRestore(data, appVersionCode, newState);