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