BackupDataInput.java revision 4e14a829129feee14ebe453f61a124784c870610
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 17package android.app.backup; 18 19import java.io.FileDescriptor; 20import java.io.IOException; 21 22/** 23 * BackupDataInput is the structured interface used for passing the contents of 24 * a backup data set to an application's {@link BackupAgent} class in its 25 * {@link BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)} 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(android.os.ParcelFileDescriptor, BackupDataOutput, android.os.ParcelFileDescriptor)} 29 * implementation. An entity is composed of a descriptive header plus a 30 * byte array that holds its raw data. 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 * <p> 35 * <b>Example</b> 36 * <p> 37 * A typical 38 * {@link BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) BackupAgent.onRestore(data, appVersionCode, newState)} 39 * implementation might be structured something like this: 40 * <pre> 41 * while (data.readNextHeader()) { 42 * String key = data.getKey(); 43 * int dataSize = data.getDataSize(); 44 * 45 * if (key.equals(MY_BACKUP_KEY_ONE)) { 46 * // process this kind of record here 47 * byte[] buffer = new byte[dataSize]; 48 * data.readEntityData(buffer, 0, dataSize); // reads the entire entity at once 49 * 50 * // now 'buffer' holds the raw data and can be processed however 51 * // the agent wishes 52 * processBackupKeyOne(buffer); 53 * } else if (key.equals(MY_BACKUP_KEY_TO_IGNORE) { 54 * // a key we recognize but wish to discard 55 * data.skipEntityData(); 56 * } // ... etc. 57 * }</pre> 58 */ 59public class BackupDataInput { 60 int mBackupReader; 61 62 private EntityHeader mHeader = new EntityHeader(); 63 private boolean mHeaderReady; 64 65 private static class EntityHeader { 66 String key; 67 int dataSize; 68 } 69 70 /** @hide */ 71 public BackupDataInput(FileDescriptor fd) { 72 if (fd == null) throw new NullPointerException(); 73 mBackupReader = ctor(fd); 74 if (mBackupReader == 0) { 75 throw new RuntimeException("Native initialization failed with fd=" + fd); 76 } 77 } 78 79 /** @hide */ 80 protected void finalize() throws Throwable { 81 try { 82 dtor(mBackupReader); 83 } finally { 84 super.finalize(); 85 } 86 } 87 88 /** 89 * Extract the next entity header from the restore stream. After this method 90 * return success, the {@link #getKey()} and {@link #getDataSize()} methods can 91 * be used to inspect the entity that is now available for processing. 92 * 93 * @return <code>true</code> when there is an entity ready for consumption from the 94 * restore stream, <code>false</code> if the restore stream has been fully consumed. 95 * @throws IOException if an error occurred while reading the restore stream 96 */ 97 public boolean readNextHeader() throws IOException { 98 int result = readNextHeader_native(mBackupReader, mHeader); 99 if (result == 0) { 100 // read successfully 101 mHeaderReady = true; 102 return true; 103 } else if (result > 0) { 104 // done 105 mHeaderReady = false; 106 return false; 107 } else { 108 // error 109 mHeaderReady = false; 110 throw new IOException("failed: 0x" + Integer.toHexString(result)); 111 } 112 } 113 114 /** 115 * Report the key associated with the current entity in the restore stream 116 * @return the current entity's key string 117 * @throws IllegalStateException if the next record header has not yet been read 118 */ 119 public String getKey() { 120 if (mHeaderReady) { 121 return mHeader.key; 122 } else { 123 throw new IllegalStateException("Entity header not read"); 124 } 125 } 126 127 /** 128 * Report the size in bytes of the data associated with the current entity in the 129 * restore stream. 130 * 131 * @return The size of the record's raw data, in bytes 132 * @throws IllegalStateException if the next record header has not yet been read 133 */ 134 public int getDataSize() { 135 if (mHeaderReady) { 136 return mHeader.dataSize; 137 } else { 138 throw new IllegalStateException("Entity header not read"); 139 } 140 } 141 142 /** 143 * Read a record's raw data from the restore stream. The record's header must first 144 * have been processed by the {@link #readNextHeader()} method. Multiple calls to 145 * this method may be made in order to process the data in chunks; not all of it 146 * must be read in a single call. Once all of the raw data for the current entity 147 * has been read, further calls to this method will simply return zero. 148 * 149 * @param data An allocated byte array of at least 'size' bytes 150 * @param offset Offset within the 'data' array at which the data will be placed 151 * when read from the stream 152 * @param size The number of bytes to read in this pass 153 * @return The number of bytes of data read. Once all of the data for this entity 154 * has been read, further calls to this method will return zero. 155 * @throws IOException if an error occurred when trying to read the restore data stream 156 */ 157 public int readEntityData(byte[] data, int offset, int size) throws IOException { 158 if (mHeaderReady) { 159 int result = readEntityData_native(mBackupReader, data, offset, size); 160 if (result >= 0) { 161 return result; 162 } else { 163 throw new IOException("result=0x" + Integer.toHexString(result)); 164 } 165 } else { 166 throw new IllegalStateException("Entity header not read"); 167 } 168 } 169 170 /** 171 * Consume the current entity's data without extracting it into a buffer 172 * for further processing. This allows a {@link android.app.backup.BackupAgent} to 173 * efficiently discard obsolete or otherwise uninteresting records during the 174 * restore operation. 175 * 176 * @throws IOException if an error occurred when trying to read the restore data stream 177 */ 178 public void skipEntityData() throws IOException { 179 if (mHeaderReady) { 180 skipEntityData_native(mBackupReader); 181 } else { 182 throw new IllegalStateException("Entity header not read"); 183 } 184 } 185 186 private native static int ctor(FileDescriptor fd); 187 private native static void dtor(int mBackupReader); 188 189 private native int readNextHeader_native(int mBackupReader, EntityHeader entity); 190 private native int readEntityData_native(int mBackupReader, byte[] data, int offset, int size); 191 private native int skipEntityData_native(int mBackupReader); 192} 193