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