Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 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 | |
Christopher Tate | 4528186 | 2010-03-05 15:46:30 -0800 | [diff] [blame] | 17 | package android.app.backup; |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 18 | |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 19 | import java.io.FileDescriptor; |
| 20 | import java.io.IOException; |
| 21 | |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 22 | /** |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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()} |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 26 | * method. The data is presented as a set of "entities," each |
| 27 | * representing one named record as previously stored by the agent's |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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. |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 31 | * <p> |
| 32 | * The agent must consume every entity in the data stream, otherwise the |
| 33 | * restored state of the application will be incomplete. |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 34 | * <h3>Example</h3> |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 35 | * <p> |
| 36 | * A typical |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 37 | * {@link BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) |
| 38 | * onRestore()} implementation might be structured something like this: |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 39 | * <pre> |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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(); |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 45 | * |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 50 | * |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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 | * } |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 59 | * }</pre> |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 60 | */ |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 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 | |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 72 | /** @hide */ |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 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 | |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 81 | /** @hide */ |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 82 | protected void finalize() throws Throwable { |
| 83 | try { |
| 84 | dtor(mBackupReader); |
| 85 | } finally { |
| 86 | super.finalize(); |
| 87 | } |
| 88 | } |
| 89 | |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 90 | /** |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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. |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 94 | * |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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. |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 97 | * @throws IOException if an error occurred while reading the restore stream |
| 98 | */ |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 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; |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 112 | throw new IOException("failed: 0x" + Integer.toHexString(result)); |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 113 | } |
| 114 | } |
| 115 | |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 116 | /** |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 117 | * Report the key associated with the current entity in the restore stream |
| 118 | * @return the current entity's key string |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 119 | * @throws IllegalStateException if the next record header has not yet been read |
| 120 | */ |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 121 | public String getKey() { |
| 122 | if (mHeaderReady) { |
| 123 | return mHeader.key; |
| 124 | } else { |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 125 | throw new IllegalStateException("Entity header not read"); |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 126 | } |
| 127 | } |
| 128 | |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 129 | /** |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 130 | * Report the size in bytes of the data associated with the current entity in the |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 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 | */ |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 136 | public int getDataSize() { |
| 137 | if (mHeaderReady) { |
| 138 | return mHeader.dataSize; |
| 139 | } else { |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 140 | throw new IllegalStateException("Entity header not read"); |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 141 | } |
| 142 | } |
| 143 | |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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. |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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. |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 157 | * @throws IOException if an error occurred when trying to read the restore data stream |
| 158 | */ |
Joe Onorato | 5f15d15 | 2009-06-16 16:31:35 -0400 | [diff] [blame] | 159 | public int readEntityData(byte[] data, int offset, int size) throws IOException { |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 160 | if (mHeaderReady) { |
Joe Onorato | 5f15d15 | 2009-06-16 16:31:35 -0400 | [diff] [blame] | 161 | int result = readEntityData_native(mBackupReader, data, offset, size); |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 162 | if (result >= 0) { |
| 163 | return result; |
| 164 | } else { |
| 165 | throw new IOException("result=0x" + Integer.toHexString(result)); |
| 166 | } |
| 167 | } else { |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 168 | throw new IllegalStateException("Entity header not read"); |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 169 | } |
| 170 | } |
| 171 | |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 172 | /** |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 173 | * Consume the current entity's data without extracting it into a buffer |
Christopher Tate | 4528186 | 2010-03-05 15:46:30 -0800 | [diff] [blame] | 174 | * for further processing. This allows a {@link android.app.backup.BackupAgent} to |
Christopher Tate | e28290e | 2010-02-16 15:22:26 -0800 | [diff] [blame] | 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 | */ |
Joe Onorato | 5f15d15 | 2009-06-16 16:31:35 -0400 | [diff] [blame] | 180 | public void skipEntityData() throws IOException { |
| 181 | if (mHeaderReady) { |
Joe Onorato | 9bb8fd7 | 2009-07-28 18:24:51 -0700 | [diff] [blame] | 182 | skipEntityData_native(mBackupReader); |
Joe Onorato | 5f15d15 | 2009-06-16 16:31:35 -0400 | [diff] [blame] | 183 | } else { |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 184 | throw new IllegalStateException("Entity header not read"); |
Joe Onorato | 5f15d15 | 2009-06-16 16:31:35 -0400 | [diff] [blame] | 185 | } |
| 186 | } |
| 187 | |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 188 | private native static int ctor(FileDescriptor fd); |
| 189 | private native static void dtor(int mBackupReader); |
| 190 | |
| 191 | private native int readNextHeader_native(int mBackupReader, EntityHeader entity); |
Joe Onorato | 5f15d15 | 2009-06-16 16:31:35 -0400 | [diff] [blame] | 192 | private native int readEntityData_native(int mBackupReader, byte[] data, int offset, int size); |
| 193 | private native int skipEntityData_native(int mBackupReader); |
Joe Onorato | 1cf5874 | 2009-06-12 11:06:24 -0700 | [diff] [blame] | 194 | } |