1 /*
2  * Copyright (C) 2010 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 package android.database;
17 
18 import android.database.sqlite.SQLiteDatabase;
19 import android.database.sqlite.SQLiteException;
20 import android.util.Log;
21 import android.util.Pair;
22 
23 import java.io.File;
24 import java.util.List;
25 
26 /**
27  * Default class used to define the action to take when database corruption is reported
28  * by sqlite.
29  * <p>
30  * An application can specify an implementation of {@link DatabaseErrorHandler} on the
31  * following:
32  * <ul>
33  *   <li>{@link SQLiteDatabase#openOrCreateDatabase(String,
34  *      android.database.sqlite.SQLiteDatabase.CursorFactory, DatabaseErrorHandler)}</li>
35  *   <li>{@link SQLiteDatabase#openDatabase(String,
36  *      android.database.sqlite.SQLiteDatabase.CursorFactory, int, DatabaseErrorHandler)}</li>
37  * </ul>
38  * The specified {@link DatabaseErrorHandler} is used to handle database corruption errors, if they
39  * occur.
40  * <p>
41  * If null is specified for the DatabaseErrorHandler param in the above calls, this class is used
42  * as the default {@link DatabaseErrorHandler}.
43  */
44 public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler {
45 
46     private static final String TAG = "DefaultDatabaseErrorHandler";
47 
48     /**
49      * defines the default method to be invoked when database corruption is detected.
50      * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
51      * is detected.
52      */
onCorruption(SQLiteDatabase dbObj)53     public void onCorruption(SQLiteDatabase dbObj) {
54         Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath());
55         SQLiteDatabase.wipeDetected(dbObj.getPath(), "corruption");
56 
57         // is the corruption detected even before database could be 'opened'?
58         if (!dbObj.isOpen()) {
59             // database files are not even openable. delete this database file.
60             // NOTE if the database has attached databases, then any of them could be corrupt.
61             // and not deleting all of them could cause corrupted database file to remain and
62             // make the application crash on database open operation. To avoid this problem,
63             // the application should provide its own {@link DatabaseErrorHandler} impl class
64             // to delete ALL files of the database (including the attached databases).
65             deleteDatabaseFile(dbObj.getPath());
66             return;
67         }
68 
69         List<Pair<String, String>> attachedDbs = null;
70         try {
71             // Close the database, which will cause subsequent operations to fail.
72             // before that, get the attached database list first.
73             try {
74                 attachedDbs = dbObj.getAttachedDbs();
75             } catch (SQLiteException e) {
76                 /* ignore */
77             }
78             try {
79                 dbObj.close();
80             } catch (SQLiteException e) {
81                 /* ignore */
82             }
83         } finally {
84             // Delete all files of this corrupt database and/or attached databases
85             if (attachedDbs != null) {
86                 for (Pair<String, String> p : attachedDbs) {
87                     deleteDatabaseFile(p.second);
88                 }
89             } else {
90                 // attachedDbs = null is possible when the database is so corrupt that even
91                 // "PRAGMA database_list;" also fails. delete the main database file
92                 deleteDatabaseFile(dbObj.getPath());
93             }
94         }
95     }
96 
deleteDatabaseFile(String fileName)97     private void deleteDatabaseFile(String fileName) {
98         if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
99             return;
100         }
101         Log.e(TAG, "deleting the database file: " + fileName);
102         try {
103             SQLiteDatabase.deleteDatabase(new File(fileName), /*removeCheckFile=*/ false);
104         } catch (Exception e) {
105             /* print warning and ignore exception */
106             Log.w(TAG, "delete failed: " + e.getMessage());
107         }
108     }
109 }
110