138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate/*
238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Copyright (C) 2011 The Android Open Source Project
338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate *
438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Licensed under the Apache License, Version 2.0 (the "License");
538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * you may not use this file except in compliance with the License.
638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * You may obtain a copy of the License at
738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate *
838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate *      http://www.apache.org/licenses/LICENSE-2.0
938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate *
1038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * Unless required by applicable law or agreed to in writing, software
1138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * distributed under the License is distributed on an "AS IS" BASIS,
1238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * See the License for the specific language governing permissions and
1438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * limitations under the License.
1538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */
1638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
1738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tatepackage com.android.hugebackup;
1838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
1938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.app.backup.BackupAgent;
2038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.app.backup.BackupDataInput;
2138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.app.backup.BackupDataOutput;
2238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport android.os.ParcelFileDescriptor;
2338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
2438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.ByteArrayInputStream;
2538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.ByteArrayOutputStream;
2638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.DataInputStream;
2738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.DataOutputStream;
2838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.File;
2938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.FileInputStream;
3038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.FileOutputStream;
3138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.IOException;
3238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tateimport java.io.RandomAccessFile;
3338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
3438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate/**
3538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * This is the backup/restore agent class for the BackupRestore sample
3638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * application.  This particular agent illustrates using the backup and
3738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate * restore APIs directly, without taking advantage of any helper classes.
3838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate */
3938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tatepublic class HugeAgent extends BackupAgent {
4038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    /**
4138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * We put a simple version number into the state files so that we can
4238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * tell properly how to read "old" versions if at some point we want
4338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * to change what data we back up and how we store the state blob.
4438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     */
4538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    static final int AGENT_VERSION = 1;
4638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
4738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    /**
4838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * Pick an arbitrary string to use as the "key" under which the
4938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * data is backed up.  This key identifies different data records
5038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * within this one application's data set.  Since we only maintain
5138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * one piece of data we don't need to distinguish, so we just pick
5238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * some arbitrary tag to use.
5338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     */
5438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    static final String APP_DATA_KEY = "alldata";
5538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    static final String HUGE_DATA_KEY = "colossus";
5638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
5738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    /** The app's current data, read from the live disk file */
5838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    boolean mAddMayo;
5938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    boolean mAddTomato;
6038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    int mFilling;
6138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
6238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    /** The location of the application's persistent data file */
6338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    File mDataFile;
6438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
6538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    /** For convenience, we set up the File object for the app's data on creation */
6638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    @Override
6738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    public void onCreate() {
6838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        mDataFile = new File(getFilesDir(), HugeBackupActivity.DATA_FILE_NAME);
6938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    }
7038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
7138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    /**
7238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * The set of data backed up by this application is very small: just
7338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * two booleans and an integer.  With such a simple dataset, it's
7438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * easiest to simply store a copy of the backed-up data as the state
7538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * blob describing the last dataset backed up.  The state file
7638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * contents can be anything; it is private to the agent class, and
7738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * is never stored off-device.
7838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     *
7938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * <p>One thing that an application may wish to do is tag the state
8038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * blob contents with a version number.  This is so that if the
8138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * application is upgraded, the next time it attempts to do a backup,
8238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * it can detect that the last backup operation was performed by an
8338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * older version of the agent, and might therefore require different
8438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * handling.
8538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     */
8638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    @Override
8738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
8838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            ParcelFileDescriptor newState) throws IOException {
8938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // First, get the current data from the application's file.  This
9038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // may throw an IOException, but in that case something has gone
9138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // badly wrong with the app's data on disk, and we do not want
9238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // to back up garbage data.  If we just let the exception go, the
9338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // Backup Manager will handle it and simply skip the current
9438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // backup operation.
9538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        synchronized (HugeBackupActivity.sDataLock) {
9638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            RandomAccessFile file = new RandomAccessFile(mDataFile, "r");
9738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            mFilling = file.readInt();
9838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            mAddMayo = file.readBoolean();
9938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            mAddTomato = file.readBoolean();
10038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        }
10138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
10238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // If the new state file descriptor is null, this is the first time
10338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // a backup is being performed, so we know we have to write the
10438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // data.  If there <em>is</em> a previous state blob, we want to
10538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // double check whether the current data is actually different from
10638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // our last backup, so that we can avoid transmitting redundant
10738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // data to the storage backend.
10838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        boolean doBackup = (oldState == null);
10938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        if (!doBackup) {
11038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            doBackup = compareStateFile(oldState);
11138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        }
11238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
11338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // If we decided that we do in fact need to write our dataset, go
11438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // ahead and do that.  The way this agent backs up the data is to
11538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // flatten it into a single buffer, then write that to the backup
11638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // transport under the single key string.
11738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        if (doBackup) {
11838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
11938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
12038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // We use a DataOutputStream to write structured data into
12138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // the buffering stream
12238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            DataOutputStream outWriter = new DataOutputStream(bufStream);
12338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            outWriter.writeInt(mFilling);
12438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            outWriter.writeBoolean(mAddMayo);
12538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            outWriter.writeBoolean(mAddTomato);
12638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
12738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // Okay, we've flattened the data for transmission.  Pull it
12838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // out of the buffering stream object and send it off.
12938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            byte[] buffer = bufStream.toByteArray();
13038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            int len = buffer.length;
13138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            data.writeEntityHeader(APP_DATA_KEY, len);
13238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            data.writeEntityData(buffer, len);
13338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
13438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // ***** pathological behavior *****
13538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // Now, in order to incur deliberate too-much-data failures,
13638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // try to back up 20 MB of data besides what we already pushed.
13738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            final int MEGABYTE = 1024*1024;
13838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            final int NUM_MEGS = 20;
13938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            buffer = new byte[MEGABYTE];
14038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            data.writeEntityHeader(HUGE_DATA_KEY, NUM_MEGS * MEGABYTE);
14138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            for (int i = 0; i < NUM_MEGS; i++) {
14238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                data.writeEntityData(buffer, MEGABYTE);
14338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            }
14438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        }
14538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
14638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // Finally, in all cases, we need to write the new state blob
14738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        writeStateFile(newState);
14838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    }
14938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
15038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    /**
15138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * Helper routine - read a previous state file and decide whether to
15238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * perform a backup based on its contents.
15338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     *
15438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * @return <code>true</code> if the application's data has changed since
15538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     *   the last backup operation; <code>false</code> otherwise.
15638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     */
15738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    boolean compareStateFile(ParcelFileDescriptor oldState) {
15838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        FileInputStream instream = new FileInputStream(oldState.getFileDescriptor());
15938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        DataInputStream in = new DataInputStream(instream);
16038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
16138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        try {
16238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            int stateVersion = in.readInt();
16338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            if (stateVersion > AGENT_VERSION) {
16438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // Whoops; the last version of the app that backed up
16538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // data on this device was <em>newer</em> than the current
16638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // version -- the user has downgraded.  That's problematic.
16738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // In this implementation, we recover by simply rewriting
16838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // the backup.
16938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                return true;
17038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            }
17138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
17238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // The state data we store is just a mirror of the app's data;
17338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // read it from the state file then return 'true' if any of
17438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // it differs from the current data.
17538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            int lastFilling = in.readInt();
17638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            boolean lastMayo = in.readBoolean();
17738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            boolean lastTomato = in.readBoolean();
17838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
17938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            return (lastFilling != mFilling)
18038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                    || (lastTomato != mAddTomato)
18138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                    || (lastMayo != mAddMayo);
18238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        } catch (IOException e) {
18338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // If something went wrong reading the state file, be safe
18438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            // and back up the data again.
18538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            return true;
18638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        }
18738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    }
18838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
18938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    /**
19038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * Write out the new state file:  the version number, followed by the
19138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * three bits of data as we sent them off to the backup transport.
19238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     */
19338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    void writeStateFile(ParcelFileDescriptor stateFile) throws IOException {
19438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
19538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        DataOutputStream out = new DataOutputStream(outstream);
19638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
19738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        out.writeInt(AGENT_VERSION);
19838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        out.writeInt(mFilling);
19938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        out.writeBoolean(mAddMayo);
20038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        out.writeBoolean(mAddTomato);
20138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    }
20238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
20338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    /**
20438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * This application does not do any "live" restores of its own data,
20538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * so the only time a restore will happen is when the application is
20638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * installed.  This means that the activity itself is not going to
20738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * be running while we change its data out from under it.  That, in
20838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * turn, means that there is no need to send out any sort of notification
20938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * of the new data:  we only need to read the data from the stream
21038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * provided here, build the application's new data file, and then
21138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * write our new backup state blob that will be consulted at the next
21238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * backup operation.
21338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     *
21438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * <p>We don't bother checking the versionCode of the app who originated
21538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * the data because we have never revised the backup data format.  If
21638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * we had, the 'appVersionCode' parameter would tell us how we should
21738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     * interpret the data we're about to read.
21838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate     */
21938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    @Override
22038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    public void onRestore(BackupDataInput data, int appVersionCode,
22138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            ParcelFileDescriptor newState) throws IOException {
22238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // We should only see one entity in the data stream, but the safest
22338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // way to consume it is using a while() loop
22438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        while (data.readNextHeader()) {
22538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            String key = data.getKey();
22638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            int dataSize = data.getDataSize();
22738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
22838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            if (APP_DATA_KEY.equals(key)) {
22938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // It's our saved data, a flattened chunk of data all in
23038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // one buffer.  Use some handy structured I/O classes to
23138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // extract it.
23238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                byte[] dataBuf = new byte[dataSize];
23338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                data.readEntityData(dataBuf, 0, dataSize);
23438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
23538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                DataInputStream in = new DataInputStream(baStream);
23638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
23738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                mFilling = in.readInt();
23838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                mAddMayo = in.readBoolean();
23938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                mAddTomato = in.readBoolean();
24038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
24138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // Now we are ready to construct the app's data file based
24238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // on the data we are restoring from.
24338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                synchronized (HugeBackupActivity.sDataLock) {
24438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                    RandomAccessFile file = new RandomAccessFile(mDataFile, "rw");
24538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                    file.setLength(0L);
24638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                    file.writeInt(mFilling);
24738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                    file.writeBoolean(mAddMayo);
24838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                    file.writeBoolean(mAddTomato);
24938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                }
25038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            } else {
25138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // Curious!  This entity is data under a key we do not
25238507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                // understand how to process.  Just skip it.
25338507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate                data.skipEntityData();
25438507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate            }
25538507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        }
25638507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate
25738507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // The last thing to do is write the state blob that describes the
25838507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        // app's data as restored from backup.
25938507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate        writeStateFile(newState);
26038507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate    }
26138507bb993239a4d8135c6cc253187efe6e976fcChristopher Tate}
262