Home | History | Annotate | Download | only in browser
      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 com.android.browser;
     18 
     19 import android.app.backup.BackupAgent;
     20 import android.app.backup.BackupDataInput;
     21 import android.app.backup.BackupDataOutput;
     22 import android.content.ContentValues;
     23 import android.database.Cursor;
     24 import android.os.ParcelFileDescriptor;
     25 import android.provider.BrowserContract;
     26 import android.provider.BrowserContract.Bookmarks;
     27 import android.util.Log;
     28 
     29 import java.io.DataInputStream;
     30 import java.io.DataOutputStream;
     31 import java.io.EOFException;
     32 import java.io.File;
     33 import java.io.FileInputStream;
     34 import java.io.FileOutputStream;
     35 import java.io.IOException;
     36 import java.util.ArrayList;
     37 import 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.
     44  *
     45  * @hide
     46  */
     47 public class BrowserBackupAgent extends BackupAgent {
     48     static final String TAG = "BrowserBackupAgent";
     49     static final boolean DEBUG = false;
     50 
     51     static final String BOOKMARK_KEY = "_bookmarks_";
     52     /** this version num MUST be incremented if the flattened-file schema ever changes */
     53     static final int BACKUP_AGENT_VERSION = 0;
     54 
     55     /**
     56      * This simply preserves the existing state as we now prefer Chrome Sync
     57      * to handle bookmark backup.
     58      */
     59     @Override
     60     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
     61             ParcelFileDescriptor newState) throws IOException {
     62         long savedFileSize = -1;
     63         long savedCrc = -1;
     64         int savedVersion = -1;
     65 
     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();
     72             savedVersion = in.readInt();
     73         } catch (EOFException e) {
     74             // It means we had no previous state; that's fine
     75             return;
     76         } finally {
     77             if (in != null) {
     78                 in.close();
     79             }
     80         }
     81         // Write the existing state
     82         writeBackupState(savedFileSize, savedCrc, newState);
     83     }
     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;
     94         File tmpfile = File.createTempFile("rst", null, getFilesDir());
     95         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 
    101                     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();
    123                         int nUnique = 0;
    124                         if (DEBUG) Log.v(TAG, "Restoring " + N + " bookmarks");
    125                         String[] urlCol = new String[] { Bookmarks.URL };
    126                         for (int i = 0; i < N; i++) {
    127                             Bookmark mark = bookmarks.get(i);
    128 
    129                             // Does this URL exist in the bookmark table?
    130                             Cursor cursor = getContentResolver().query(
    131                                     Bookmarks.CONTENT_URI, urlCol,
    132                                     Bookmarks.URL + " == ?",
    133                                     new String[] { mark.url }, null);
    134                             // if not, insert it
    135                             if (cursor.getCount() <= 0) {
    136                                 if (DEBUG) Log.v(TAG, "Did not see url: " + mark.url);
    137                                 addBookmark(mark);
    138                                 nUnique++;
    139                             } else {
    140                                 if (DEBUG) Log.v(TAG, "Skipping extant url: " + mark.url);
    141                             }
    142                             cursor.close();
    143                         }
    144                         Log.i(TAG, "Restored " + nUnique + " of " + N + " bookmarks");
    145                     } catch (IOException ioe) {
    146                         Log.w(TAG, "Bad backup data; not restoring");
    147                         crc = -1;
    148                     } finally {
    149                         if (in != null) {
    150                             in.close();
    151                         }
    152                     }
    153                 }
    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 
    165     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 
    175     static class Bookmark {
    176         public String url;
    177         public int visits;
    178         public long date;
    179         public long created;
    180         public String title;
    181     }
    182     /*
    183      * Utility functions
    184      */
    185 
    186     // 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();
    192         FileOutputStream out = new FileOutputStream(file);
    193 
    194         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             }
    205         }
    206         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()));
    214         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         }
    223     }
    224 }
    225