11c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate/* 21c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * Copyright (C) 2010 The Android Open Source Project 31c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * 41c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * Licensed under the Apache License, Version 2.0 (the "License"); 51c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * you may not use this file except in compliance with the License. 61c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * You may obtain a copy of the License at 71c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * 81c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * http://www.apache.org/licenses/LICENSE-2.0 91c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * 101c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * Unless required by applicable law or agreed to in writing, software 111c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * distributed under the License is distributed on an "AS IS" BASIS, 121c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * See the License for the specific language governing permissions and 141c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * limitations under the License. 151c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate */ 161c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 171c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tatepackage com.example.android.backuprestore; 181c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 191c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport java.io.ByteArrayOutputStream; 201c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport java.io.ByteArrayInputStream; 211c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport java.io.DataInputStream; 221c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport java.io.DataOutputStream; 231c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport java.io.File; 241c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport java.io.FileInputStream; 251c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport java.io.FileOutputStream; 261c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport java.io.IOException; 271c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport java.io.RandomAccessFile; 281c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 291c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport android.app.backup.BackupAgent; 301c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport android.app.backup.BackupDataInput; 311c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport android.app.backup.BackupDataOutput; 321c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tateimport android.os.ParcelFileDescriptor; 331c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 341c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate/** 351c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * This agent implementation is similar to the {@link ExampleAgent} one, but 361c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * stores each distinct piece of application data in a separate record within 371c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * the backup data set. These records are updated independently: if the user 381c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * changes the state of one of the UI's checkboxes, for example, only that 391c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * datum's backup record is updated, not the entire data file. 401c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate */ 411c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tatepublic class MultiRecordExampleAgent extends BackupAgent { 421c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Key strings for each record in the backup set 431c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate static final String FILLING_KEY = "filling"; 441c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate static final String MAYO_KEY = "mayo"; 451c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate static final String TOMATO_KEY = "tomato"; 461c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 471c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Current live data, read from the application's data file 481c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate int mFilling; 491c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate boolean mAddMayo; 501c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate boolean mAddTomato; 511c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 521c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate /** The location of the application's persistent data file */ 531c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate File mDataFile; 541c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 551c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate @Override 561c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate public void onCreate() { 571c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Cache a File for the app's data 581c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate mDataFile = new File(getFilesDir(), BackupRestoreActivity.DATA_FILE_NAME); 591c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 601c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 611c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate @Override 621c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, 631c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate ParcelFileDescriptor newState) throws IOException { 641c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // First, get the current data from the application's file. This 651c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // may throw an IOException, but in that case something has gone 661c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // badly wrong with the app's data on disk, and we do not want 671c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // to back up garbage data. If we just let the exception go, the 681c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Backup Manager will handle it and simply skip the current 691c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // backup operation. 701c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate synchronized (BackupRestoreActivity.sDataLock) { 711c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate RandomAccessFile file = new RandomAccessFile(mDataFile, "r"); 721c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate mFilling = file.readInt(); 731c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate mAddMayo = file.readBoolean(); 741c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate mAddTomato = file.readBoolean(); 751c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 761c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 771c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // If this is the first backup ever, we have to back up everything 781c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate boolean forceBackup = (oldState == null); 791c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 801c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Now read the state as of the previous backup pass, if any 811c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate int lastFilling = 0; 821c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate boolean lastMayo = false; 831c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate boolean lastTomato = false; 841c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 851c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate if (!forceBackup) { 861c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 871c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate FileInputStream instream = new FileInputStream(oldState.getFileDescriptor()); 881c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate DataInputStream in = new DataInputStream(instream); 891c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 901c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate try { 911c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Read the state as of the last backup 921c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate lastFilling = in.readInt(); 931c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate lastMayo = in.readBoolean(); 941c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate lastTomato = in.readBoolean(); 951c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } catch (IOException e) { 961c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // If something went wrong reading the state file, be safe and 971c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // force a backup of all the data again. 981c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate forceBackup = true; 991c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1001c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1011c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1021c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Okay, now check each datum to see whether we need to back up a new value. We'll 1031c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // reuse the bytearray buffering stream for each datum. We also use a little 1041c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // helper routine to avoid some code duplication when writing the two boolean 1051c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // records. 1061c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); 1071c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate DataOutputStream out = new DataOutputStream(bufStream); 1081c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1091c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate if (forceBackup || (mFilling != lastFilling)) { 1101c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // bufStream.reset(); // not necessary the first time, but good to remember 1111c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate out.writeInt(mFilling); 1121c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate writeBackupEntity(data, bufStream, FILLING_KEY); 1131c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1141c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1151c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate if (forceBackup || (mAddMayo != lastMayo)) { 1161c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate bufStream.reset(); 1171c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate out.writeBoolean(mAddMayo); 1181c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate writeBackupEntity(data, bufStream, MAYO_KEY); 1191c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1201c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1211c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate if (forceBackup || (mAddTomato != lastTomato)) { 1221c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate bufStream.reset(); 1231c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate out.writeBoolean(mAddTomato); 1241c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate writeBackupEntity(data, bufStream, TOMATO_KEY); 1251c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1261c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1271c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Finally, write the state file that describes our data as of this backup pass 1281c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate writeStateFile(newState); 1291c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1301c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1311c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate /** 1321c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * Write out the new state file: the version number, followed by the 1331c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * three bits of data as we sent them off to the backup transport. 1341c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate */ 1351c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate void writeStateFile(ParcelFileDescriptor stateFile) throws IOException { 1361c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); 1371c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate DataOutputStream out = new DataOutputStream(outstream); 1381c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1391c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate out.writeInt(mFilling); 1401c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate out.writeBoolean(mAddMayo); 1411c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate out.writeBoolean(mAddTomato); 1421c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1431c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1441c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Helper: write the boolean 'value' as a backup record under the given 'key', 1451c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // reusing the given buffering stream & data writer objects to do so. 1461c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate void writeBackupEntity(BackupDataOutput data, ByteArrayOutputStream bufStream, String key) 1471c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate throws IOException { 1481c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate byte[] buf = bufStream.toByteArray(); 1491c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate data.writeEntityHeader(key, buf.length); 1501c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate data.writeEntityData(buf, buf.length); 1511c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1521c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1531c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate /** 1541c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * On restore, we pull the various bits of data out of the restore stream, 1551c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * then reconstruct the application's data file inside the shared lock. A 1561c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * restore data set will always be the full set of records supplied by the 1571c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate * application's backup operations. 1581c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate */ 1591c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate @Override 1601c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate public void onRestore(BackupDataInput data, int appVersionCode, 1611c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate ParcelFileDescriptor newState) throws IOException { 1621c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1631c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Consume the restore data set, remembering each bit of application state 1641c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // that we see along the way 1651c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate while (data.readNextHeader()) { 1661c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate String key = data.getKey(); 1671c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate int dataSize = data.getDataSize(); 1681c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1691c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // In this implementation, we trust that we won't see any record keys 1701c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // that we don't understand. Since we expect to handle them all, we 1711c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // go ahead and extract the data for each record before deciding how 1721c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // it will be handled. 1731c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate byte[] dataBuf = new byte[dataSize]; 1741c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate data.readEntityData(dataBuf, 0, dataSize); 1751c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate ByteArrayInputStream instream = new ByteArrayInputStream(dataBuf); 1761c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate DataInputStream in = new DataInputStream(instream); 1771c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1781c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate if (FILLING_KEY.equals(key)) { 1791c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate mFilling = in.readInt(); 1801c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } else if (MAYO_KEY.equals(key)) { 1811c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate mAddMayo = in.readBoolean(); 1821c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } else if (TOMATO_KEY.equals(key)) { 1831c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate mAddTomato = in.readBoolean(); 1841c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1851c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1861c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1871c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Now we're ready to write out a full new dataset for the application. Note that 1881c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // the restore process is intended to *replace* any existing or default data, so 1891c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // we can just go ahead and overwrite it all. 1901c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate synchronized (BackupRestoreActivity.sDataLock) { 1911c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate RandomAccessFile file = new RandomAccessFile(mDataFile, "rw"); 1921c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate file.setLength(0L); 1931c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate file.writeInt(mFilling); 1941c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate file.writeBoolean(mAddMayo); 1951c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate file.writeBoolean(mAddTomato); 1961c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 1971c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate 1981c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate // Finally, write the state file that describes our data as of this restore pass. 1991c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate writeStateFile(newState); 2001c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate } 2011c0a20aeeb089971a624c102435755ba9ae74aabChristopher Tate} 202