blob: e38ef054c760a38568cbaa546cfb723c9d036c61 [file] [log] [blame]
Christopher Tatede6f1312009-07-07 13:11:41 -07001/*
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 com.android.browser;
18
19import java.io.IOException;
20
21import android.app.BackupAgent;
22import android.backup.BackupDataInput;
23import android.backup.BackupDataOutput;
24import android.database.Cursor;
25import android.os.ParcelFileDescriptor;
26import android.provider.Browser;
27import android.provider.Browser.BookmarkColumns;
28
29import java.io.DataInputStream;
30import java.io.DataOutputStream;
31import java.io.EOFException;
32import java.io.File;
33import java.io.FileInputStream;
34import java.io.FileOutputStream;
35
36import java.util.zip.CRC32;
37
38/**
39 * Settings backup agent for the Android browser. Currently the only thing
40 * stored is the set of bookmarks. It's okay if I/O exceptions are thrown
41 * out of the agent; the calling code handles it and the backup operation
42 * simply fails.
43 */
44public class BrowserBackupAgent extends BackupAgent {
45 static final String BOOKMARK_KEY = "_bookmarks_";
46
47 /**
48 * In order to determine whether the bookmark set has changed since the
49 * last time we did a backup, we store the following bits of info in the
50 * state file after a backup:
51 *
52 * 1. the size of the flattened bookmark file
53 * 2. the CRC32 of that file
54 *
55 * After we flatten the bookmarks file here in onBackup, we compare its
56 * metrics with the values from the saved state. If they match, it means
57 * the bookmarks didn't really change and we don't need to send the data.
58 * (If they don't match, of course, then they've changed and we do indeed
59 * send the new flattened file to be backed up.)
60 */
61 @Override
62 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
63 ParcelFileDescriptor newState) throws IOException {
64 long savedFileSize = -1;
65 long savedCrc = -1;
66
67 // Extract the previous bookmark file size & CRC from the saved state
68 DataInputStream in = new DataInputStream(
69 new FileInputStream(oldState.getFileDescriptor()));
70 try {
71 savedFileSize = in.readLong();
72 savedCrc = in.readLong();
73 } catch (EOFException e) {
74 // It means we had no previous state; that's fine
75 }
76
77 // TODO: BUILD THE FLATTENED BOOKMARK FILE FROM THE DB (into tmpfile)
78 File tmpfile = getFilesDir().createTempFile("bkp", null);
79 CRC32 crc = new CRC32();
80 try {
81 Cursor cursor = getContentResolver().query(Browser.BOOKMARKS_URI,
82 new String[] { BookmarkColumns.URL, BookmarkColumns.VISITS,
83 BookmarkColumns.DATE, BookmarkColumns.CREATED,
84 BookmarkColumns.TITLE },
85 BookmarkColumns.BOOKMARK + " == 1 ", null, null);
86 int count = cursor.getCount();
87 FileOutputStream out = new FileOutputStream(tmpfile);
88 for (int i = 0; i < count; i++) {
89 StringBuilder sb = new StringBuilder();
90 // URL
91 sb.append("'");
92 sb.append(cursor.getString(0));
93 sb.append("','");
94 // VISITS
95 sb.append(cursor.getInt(1));
96 sb.append("','");
97 // DATE
98 sb.append(cursor.getLong(2));
99 sb.append("','");
100 // CREATED
101 sb.append(cursor.getLong(3));
102 sb.append("','");
103 // TITLE
104 sb.append(cursor.getString(4));
105 sb.append("'");
106 out.write(sb.toString().getBytes());
107 }
108 out.close();
109 /*
110 android.util.Log.d("s", "backing up data" +
111 getContentResolver().openFileDescriptor(Browser.BOOKMARKS_URI, "r").toString());
112 */
113 // NOTE: feed the flattened data through the crc engine on the fly
114 // to save re-reading it later just to checksum it
115
116 // Once the file is built, compare its metrics with the saved ones
117 if ((crc.getValue() != savedCrc) || (tmpfile.length() != savedFileSize)) {
118 // Different checksum or different size, so we need to back it up
119 copyFileToBackup(BOOKMARK_KEY, tmpfile, data);
120 }
121
122 // Last, record the metrics of the bookmark file that we just stored
123 writeBackupState(tmpfile.length(), crc.getValue(), newState);
124 } finally {
125 // Make sure to tidy up when we're done
126 tmpfile.delete();
127 }
128 }
129
130 /**
131 * Restore from backup -- reads in the flattened bookmark file as supplied from
132 * the backup service, parses that out, and rebuilds the bookmarks table in the
133 * browser database from it.
134 */
135 @Override
136 public void onRestore(BackupDataInput data, int appVersionCode,
137 ParcelFileDescriptor newState) throws IOException {
138 long crc = -1;
139 File tmpfile = getFilesDir().createTempFile("rst", null);
140 try {
141 while (data.readNextHeader()) {
142 if (BOOKMARK_KEY.equals(data.getKey())) {
143 // Read the flattened bookmark data into a temp file
144 crc = copyBackupToFile(data, tmpfile, data.getDataSize());
145
146 // TODO: READ THE FLAT BOOKMARKS FILE 'tmpfile' AND REBUILD THE DB TABLE
147 }
148
149 // Last, write the state we just restored from so we can discern
150 // changes whenever we get invoked for backup in the future
151 writeBackupState(tmpfile.length(), crc, newState);
152 }
153 } finally {
154 // Whatever happens, delete the temp file
155 tmpfile.delete();
156 }
157 }
158
159 /*
160 * Utility functions
161 */
162
163 // Write the file to backup as a single record under the given key
164 private void copyFileToBackup(String key, File file, BackupDataOutput data)
165 throws IOException {
166 final int CHUNK = 8192;
167 byte[] buf = new byte[CHUNK];
168
169 int toCopy = (int) file.length();
170 data.writeEntityHeader(key, toCopy);
171
172 FileInputStream in = new FileInputStream(file);
173 int nRead;
174 while (toCopy > 0) {
175 nRead = in.read(buf, 0, CHUNK);
176 data.writeEntityData(buf, nRead);
177 toCopy -= nRead;
178 }
179 in.close();
180 }
181
182 // Read the given file from backup to a file, calculating a CRC32 along the way
183 private long copyBackupToFile(BackupDataInput data, File file, int toRead)
184 throws IOException {
185 final int CHUNK = 8192;
186 byte[] buf = new byte[CHUNK];
187 CRC32 crc = new CRC32();
188
189 while (toRead > 0) {
190 int numRead = data.readEntityData(buf, 0, CHUNK);
191 crc.update(buf, 0, numRead);
192 toRead -= numRead;
193 }
194
195 return crc.getValue();
196 }
197
198 // Write the given metrics to the new state file
199 private void writeBackupState(long fileSize, long crc, ParcelFileDescriptor stateFile)
200 throws IOException {
201 DataOutputStream out = new DataOutputStream(
202 new FileOutputStream(stateFile.getFileDescriptor()));
203 out.writeLong(fileSize);
204 out.writeLong(crc);
205 }
206}