blob: 295047429532469d8ab20f76dfc4d33bab8c6a61 [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
Christopher Tateb6a65442010-03-05 15:47:48 -080019import android.app.backup.BackupAgent;
20import android.app.backup.BackupDataInput;
21import android.app.backup.BackupDataOutput;
John Reckc168a362011-01-04 17:51:12 -080022import android.content.ContentValues;
Christopher Tatede6f1312009-07-07 13:11:41 -070023import android.database.Cursor;
24import android.os.ParcelFileDescriptor;
John Reckc168a362011-01-04 17:51:12 -080025import android.provider.BrowserContract;
26import android.provider.BrowserContract.Bookmarks;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070027import android.util.Log;
Christopher Tatede6f1312009-07-07 13:11:41 -070028
29import java.io.DataInputStream;
30import java.io.DataOutputStream;
31import java.io.EOFException;
32import java.io.File;
33import java.io.FileInputStream;
34import java.io.FileOutputStream;
John Reckc168a362011-01-04 17:51:12 -080035import java.io.IOException;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070036import java.util.ArrayList;
Christopher Tatede6f1312009-07-07 13:11:41 -070037import java.util.zip.CRC32;
38
39/**
40 * Settings backup agent for the Android browser. Currently the only thing
41 * stored is the set of bookmarks. It's okay if I/O exceptions are thrown
42 * out of the agent; the calling code handles it and the backup operation
43 * simply fails.
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070044 *
45 * @hide
Christopher Tatede6f1312009-07-07 13:11:41 -070046 */
47public class BrowserBackupAgent extends BackupAgent {
Christopher Tatef8b59982009-09-29 12:40:25 -070048 static final String TAG = "BrowserBackupAgent";
49 static final boolean DEBUG = false;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070050
Christopher Tatede6f1312009-07-07 13:11:41 -070051 static final String BOOKMARK_KEY = "_bookmarks_";
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070052 /** this version num MUST be incremented if the flattened-file schema ever changes */
53 static final int BACKUP_AGENT_VERSION = 0;
Christopher Tatede6f1312009-07-07 13:11:41 -070054
55 /**
John Reckc168a362011-01-04 17:51:12 -080056 * This simply preserves the existing state as we now prefer Chrome Sync
57 * to handle bookmark backup.
Christopher Tatede6f1312009-07-07 13:11:41 -070058 */
59 @Override
60 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
61 ParcelFileDescriptor newState) throws IOException {
62 long savedFileSize = -1;
63 long savedCrc = -1;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070064 int savedVersion = -1;
Christopher Tatede6f1312009-07-07 13:11:41 -070065
66 // Extract the previous bookmark file size & CRC from the saved state
67 DataInputStream in = new DataInputStream(
68 new FileInputStream(oldState.getFileDescriptor()));
69 try {
70 savedFileSize = in.readLong();
71 savedCrc = in.readLong();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070072 savedVersion = in.readInt();
Christopher Tatede6f1312009-07-07 13:11:41 -070073 } catch (EOFException e) {
74 // It means we had no previous state; that's fine
John Reckc168a362011-01-04 17:51:12 -080075 return;
Henrik Baard37550402010-04-21 12:36:33 +020076 } finally {
77 if (in != null) {
78 in.close();
79 }
Christopher Tatede6f1312009-07-07 13:11:41 -070080 }
John Reckc168a362011-01-04 17:51:12 -080081 // Write the existing state
82 writeBackupState(savedFileSize, savedCrc, newState);
Christopher Tatede6f1312009-07-07 13:11:41 -070083 }
84
85 /**
86 * Restore from backup -- reads in the flattened bookmark file as supplied from
87 * the backup service, parses that out, and rebuilds the bookmarks table in the
88 * browser database from it.
89 */
90 @Override
91 public void onRestore(BackupDataInput data, int appVersionCode,
92 ParcelFileDescriptor newState) throws IOException {
93 long crc = -1;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070094 File tmpfile = File.createTempFile("rst", null, getFilesDir());
Christopher Tatede6f1312009-07-07 13:11:41 -070095 try {
96 while (data.readNextHeader()) {
97 if (BOOKMARK_KEY.equals(data.getKey())) {
98 // Read the flattened bookmark data into a temp file
99 crc = copyBackupToFile(data, tmpfile, data.getDataSize());
100
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700101 FileInputStream infstream = new FileInputStream(tmpfile);
102 DataInputStream in = new DataInputStream(infstream);
103
104 try {
105 int count = in.readInt();
106 ArrayList<Bookmark> bookmarks = new ArrayList<Bookmark>(count);
107
108 // Read all the bookmarks, then process later -- if we can't read
109 // all the data successfully, we don't touch the bookmarks table
110 for (int i = 0; i < count; i++) {
111 Bookmark mark = new Bookmark();
112 mark.url = in.readUTF();
113 mark.visits = in.readInt();
114 mark.date = in.readLong();
115 mark.created = in.readLong();
116 mark.title = in.readUTF();
117 bookmarks.add(mark);
118 }
119
120 // Okay, we have all the bookmarks -- now see if we need to add
121 // them to the browser's database
122 int N = bookmarks.size();
Christopher Tatef8b59982009-09-29 12:40:25 -0700123 int nUnique = 0;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700124 if (DEBUG) Log.v(TAG, "Restoring " + N + " bookmarks");
John Reckc168a362011-01-04 17:51:12 -0800125 String[] urlCol = new String[] { Bookmarks.URL };
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700126 for (int i = 0; i < N; i++) {
127 Bookmark mark = bookmarks.get(i);
128
129 // Does this URL exist in the bookmark table?
John Reckc168a362011-01-04 17:51:12 -0800130 Cursor cursor = getContentResolver().query(
131 Bookmarks.CONTENT_URI, urlCol,
132 Bookmarks.URL + " == ?",
133 new String[] { mark.url }, null);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700134 // if not, insert it
135 if (cursor.getCount() <= 0) {
Christopher Tatef8b59982009-09-29 12:40:25 -0700136 if (DEBUG) Log.v(TAG, "Did not see url: " + mark.url);
John Reckc168a362011-01-04 17:51:12 -0800137 addBookmark(mark);
Christopher Tatef8b59982009-09-29 12:40:25 -0700138 nUnique++;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700139 } else {
Christopher Tatef8b59982009-09-29 12:40:25 -0700140 if (DEBUG) Log.v(TAG, "Skipping extant url: " + mark.url);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700141 }
142 cursor.close();
143 }
Christopher Tatef8b59982009-09-29 12:40:25 -0700144 Log.i(TAG, "Restored " + nUnique + " of " + N + " bookmarks");
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700145 } catch (IOException ioe) {
146 Log.w(TAG, "Bad backup data; not restoring");
147 crc = -1;
Henrik Baard37550402010-04-21 12:36:33 +0200148 } finally {
149 if (in != null) {
150 in.close();
151 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700152 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700153 }
154
155 // Last, write the state we just restored from so we can discern
156 // changes whenever we get invoked for backup in the future
157 writeBackupState(tmpfile.length(), crc, newState);
158 }
159 } finally {
160 // Whatever happens, delete the temp file
161 tmpfile.delete();
162 }
163 }
164
John Reckc168a362011-01-04 17:51:12 -0800165 void addBookmark(Bookmark mark) {
166 ContentValues values = new ContentValues();
167 values.put(Bookmarks.TITLE, mark.title);
168 values.put(Bookmarks.URL, mark.url);
169 values.put(Bookmarks.IS_FOLDER, 0);
170 values.put(Bookmarks.DATE_CREATED, mark.created);
171 values.put(Bookmarks.DATE_MODIFIED, mark.date);
172 getContentResolver().insert(Bookmarks.CONTENT_URI, values);
173 }
174
Henrik Baard37550402010-04-21 12:36:33 +0200175 static class Bookmark {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700176 public String url;
177 public int visits;
178 public long date;
179 public long created;
180 public String title;
181 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700182 /*
183 * Utility functions
184 */
185
Christopher Tatede6f1312009-07-07 13:11:41 -0700186 // Read the given file from backup to a file, calculating a CRC32 along the way
187 private long copyBackupToFile(BackupDataInput data, File file, int toRead)
188 throws IOException {
189 final int CHUNK = 8192;
190 byte[] buf = new byte[CHUNK];
191 CRC32 crc = new CRC32();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700192 FileOutputStream out = new FileOutputStream(file);
Christopher Tatede6f1312009-07-07 13:11:41 -0700193
Henrik Baard37550402010-04-21 12:36:33 +0200194 try {
195 while (toRead > 0) {
196 int numRead = data.readEntityData(buf, 0, CHUNK);
197 crc.update(buf, 0, numRead);
198 out.write(buf, 0, numRead);
199 toRead -= numRead;
200 }
201 } finally {
202 if (out != null) {
203 out.close();
204 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700205 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700206 return crc.getValue();
207 }
208
209 // Write the given metrics to the new state file
210 private void writeBackupState(long fileSize, long crc, ParcelFileDescriptor stateFile)
211 throws IOException {
212 DataOutputStream out = new DataOutputStream(
213 new FileOutputStream(stateFile.getFileDescriptor()));
Henrik Baard37550402010-04-21 12:36:33 +0200214 try {
215 out.writeLong(fileSize);
216 out.writeLong(crc);
217 out.writeInt(BACKUP_AGENT_VERSION);
218 } finally {
219 if (out != null) {
220 out.close();
221 }
222 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700223 }
224}