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 java.io.File;
19 import java.util.List;
20 
21 import android.database.sqlite.SQLiteDatabase;
22 import android.database.sqlite.SQLiteException;
23 import android.util.Log;
24 import android.util.Pair;
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 
56         // is the corruption detected even before database could be 'opened'?
57         if (!dbObj.isOpen()) {
58             // database files are not even openable. delete this database file.
59             // NOTE if the database has attached databases, then any of them could be corrupt.
60             // and not deleting all of them could cause corrupted database file to remain and
61             // make the application crash on database open operation. To avoid this problem,
62             // the application should provide its own {@link DatabaseErrorHandler} impl class
63             // to delete ALL files of the database (including the attached databases).
64             deleteDatabaseFile(dbObj.getPath());
65             return;
66         }
67 
68         List<Pair<String, String>> attachedDbs = null;
69         try {
70             // Close the database, which will cause subsequent operations to fail.
71             // before that, get the attached database list first.
72             try {
73                 attachedDbs = dbObj.getAttachedDbs();
74             } catch (SQLiteException e) {
75                 /* ignore */
76             }
77             try {
78                 dbObj.close();
79             } catch (SQLiteException e) {
80                 /* ignore */
81             }
82         } finally {
83             // Delete all files of this corrupt database and/or attached databases
84             if (attachedDbs != null) {
85                 for (Pair<String, String> p : attachedDbs) {
86                     deleteDatabaseFile(p.second);
87                 }
88             } else {
89                 // attachedDbs = null is possible when the database is so corrupt that even
90                 // "PRAGMA database_list;" also fails. delete the main database file
91                 deleteDatabaseFile(dbObj.getPath());
92             }
93         }
94     }
95 
deleteDatabaseFile(String fileName)96     private void deleteDatabaseFile(String fileName) {
97         if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
98             return;
99         }
100         Log.e(TAG, "deleting the database file: " + fileName);
101         try {
102             SQLiteDatabase.deleteDatabase(new File(fileName));
103         } catch (Exception e) {
104             /* print warning and ignore exception */
105             Log.w(TAG, "delete failed: " + e.getMessage());
106         }
107     }
108 }
109