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