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

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

Merge "Don't write widget metadata to backup unless it's new/changed" into lmp-mr1-dev

parents 92dd1abe cf962601
Loading
Loading
Loading
Loading
+80 −11
Original line number Diff line number Diff line
@@ -130,6 +130,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
@@ -2695,6 +2696,84 @@ public class BackupManagerService {
            }
        }

        // SHA-1 a byte array and return the result in hex
        private String SHA1Checksum(byte[] input) {
            final byte[] checksum;
            try {
                MessageDigest md = MessageDigest.getInstance("SHA-1");
                checksum = md.digest(input);
            } catch (NoSuchAlgorithmException e) {
                Slog.e(TAG, "Unable to use SHA-1!");
                return "00";
            }

            StringBuffer sb = new StringBuffer(checksum.length * 2);
            for (int i = 0; i < checksum.length; i++) {
                sb.append(Integer.toHexString(checksum[i]));
            }
            return sb.toString();
        }

        private void writeWidgetPayloadIfAppropriate(FileDescriptor fd, String pkgName)
                throws IOException {
            byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
                    UserHandle.USER_OWNER);
            // has the widget state changed since last time?
            final File widgetFile = new File(mStateDir, pkgName + "_widget");
            final boolean priorStateExists = widgetFile.exists();

            if (MORE_DEBUG) {
                if (priorStateExists || widgetState != null) {
                    Slog.i(TAG, "Checking widget update: state=" + (widgetState != null)
                            + " prior=" + priorStateExists);
                }
            }

            if (!priorStateExists && widgetState == null) {
                // no prior state, no new state => nothing to do
                return;
            }

            // if the new state is not null, we might need to compare checksums to
            // determine whether to update the widget blob in the archive.  If the
            // widget state *is* null, we know a priori at this point that we simply
            // need to commit a deletion for it.
            String newChecksum = null;
            if (widgetState != null) {
                newChecksum = SHA1Checksum(widgetState);
                if (priorStateExists) {
                    final String priorChecksum;
                    try (
                        FileInputStream fin = new FileInputStream(widgetFile);
                        DataInputStream in = new DataInputStream(fin)
                    ) {
                        priorChecksum = in.readUTF();
                    }
                    if (Objects.equals(newChecksum, priorChecksum)) {
                        // Same checksum => no state change => don't rewrite the widget data
                        return;
                    }
                }
            } // else widget state *became* empty, so we need to commit a deletion

            BackupDataOutput out = new BackupDataOutput(fd);
            if (widgetState != null) {
                try (
                    FileOutputStream fout = new FileOutputStream(widgetFile);
                    DataOutputStream stateOut = new DataOutputStream(fout)
                ) {
                    stateOut.writeUTF(newChecksum);
                }

                out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
                out.writeEntityData(widgetState, widgetState.length);
            } else {
                // Widget state for this app has been removed; commit a deletion
                out.writeEntityHeader(KEY_WIDGET_STATE, -1);
                widgetFile.delete();
            }
        }

        @Override
        public void operationComplete() {
            // Okay, the agent successfully reported back to us!
@@ -2733,17 +2812,7 @@ public class BackupManagerService {
                }

                // Piggyback the widget state payload, if any
                BackupDataOutput out = new BackupDataOutput(fd);
                byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
                        UserHandle.USER_OWNER);
                if (widgetState != null) {
                    out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
                    out.writeEntityData(widgetState, widgetState.length);
                } else {
                    // No widget state for this app, but push a 'delete' operation for it
                    // in case they're trying to play games with the payload.
                    out.writeEntityHeader(KEY_WIDGET_STATE, -1);
                }
                writeWidgetPayloadIfAppropriate(fd, pkgName);
            } catch (IOException e) {
                // Hard disk error; recovery/failure policy TBD.  For now roll back,
                // but we may want to consider this a transport-level failure (i.e.