1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app.backup; 18 19 import java.io.FileDescriptor; 20 import java.io.IOException; 21 22 /** 23 * Provides the structured interface through which a {@link BackupAgent} reads 24 * information from the backup data set, via its 25 * {@link BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()} 26 * method. The data is presented as a set of "entities," each 27 * representing one named record as previously stored by the agent's 28 * {@link BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) 29 * onBackup()} implementation. An entity is composed of a descriptive header plus a 30 * byte array that holds the raw data saved in the remote backup. 31 * <p> 32 * The agent must consume every entity in the data stream, otherwise the 33 * restored state of the application will be incomplete. 34 * <h3>Example</h3> 35 * <p> 36 * A typical 37 * {@link BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) 38 * onRestore()} implementation might be structured something like this: 39 * <pre> 40 * public void onRestore(BackupDataInput data, int appVersionCode, 41 * ParcelFileDescriptor newState) { 42 * while (data.readNextHeader()) { 43 * String key = data.getKey(); 44 * int dataSize = data.getDataSize(); 45 * 46 * if (key.equals(MY_BACKUP_KEY_ONE)) { 47 * // process this kind of record here 48 * byte[] buffer = new byte[dataSize]; 49 * data.readEntityData(buffer, 0, dataSize); // reads the entire entity at once 50 * 51 * // now 'buffer' holds the raw data and can be processed however 52 * // the agent wishes 53 * processBackupKeyOne(buffer); 54 * } else if (key.equals(MY_BACKUP_KEY_TO_IGNORE) { 55 * // a key we recognize but wish to discard 56 * data.skipEntityData(); 57 * } // ... etc. 58 * } 59 * }</pre> 60 */ 61 public class BackupDataInput { 62 int mBackupReader; 63 64 private EntityHeader mHeader = new EntityHeader(); 65 private boolean mHeaderReady; 66 67 private static class EntityHeader { 68 String key; 69 int dataSize; 70 } 71 72 /** @hide */ BackupDataInput(FileDescriptor fd)73 public BackupDataInput(FileDescriptor fd) { 74 if (fd == null) throw new NullPointerException(); 75 mBackupReader = ctor(fd); 76 if (mBackupReader == 0) { 77 throw new RuntimeException("Native initialization failed with fd=" + fd); 78 } 79 } 80 81 /** @hide */ finalize()82 protected void finalize() throws Throwable { 83 try { 84 dtor(mBackupReader); 85 } finally { 86 super.finalize(); 87 } 88 } 89 90 /** 91 * Extract the next entity header from the restore stream. After this method 92 * return success, the {@link #getKey()} and {@link #getDataSize()} methods can 93 * be used to inspect the entity that is now available for processing. 94 * 95 * @return <code>true</code> when there is an entity ready for consumption from the 96 * restore stream, <code>false</code> if the restore stream has been fully consumed. 97 * @throws IOException if an error occurred while reading the restore stream 98 */ readNextHeader()99 public boolean readNextHeader() throws IOException { 100 int result = readNextHeader_native(mBackupReader, mHeader); 101 if (result == 0) { 102 // read successfully 103 mHeaderReady = true; 104 return true; 105 } else if (result > 0) { 106 // done 107 mHeaderReady = false; 108 return false; 109 } else { 110 // error 111 mHeaderReady = false; 112 throw new IOException("failed: 0x" + Integer.toHexString(result)); 113 } 114 } 115 116 /** 117 * Report the key associated with the current entity in the restore stream 118 * @return the current entity's key string 119 * @throws IllegalStateException if the next record header has not yet been read 120 */ getKey()121 public String getKey() { 122 if (mHeaderReady) { 123 return mHeader.key; 124 } else { 125 throw new IllegalStateException("Entity header not read"); 126 } 127 } 128 129 /** 130 * Report the size in bytes of the data associated with the current entity in the 131 * restore stream. 132 * 133 * @return The size of the record's raw data, in bytes 134 * @throws IllegalStateException if the next record header has not yet been read 135 */ getDataSize()136 public int getDataSize() { 137 if (mHeaderReady) { 138 return mHeader.dataSize; 139 } else { 140 throw new IllegalStateException("Entity header not read"); 141 } 142 } 143 144 /** 145 * Read a record's raw data from the restore stream. The record's header must first 146 * have been processed by the {@link #readNextHeader()} method. Multiple calls to 147 * this method may be made in order to process the data in chunks; not all of it 148 * must be read in a single call. Once all of the raw data for the current entity 149 * has been read, further calls to this method will simply return zero. 150 * 151 * @param data An allocated byte array of at least 'size' bytes 152 * @param offset Offset within the 'data' array at which the data will be placed 153 * when read from the stream 154 * @param size The number of bytes to read in this pass 155 * @return The number of bytes of data read. Once all of the data for this entity 156 * has been read, further calls to this method will return zero. 157 * @throws IOException if an error occurred when trying to read the restore data stream 158 */ readEntityData(byte[] data, int offset, int size)159 public int readEntityData(byte[] data, int offset, int size) throws IOException { 160 if (mHeaderReady) { 161 int result = readEntityData_native(mBackupReader, data, offset, size); 162 if (result >= 0) { 163 return result; 164 } else { 165 throw new IOException("result=0x" + Integer.toHexString(result)); 166 } 167 } else { 168 throw new IllegalStateException("Entity header not read"); 169 } 170 } 171 172 /** 173 * Consume the current entity's data without extracting it into a buffer 174 * for further processing. This allows a {@link android.app.backup.BackupAgent} to 175 * efficiently discard obsolete or otherwise uninteresting records during the 176 * restore operation. 177 * 178 * @throws IOException if an error occurred when trying to read the restore data stream 179 */ skipEntityData()180 public void skipEntityData() throws IOException { 181 if (mHeaderReady) { 182 skipEntityData_native(mBackupReader); 183 } else { 184 throw new IllegalStateException("Entity header not read"); 185 } 186 } 187 ctor(FileDescriptor fd)188 private native static int ctor(FileDescriptor fd); dtor(int mBackupReader)189 private native static void dtor(int mBackupReader); 190 readNextHeader_native(int mBackupReader, EntityHeader entity)191 private native int readNextHeader_native(int mBackupReader, EntityHeader entity); readEntityData_native(int mBackupReader, byte[] data, int offset, int size)192 private native int readEntityData_native(int mBackupReader, byte[] data, int offset, int size); skipEntityData_native(int mBackupReader)193 private native int skipEntityData_native(int mBackupReader); 194 } 195