Add database to store anomaly data
1. Refactor HighUsageApp to AppInfo so it could be reused both in dialog and app restriction 2. Add BatteryDatabaseHelper to store the anomaly log Bug: 70570352 Test: RunSettingsRoboTests Change-Id: I900cd9746ff7f1e19bd6f3948463588b7cf72b85
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.fuelgauge.batterytip;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.settings.fuelgauge.anomaly.Anomaly;
|
||||
|
||||
/**
|
||||
* Database controls the anomaly logging(e.g. packageName, anomalyType and time)
|
||||
*/
|
||||
public class AnomalyDatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final String TAG = "BatteryDatabaseHelper";
|
||||
|
||||
private static final String DATABASE_NAME = "battery_settings.db";
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
|
||||
public interface Tables {
|
||||
String TABLE_ANOMALY = "anomaly";
|
||||
}
|
||||
|
||||
public interface AnomalyColumns {
|
||||
/**
|
||||
* The package name of the anomaly app
|
||||
*/
|
||||
String PACKAGE_NAME = "package_name";
|
||||
/**
|
||||
* The type of the anomaly app
|
||||
* @see Anomaly.AnomalyType
|
||||
*/
|
||||
String ANOMALY_TYPE = "anomaly_type";
|
||||
/**
|
||||
* The time when anomaly happens
|
||||
*/
|
||||
String TIME_STAMP_MS = "time_stamp_ms";
|
||||
}
|
||||
|
||||
private static final String CREATE_ANOMALY_TABLE =
|
||||
"CREATE TABLE " + Tables.TABLE_ANOMALY +
|
||||
"(" +
|
||||
AnomalyColumns.PACKAGE_NAME +
|
||||
" TEXT, " +
|
||||
AnomalyColumns.ANOMALY_TYPE +
|
||||
" INTEGER, " +
|
||||
AnomalyColumns.TIME_STAMP_MS +
|
||||
" INTEGER)";
|
||||
|
||||
private static AnomalyDatabaseHelper sSingleton;
|
||||
|
||||
public static synchronized AnomalyDatabaseHelper getInstance(Context context) {
|
||||
if (sSingleton == null) {
|
||||
sSingleton = new AnomalyDatabaseHelper(context.getApplicationContext());
|
||||
}
|
||||
return sSingleton;
|
||||
}
|
||||
|
||||
private AnomalyDatabaseHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
bootstrapDB(db);
|
||||
}
|
||||
|
||||
private void bootstrapDB(SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_ANOMALY_TABLE);
|
||||
Log.i(TAG, "Bootstrapped database");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion < DATABASE_VERSION) {
|
||||
Log.w(TAG, "Detected schema version '" + oldVersion + "'. " +
|
||||
"Index needs to be rebuilt for schema version '" + newVersion + "'.");
|
||||
// We need to drop the tables and recreate them
|
||||
reconstruct(db);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
Log.w(TAG, "Detected schema version '" + oldVersion + "'. " +
|
||||
"Index needs to be rebuilt for schema version '" + newVersion + "'.");
|
||||
// We need to drop the tables and recreate them
|
||||
reconstruct(db);
|
||||
}
|
||||
|
||||
public void reconstruct(SQLiteDatabase db) {
|
||||
dropTables(db);
|
||||
bootstrapDB(db);
|
||||
}
|
||||
|
||||
private void dropTables(SQLiteDatabase db) {
|
||||
db.execSQL("DROP TABLE IF EXISTS " + Tables.TABLE_ANOMALY);
|
||||
}
|
||||
}
|
101
src/com/android/settings/fuelgauge/batterytip/AppInfo.java
Normal file
101
src/com/android/settings/fuelgauge/batterytip/AppInfo.java
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.fuelgauge.batterytip;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.fuelgauge.anomaly.Anomaly;
|
||||
|
||||
/**
|
||||
* Model class stores app info(e.g. package name, type..) that used in battery tip
|
||||
*/
|
||||
public class AppInfo implements Comparable<AppInfo>, Parcelable {
|
||||
public final String packageName;
|
||||
/**
|
||||
* Anomaly type of the app
|
||||
* @see Anomaly.AnomalyType
|
||||
*/
|
||||
public final int anomalyType;
|
||||
public final long screenOnTimeMs;
|
||||
|
||||
private AppInfo(AppInfo.Builder builder) {
|
||||
packageName = builder.mPackageName;
|
||||
anomalyType = builder.mAnomalyType;
|
||||
screenOnTimeMs = builder.mScreenOnTimeMs;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
AppInfo(Parcel in) {
|
||||
packageName = in.readString();
|
||||
anomalyType = in.readInt();
|
||||
screenOnTimeMs = in.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(AppInfo o) {
|
||||
return Long.compare(screenOnTimeMs, o.screenOnTimeMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(packageName);
|
||||
dest.writeInt(anomalyType);
|
||||
dest.writeLong(screenOnTimeMs);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
|
||||
public AppInfo createFromParcel(Parcel in) {
|
||||
return new AppInfo(in);
|
||||
}
|
||||
|
||||
public AppInfo[] newArray(int size) {
|
||||
return new AppInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
public static final class Builder {
|
||||
private int mAnomalyType;
|
||||
private String mPackageName;
|
||||
private long mScreenOnTimeMs;
|
||||
|
||||
public Builder setAnomalyType(int type) {
|
||||
mAnomalyType = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPackageName(String packageName) {
|
||||
mPackageName = packageName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setScreenOnTimeMs(long screenOnTimeMs) {
|
||||
mScreenOnTimeMs = screenOnTimeMs;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AppInfo build() {
|
||||
return new AppInfo(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.fuelgauge.batterytip;
|
||||
|
||||
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
|
||||
.PACKAGE_NAME;
|
||||
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
|
||||
.ANOMALY_TYPE;
|
||||
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.AnomalyColumns
|
||||
.TIME_STAMP_MS;
|
||||
import static com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper.Tables.TABLE_ANOMALY;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Database manager for battery data. Now it only contains anomaly data stored in {@link AppInfo}.
|
||||
*/
|
||||
public class BatteryDatabaseManager {
|
||||
private final AnomalyDatabaseHelper mDatabaseHelper;
|
||||
|
||||
public BatteryDatabaseManager(Context context) {
|
||||
mDatabaseHelper = AnomalyDatabaseHelper.getInstance(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an anomaly log to database.
|
||||
*
|
||||
* @param packageName the package name of the app
|
||||
* @param type the type of the anomaly
|
||||
* @param timestampMs the time when it is happened
|
||||
*/
|
||||
public void insertAnomaly(String packageName, int type, long timestampMs) {
|
||||
try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(PACKAGE_NAME, packageName);
|
||||
values.put(ANOMALY_TYPE, type);
|
||||
values.put(TIME_STAMP_MS, timestampMs);
|
||||
|
||||
db.insert(TABLE_ANOMALY, null, values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query all the anomalies that happened after {@code timestampMs}.
|
||||
*/
|
||||
public List<AppInfo> queryAllAnomaliesAfter(long timestampMs) {
|
||||
final List<AppInfo> appInfos = new ArrayList<>();
|
||||
try (SQLiteDatabase db = mDatabaseHelper.getReadableDatabase()) {
|
||||
final String[] projection = {PACKAGE_NAME, ANOMALY_TYPE};
|
||||
final String orderBy = AnomalyDatabaseHelper.AnomalyColumns.TIME_STAMP_MS + " DESC";
|
||||
|
||||
try (Cursor cursor = db.query(TABLE_ANOMALY, projection, TIME_STAMP_MS + " > ?",
|
||||
new String[]{String.valueOf(timestampMs)}, null, null, orderBy)) {
|
||||
while (cursor.moveToNext()) {
|
||||
AppInfo appInfo = new AppInfo.Builder()
|
||||
.setPackageName(cursor.getString(cursor.getColumnIndex(PACKAGE_NAME)))
|
||||
.setAnomalyType(cursor.getInt(cursor.getColumnIndex(ANOMALY_TYPE)))
|
||||
.build();
|
||||
appInfos.add(appInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return appInfos;
|
||||
}
|
||||
|
||||
public void deleteAllAnomaliesBeforeTimeStamp(long timestampMs) {
|
||||
try (SQLiteDatabase db = mDatabaseHelper.getWritableDatabase()) {
|
||||
db.delete(TABLE_ANOMALY, TIME_STAMP_MS + " < ?",
|
||||
new String[]{String.valueOf(timestampMs)});
|
||||
}
|
||||
}
|
||||
}
|
@@ -39,7 +39,7 @@ public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.View
|
||||
private final Context mContext;
|
||||
private final IconDrawableFactory mIconDrawableFactory;
|
||||
private final PackageManager mPackageManager;
|
||||
private final List<HighUsageApp> mHighUsageAppList;
|
||||
private final List<AppInfo> mHighUsageAppList;
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public View view;
|
||||
@@ -56,7 +56,7 @@ public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.View
|
||||
}
|
||||
}
|
||||
|
||||
public HighUsageAdapter(Context context, List<HighUsageApp> highUsageAppList) {
|
||||
public HighUsageAdapter(Context context, List<AppInfo> highUsageAppList) {
|
||||
mContext = context;
|
||||
mHighUsageAppList = highUsageAppList;
|
||||
mIconDrawableFactory = IconDrawableFactory.newInstance(context);
|
||||
@@ -72,7 +72,7 @@ public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.View
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
final HighUsageApp app = mHighUsageAppList.get(position);
|
||||
final AppInfo app = mHighUsageAppList.get(position);
|
||||
holder.appIcon.setImageDrawable(
|
||||
Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, app.packageName,
|
||||
UserHandle.myUserId()));
|
||||
|
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.fuelgauge.batterytip;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Class representing app with high screen usage
|
||||
*/
|
||||
public class HighUsageApp implements Comparable<HighUsageApp>, Parcelable {
|
||||
public final String packageName;
|
||||
public final long screenOnTimeMs;
|
||||
|
||||
public HighUsageApp(String packageName, long screenOnTimeMs) {
|
||||
this.packageName = packageName;
|
||||
this.screenOnTimeMs = screenOnTimeMs;
|
||||
}
|
||||
|
||||
private HighUsageApp(Parcel in) {
|
||||
packageName = in.readString();
|
||||
screenOnTimeMs = in.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(HighUsageApp o) {
|
||||
return Long.compare(screenOnTimeMs, o.screenOnTimeMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(packageName);
|
||||
dest.writeLong(screenOnTimeMs);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
|
||||
public HighUsageApp createFromParcel(Parcel in) {
|
||||
return new HighUsageApp(in);
|
||||
}
|
||||
|
||||
public HighUsageApp[] newArray(int size) {
|
||||
return new HighUsageApp[size];
|
||||
}
|
||||
};
|
||||
}
|
@@ -23,13 +23,11 @@ import android.text.format.DateUtils;
|
||||
|
||||
import com.android.internal.os.BatterySipper;
|
||||
import com.android.internal.os.BatteryStatsHelper;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.fuelgauge.BatteryUtils;
|
||||
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
|
||||
import com.android.settings.fuelgauge.batterytip.HighUsageApp;
|
||||
import com.android.settings.fuelgauge.batterytip.AppInfo;
|
||||
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
|
||||
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
|
||||
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -42,7 +40,7 @@ import java.util.List;
|
||||
public class HighUsageDetector implements BatteryTipDetector {
|
||||
private BatteryTipPolicy mPolicy;
|
||||
private BatteryStatsHelper mBatteryStatsHelper;
|
||||
private List<HighUsageApp> mHighUsageAppList;
|
||||
private List<AppInfo> mHighUsageAppList;
|
||||
private Context mContext;
|
||||
@VisibleForTesting
|
||||
BatteryUtils mBatteryUtils;
|
||||
@@ -68,9 +66,10 @@ public class HighUsageDetector implements BatteryTipDetector {
|
||||
final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
|
||||
BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
|
||||
BatteryStats.STATS_SINCE_CHARGED);
|
||||
mHighUsageAppList.add(new HighUsageApp(
|
||||
mBatteryUtils.getPackageName(batterySipper.getUid()),
|
||||
foregroundTimeMs));
|
||||
mHighUsageAppList.add(new AppInfo.Builder()
|
||||
.setPackageName(mBatteryUtils.getPackageName(batterySipper.getUid()))
|
||||
.setScreenOnTimeMs(foregroundTimeMs)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,7 @@ import android.support.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.fuelgauge.batterytip.HighUsageApp;
|
||||
import com.android.settings.fuelgauge.batterytip.AppInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -34,9 +34,9 @@ public class HighUsageTip extends BatteryTip {
|
||||
|
||||
private final long mScreenTimeMs;
|
||||
@VisibleForTesting
|
||||
final List<HighUsageApp> mHighUsageAppList;
|
||||
final List<AppInfo> mHighUsageAppList;
|
||||
|
||||
public HighUsageTip(long screenTimeMs, List<HighUsageApp> appList) {
|
||||
public HighUsageTip(long screenTimeMs, List<AppInfo> appList) {
|
||||
super(TipType.HIGH_DEVICE_USAGE, appList.isEmpty() ? StateType.INVISIBLE : StateType.NEW,
|
||||
true /* showDialog */);
|
||||
mScreenTimeMs = screenTimeMs;
|
||||
@@ -47,7 +47,7 @@ public class HighUsageTip extends BatteryTip {
|
||||
HighUsageTip(Parcel in) {
|
||||
super(in);
|
||||
mScreenTimeMs = in.readLong();
|
||||
mHighUsageAppList = in.createTypedArrayList(HighUsageApp.CREATOR);
|
||||
mHighUsageAppList = in.createTypedArrayList(AppInfo.CREATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,7 +82,7 @@ public class HighUsageTip extends BatteryTip {
|
||||
return mScreenTimeMs;
|
||||
}
|
||||
|
||||
public List<HighUsageApp> getHighUsageAppList() {
|
||||
public List<AppInfo> getHighUsageAppList() {
|
||||
return mHighUsageAppList;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user