• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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