1 /*
2  * Copyright (C) 2018 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.settings.fuelgauge.batterytip;
18 
19 import android.content.Context;
20 import android.database.sqlite.SQLiteDatabase;
21 import android.database.sqlite.SQLiteOpenHelper;
22 import android.util.Log;
23 
24 import androidx.annotation.IntDef;
25 
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 
29 /** Database controls the anomaly logging(e.g. packageName, anomalyType and time) */
30 public class AnomalyDatabaseHelper extends SQLiteOpenHelper {
31     private static final String TAG = "BatteryDatabaseHelper";
32 
33     private static final String DATABASE_NAME = "battery_settings.db";
34     private static final int DATABASE_VERSION = 5;
35 
36     @Retention(RetentionPolicy.SOURCE)
37     @IntDef({State.NEW, State.HANDLED, State.AUTO_HANDLED})
38     public @interface State {
39         int NEW = 0;
40         int HANDLED = 1;
41         int AUTO_HANDLED = 2;
42     }
43 
44     @Retention(RetentionPolicy.SOURCE)
45     @IntDef({ActionType.RESTRICTION})
46     public @interface ActionType {
47         int RESTRICTION = 0;
48     }
49 
50     public interface Tables {
51         String TABLE_ANOMALY = "anomaly";
52         String TABLE_ACTION = "action";
53     }
54 
55     public interface AnomalyColumns {
56         /** The package name of the anomaly app */
57         String PACKAGE_NAME = "package_name";
58 
59         /** The uid of the anomaly app */
60         String UID = "uid";
61 
62         /**
63          * The type of the anomaly app
64          *
65          * @see StatsManagerConfig.AnomalyType
66          */
67         String ANOMALY_TYPE = "anomaly_type";
68 
69         /**
70          * The state of the anomaly app
71          *
72          * @see State
73          */
74         String ANOMALY_STATE = "anomaly_state";
75 
76         /** The time when anomaly happens */
77         String TIME_STAMP_MS = "time_stamp_ms";
78     }
79 
80     private static final String CREATE_ANOMALY_TABLE =
81             "CREATE TABLE "
82                     + Tables.TABLE_ANOMALY
83                     + "("
84                     + AnomalyColumns.UID
85                     + " INTEGER NOT NULL, "
86                     + AnomalyColumns.PACKAGE_NAME
87                     + " TEXT, "
88                     + AnomalyColumns.ANOMALY_TYPE
89                     + " INTEGER NOT NULL, "
90                     + AnomalyColumns.ANOMALY_STATE
91                     + " INTEGER NOT NULL, "
92                     + AnomalyColumns.TIME_STAMP_MS
93                     + " INTEGER NOT NULL, "
94                     + " PRIMARY KEY ("
95                     + AnomalyColumns.UID
96                     + ","
97                     + AnomalyColumns.ANOMALY_TYPE
98                     + ","
99                     + AnomalyColumns.ANOMALY_STATE
100                     + ","
101                     + AnomalyColumns.TIME_STAMP_MS
102                     + ")"
103                     + ")";
104 
105     public interface ActionColumns {
106         /** The package name of an app been performed an action */
107         String PACKAGE_NAME = "package_name";
108 
109         /** The uid of an app been performed an action */
110         String UID = "uid";
111 
112         /**
113          * The type of user action
114          *
115          * @see ActionType
116          */
117         String ACTION_TYPE = "action_type";
118 
119         /** The time when action been performed */
120         String TIME_STAMP_MS = "time_stamp_ms";
121     }
122 
123     private static final String CREATE_ACTION_TABLE =
124             "CREATE TABLE "
125                     + Tables.TABLE_ACTION
126                     + "("
127                     + ActionColumns.UID
128                     + " INTEGER NOT NULL, "
129                     + ActionColumns.PACKAGE_NAME
130                     + " TEXT, "
131                     + ActionColumns.ACTION_TYPE
132                     + " INTEGER NOT NULL, "
133                     + ActionColumns.TIME_STAMP_MS
134                     + " INTEGER NOT NULL, "
135                     + " PRIMARY KEY ("
136                     + ActionColumns.ACTION_TYPE
137                     + ","
138                     + ActionColumns.UID
139                     + ","
140                     + ActionColumns.PACKAGE_NAME
141                     + ")"
142                     + ")";
143 
144     private static AnomalyDatabaseHelper sSingleton;
145 
getInstance(Context context)146     public static synchronized AnomalyDatabaseHelper getInstance(Context context) {
147         if (sSingleton == null) {
148             sSingleton = new AnomalyDatabaseHelper(context.getApplicationContext());
149         }
150         return sSingleton;
151     }
152 
AnomalyDatabaseHelper(Context context)153     private AnomalyDatabaseHelper(Context context) {
154         super(context, DATABASE_NAME, null, DATABASE_VERSION);
155     }
156 
157     @Override
onCreate(SQLiteDatabase db)158     public void onCreate(SQLiteDatabase db) {
159         bootstrapDB(db);
160     }
161 
162     @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)163     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
164         if (oldVersion < DATABASE_VERSION) {
165             Log.w(
166                     TAG,
167                     "Detected schema version '"
168                             + oldVersion
169                             + "'. "
170                             + "Index needs to be rebuilt for schema version '"
171                             + newVersion
172                             + "'.");
173             // We need to drop the tables and recreate them
174             reconstruct(db);
175         }
176     }
177 
178     @Override
onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)179     public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
180         Log.w(
181                 TAG,
182                 "Detected schema version '"
183                         + oldVersion
184                         + "'. "
185                         + "Index needs to be rebuilt for schema version '"
186                         + newVersion
187                         + "'.");
188         // We need to drop the tables and recreate them
189         reconstruct(db);
190     }
191 
reconstruct(SQLiteDatabase db)192     public void reconstruct(SQLiteDatabase db) {
193         dropTables(db);
194         bootstrapDB(db);
195     }
196 
bootstrapDB(SQLiteDatabase db)197     private void bootstrapDB(SQLiteDatabase db) {
198         db.execSQL(CREATE_ANOMALY_TABLE);
199         db.execSQL(CREATE_ACTION_TABLE);
200         Log.i(TAG, "Bootstrapped database");
201     }
202 
dropTables(SQLiteDatabase db)203     private void dropTables(SQLiteDatabase db) {
204         db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_ANOMALY);
205         db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_ACTION);
206     }
207 }
208