blob: 0f5fcd8c5a000a814ee0d1ae6f17cc850023b2a8 [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
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
Christopher Tatede6f1312009-07-07 13:11:41 -070018
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;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080025
Bijan Amirzada41242f22014-03-21 12:12:18 -070026import com.android.browser.platformsupport.BrowserContract;
27import com.android.browser.platformsupport.BrowserContract.Bookmarks;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080028
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070029import android.util.Log;
Christopher Tatede6f1312009-07-07 13:11:41 -070030
31import java.io.DataInputStream;
32import java.io.DataOutputStream;
33import java.io.EOFException;
34import java.io.File;
35import java.io.FileInputStream;
36import java.io.FileOutputStream;
John Reckc168a362011-01-04 17:51:12 -080037import java.io.IOException;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070038import java.util.ArrayList;
Christopher Tatede6f1312009-07-07 13:11:41 -070039import java.util.zip.CRC32;
40
41/**
42 * Settings backup agent for the Android browser. Currently the only thing
43 * stored is the set of bookmarks. It's okay if I/O exceptions are thrown
44 * out of the agent; the calling code handles it and the backup operation
45 * simply fails.
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070046 *
47 * @hide
Christopher Tatede6f1312009-07-07 13:11:41 -070048 */
49public class BrowserBackupAgent extends BackupAgent {
Christopher Tatef8b59982009-09-29 12:40:25 -070050 static final String TAG = "BrowserBackupAgent";
51 static final boolean DEBUG = false;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070052
Christopher Tatede6f1312009-07-07 13:11:41 -070053 static final String BOOKMARK_KEY = "_bookmarks_";
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070054 /** this version num MUST be incremented if the flattened-file schema ever changes */
55 static final int BACKUP_AGENT_VERSION = 0;
Christopher Tatede6f1312009-07-07 13:11:41 -070056
57 /**
John Reckc168a362011-01-04 17:51:12 -080058 * This simply preserves the existing state as we now prefer Chrome Sync
59 * to handle bookmark backup.
Christopher Tatede6f1312009-07-07 13:11:41 -070060 */
61 @Override
62 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
63 ParcelFileDescriptor newState) throws IOException {
64 long savedFileSize = -1;
65 long savedCrc = -1;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070066 int savedVersion = -1;
Christopher Tatede6f1312009-07-07 13:11:41 -070067
68 // Extract the previous bookmark file size & CRC from the saved state
69 DataInputStream in = new DataInputStream(
70 new FileInputStream(oldState.getFileDescriptor()));
71 try {
72 savedFileSize = in.readLong();
73 savedCrc = in.readLong();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070074 savedVersion = in.readInt();
Christopher Tatede6f1312009-07-07 13:11:41 -070075 } catch (EOFException e) {
76 // It means we had no previous state; that's fine
John Reckc168a362011-01-04 17:51:12 -080077 return;
Henrik Baard37550402010-04-21 12:36:33 +020078 } finally {
79 if (in != null) {
80 in.close();
81 }
Christopher Tatede6f1312009-07-07 13:11:41 -070082 }
John Reckc168a362011-01-04 17:51:12 -080083 // Write the existing state
84 writeBackupState(savedFileSize, savedCrc, newState);
Christopher Tatede6f1312009-07-07 13:11:41 -070085 }
86
87 /**
88 * Restore from backup -- reads in the flattened bookmark file as supplied from
89 * the backup service, parses that out, and rebuilds the bookmarks table in the
90 * browser database from it.
91 */
92 @Override
93 public void onRestore(BackupDataInput data, int appVersionCode,
94 ParcelFileDescriptor newState) throws IOException {
95 long crc = -1;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -070096 File tmpfile = File.createTempFile("rst", null, getFilesDir());
Christopher Tatede6f1312009-07-07 13:11:41 -070097 try {
98 while (data.readNextHeader()) {
99 if (BOOKMARK_KEY.equals(data.getKey())) {
100 // Read the flattened bookmark data into a temp file
101 crc = copyBackupToFile(data, tmpfile, data.getDataSize());
102
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700103 FileInputStream infstream = new FileInputStream(tmpfile);
104 DataInputStream in = new DataInputStream(infstream);
105
106 try {
107 int count = in.readInt();
108 ArrayList<Bookmark> bookmarks = new ArrayList<Bookmark>(count);
109
110 // Read all the bookmarks, then process later -- if we can't read
111 // all the data successfully, we don't touch the bookmarks table
112 for (int i = 0; i < count; i++) {
113 Bookmark mark = new Bookmark();
114 mark.url = in.readUTF();
115 mark.visits = in.readInt();
116 mark.date = in.readLong();
117 mark.created = in.readLong();
118 mark.title = in.readUTF();
119 bookmarks.add(mark);
120 }
121
122 // Okay, we have all the bookmarks -- now see if we need to add
123 // them to the browser's database
124 int N = bookmarks.size();
Christopher Tatef8b59982009-09-29 12:40:25 -0700125 int nUnique = 0;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700126 if (DEBUG) Log.v(TAG, "Restoring " + N + " bookmarks");
John Reckc168a362011-01-04 17:51:12 -0800127 String[] urlCol = new String[] { Bookmarks.URL };
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700128 for (int i = 0; i < N; i++) {
129 Bookmark mark = bookmarks.get(i);
130
131 // Does this URL exist in the bookmark table?
John Reckc168a362011-01-04 17:51:12 -0800132 Cursor cursor = getContentResolver().query(
133 Bookmarks.CONTENT_URI, urlCol,
134 Bookmarks.URL + " == ?",
135 new String[] { mark.url }, null);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700136 // if not, insert it
137 if (cursor.getCount() <= 0) {
Christopher Tatef8b59982009-09-29 12:40:25 -0700138 if (DEBUG) Log.v(TAG, "Did not see url: " + mark.url);
John Reckc168a362011-01-04 17:51:12 -0800139 addBookmark(mark);
Christopher Tatef8b59982009-09-29 12:40:25 -0700140 nUnique++;
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700141 } else {
Christopher Tatef8b59982009-09-29 12:40:25 -0700142 if (DEBUG) Log.v(TAG, "Skipping extant url: " + mark.url);
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700143 }
144 cursor.close();
145 }
Christopher Tatef8b59982009-09-29 12:40:25 -0700146 Log.i(TAG, "Restored " + nUnique + " of " + N + " bookmarks");
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700147 } catch (IOException ioe) {
148 Log.w(TAG, "Bad backup data; not restoring");
149 crc = -1;
Henrik Baard37550402010-04-21 12:36:33 +0200150 } finally {
151 if (in != null) {
152 in.close();
153 }
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700154 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700155 }
156
157 // Last, write the state we just restored from so we can discern
158 // changes whenever we get invoked for backup in the future
159 writeBackupState(tmpfile.length(), crc, newState);
160 }
161 } finally {
162 // Whatever happens, delete the temp file
163 tmpfile.delete();
164 }
165 }
166
John Reckc168a362011-01-04 17:51:12 -0800167 void addBookmark(Bookmark mark) {
168 ContentValues values = new ContentValues();
169 values.put(Bookmarks.TITLE, mark.title);
170 values.put(Bookmarks.URL, mark.url);
171 values.put(Bookmarks.IS_FOLDER, 0);
172 values.put(Bookmarks.DATE_CREATED, mark.created);
173 values.put(Bookmarks.DATE_MODIFIED, mark.date);
174 getContentResolver().insert(Bookmarks.CONTENT_URI, values);
175 }
176
Henrik Baard37550402010-04-21 12:36:33 +0200177 static class Bookmark {
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700178 public String url;
179 public int visits;
180 public long date;
181 public long created;
182 public String title;
183 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700184 /*
185 * Utility functions
186 */
187
Christopher Tatede6f1312009-07-07 13:11:41 -0700188 // Read the given file from backup to a file, calculating a CRC32 along the way
189 private long copyBackupToFile(BackupDataInput data, File file, int toRead)
190 throws IOException {
191 final int CHUNK = 8192;
192 byte[] buf = new byte[CHUNK];
193 CRC32 crc = new CRC32();
Christopher Tate9c0dd8c2009-07-10 17:51:48 -0700194 FileOutputStream out = new FileOutputStream(file);
Christopher Tatede6f1312009-07-07 13:11:41 -0700195
Henrik Baard37550402010-04-21 12:36:33 +0200196 try {
197 while (toRead > 0) {
198 int numRead = data.readEntityData(buf, 0, CHUNK);
199 crc.update(buf, 0, numRead);
200 out.write(buf, 0, numRead);
201 toRead -= numRead;
202 }
203 } finally {
204 if (out != null) {
205 out.close();
206 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700207 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700208 return crc.getValue();
209 }
210
211 // Write the given metrics to the new state file
212 private void writeBackupState(long fileSize, long crc, ParcelFileDescriptor stateFile)
213 throws IOException {
214 DataOutputStream out = new DataOutputStream(
215 new FileOutputStream(stateFile.getFileDescriptor()));
Henrik Baard37550402010-04-21 12:36:33 +0200216 try {
217 out.writeLong(fileSize);
218 out.writeLong(crc);
219 out.writeInt(BACKUP_AGENT_VERSION);
220 } finally {
221 if (out != null) {
222 out.close();
223 }
224 }
Christopher Tatede6f1312009-07-07 13:11:41 -0700225 }
226}