Merge "Add database to store anomaly data"

This commit is contained in:
Lei Yu
2018-01-19 01:35:53 +00:00
committed by Android (Google) Code Review
11 changed files with 534 additions and 83 deletions

View File

@@ -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);
}
}

View 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);
}
}
}

View File

@@ -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)});
}
}
}

View File

@@ -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()));

View File

@@ -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];
}
};
}

View File

@@ -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());
}
}

View File

@@ -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;
}