Merge "Add database to store anomaly data"
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 Context mContext;
|
||||||
private final IconDrawableFactory mIconDrawableFactory;
|
private final IconDrawableFactory mIconDrawableFactory;
|
||||||
private final PackageManager mPackageManager;
|
private final PackageManager mPackageManager;
|
||||||
private final List<HighUsageApp> mHighUsageAppList;
|
private final List<AppInfo> mHighUsageAppList;
|
||||||
|
|
||||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
public View view;
|
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;
|
mContext = context;
|
||||||
mHighUsageAppList = highUsageAppList;
|
mHighUsageAppList = highUsageAppList;
|
||||||
mIconDrawableFactory = IconDrawableFactory.newInstance(context);
|
mIconDrawableFactory = IconDrawableFactory.newInstance(context);
|
||||||
@@ -72,7 +72,7 @@ public class HighUsageAdapter extends RecyclerView.Adapter<HighUsageAdapter.View
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
final HighUsageApp app = mHighUsageAppList.get(position);
|
final AppInfo app = mHighUsageAppList.get(position);
|
||||||
holder.appIcon.setImageDrawable(
|
holder.appIcon.setImageDrawable(
|
||||||
Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, app.packageName,
|
Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, app.packageName,
|
||||||
UserHandle.myUserId()));
|
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.BatterySipper;
|
||||||
import com.android.internal.os.BatteryStatsHelper;
|
import com.android.internal.os.BatteryStatsHelper;
|
||||||
import com.android.settings.Utils;
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
import com.android.settings.fuelgauge.BatteryUtils;
|
||||||
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
|
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.BatteryTip;
|
||||||
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
|
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
|
||||||
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -42,7 +40,7 @@ import java.util.List;
|
|||||||
public class HighUsageDetector implements BatteryTipDetector {
|
public class HighUsageDetector implements BatteryTipDetector {
|
||||||
private BatteryTipPolicy mPolicy;
|
private BatteryTipPolicy mPolicy;
|
||||||
private BatteryStatsHelper mBatteryStatsHelper;
|
private BatteryStatsHelper mBatteryStatsHelper;
|
||||||
private List<HighUsageApp> mHighUsageAppList;
|
private List<AppInfo> mHighUsageAppList;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
BatteryUtils mBatteryUtils;
|
BatteryUtils mBatteryUtils;
|
||||||
@@ -68,9 +66,10 @@ public class HighUsageDetector implements BatteryTipDetector {
|
|||||||
final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
|
final long foregroundTimeMs = mBatteryUtils.getProcessTimeMs(
|
||||||
BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
|
BatteryUtils.StatusType.FOREGROUND, batterySipper.uidObj,
|
||||||
BatteryStats.STATS_SINCE_CHARGED);
|
BatteryStats.STATS_SINCE_CHARGED);
|
||||||
mHighUsageAppList.add(new HighUsageApp(
|
mHighUsageAppList.add(new AppInfo.Builder()
|
||||||
mBatteryUtils.getPackageName(batterySipper.getUid()),
|
.setPackageName(mBatteryUtils.getPackageName(batterySipper.getUid()))
|
||||||
foregroundTimeMs));
|
.setScreenOnTimeMs(foregroundTimeMs)
|
||||||
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,7 +23,7 @@ import android.support.annotation.VisibleForTesting;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.fuelgauge.batterytip.HighUsageApp;
|
import com.android.settings.fuelgauge.batterytip.AppInfo;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -34,9 +34,9 @@ public class HighUsageTip extends BatteryTip {
|
|||||||
|
|
||||||
private final long mScreenTimeMs;
|
private final long mScreenTimeMs;
|
||||||
@VisibleForTesting
|
@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,
|
super(TipType.HIGH_DEVICE_USAGE, appList.isEmpty() ? StateType.INVISIBLE : StateType.NEW,
|
||||||
true /* showDialog */);
|
true /* showDialog */);
|
||||||
mScreenTimeMs = screenTimeMs;
|
mScreenTimeMs = screenTimeMs;
|
||||||
@@ -47,7 +47,7 @@ public class HighUsageTip extends BatteryTip {
|
|||||||
HighUsageTip(Parcel in) {
|
HighUsageTip(Parcel in) {
|
||||||
super(in);
|
super(in);
|
||||||
mScreenTimeMs = in.readLong();
|
mScreenTimeMs = in.readLong();
|
||||||
mHighUsageAppList = in.createTypedArrayList(HighUsageApp.CREATOR);
|
mHighUsageAppList = in.createTypedArrayList(AppInfo.CREATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,7 +82,7 @@ public class HighUsageTip extends BatteryTip {
|
|||||||
return mScreenTimeMs;
|
return mScreenTimeMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<HighUsageApp> getHighUsageAppList() {
|
public List<AppInfo> getHighUsageAppList() {
|
||||||
return mHighUsageAppList;
|
return mHighUsageAppList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.fuelgauge.batterytip.AppInfo;
|
||||||
|
import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
|
||||||
|
import com.android.settings.testutils.DatabaseTestUtils;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||||
|
public class BatteryDatabaseManagerTest {
|
||||||
|
private static String PACKAGE_NAME_NEW = "com.android.app1";
|
||||||
|
private static int TYPE_NEW = 1;
|
||||||
|
private static String PACKAGE_NAME_OLD = "com.android.app2";
|
||||||
|
private static int TYPE_OLD = 2;
|
||||||
|
private static long NOW = System.currentTimeMillis();
|
||||||
|
private static long ONE_DAY_BEFORE = NOW - DateUtils.DAY_IN_MILLIS;
|
||||||
|
private static long TWO_DAYS_BEFORE = NOW - 2 * DateUtils.DAY_IN_MILLIS;
|
||||||
|
private Context mContext;
|
||||||
|
private BatteryDatabaseManager mBatteryDatabaseManager;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
mContext = RuntimeEnvironment.application;
|
||||||
|
mBatteryDatabaseManager = spy(new BatteryDatabaseManager(mContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanUp() {
|
||||||
|
DatabaseTestUtils.clearDb(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAllFunctions() {
|
||||||
|
mBatteryDatabaseManager.insertAnomaly(PACKAGE_NAME_NEW, TYPE_NEW, NOW);
|
||||||
|
mBatteryDatabaseManager.insertAnomaly(PACKAGE_NAME_OLD, TYPE_OLD, TWO_DAYS_BEFORE);
|
||||||
|
|
||||||
|
// In database, it contains two record
|
||||||
|
List<AppInfo> totalAppInfos = mBatteryDatabaseManager.queryAllAnomaliesAfter(0);
|
||||||
|
assertThat(totalAppInfos).hasSize(2);
|
||||||
|
verifyAppInfo(totalAppInfos.get(0), PACKAGE_NAME_NEW, TYPE_NEW);
|
||||||
|
verifyAppInfo(totalAppInfos.get(1), PACKAGE_NAME_OLD, TYPE_OLD);
|
||||||
|
|
||||||
|
// Only one record shows up if we query by timestamp
|
||||||
|
List<AppInfo> appInfos = mBatteryDatabaseManager.queryAllAnomaliesAfter(ONE_DAY_BEFORE);
|
||||||
|
assertThat(appInfos).hasSize(1);
|
||||||
|
verifyAppInfo(appInfos.get(0), PACKAGE_NAME_NEW, TYPE_NEW);
|
||||||
|
|
||||||
|
mBatteryDatabaseManager.deleteAllAnomaliesBeforeTimeStamp(ONE_DAY_BEFORE);
|
||||||
|
|
||||||
|
// The obsolete record is removed from database
|
||||||
|
List<AppInfo> appInfos1 = mBatteryDatabaseManager.queryAllAnomaliesAfter(0);
|
||||||
|
assertThat(appInfos1).hasSize(1);
|
||||||
|
verifyAppInfo(appInfos1.get(0), PACKAGE_NAME_NEW, TYPE_NEW);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyAppInfo(final AppInfo appInfo, String packageName, int type) {
|
||||||
|
assertThat(appInfo.packageName).isEqualTo(packageName);
|
||||||
|
assertThat(appInfo.anomalyType).isEqualTo(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* 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.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
|
import com.android.settings.TestConfig;
|
||||||
|
import com.android.settings.fuelgauge.anomaly.Anomaly;
|
||||||
|
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RunWith(SettingsRobolectricTestRunner.class)
|
||||||
|
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
|
||||||
|
public class AppInfoTest {
|
||||||
|
private static final String PACKAGE_NAME = "com.android.app";
|
||||||
|
private static final int ANOMALY_TYPE = Anomaly.AnomalyType.WAKE_LOCK;
|
||||||
|
private static final long SCREEN_TIME_MS = DateUtils.HOUR_IN_MILLIS;
|
||||||
|
|
||||||
|
private AppInfo mAppInfo;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mAppInfo = new AppInfo.Builder()
|
||||||
|
.setPackageName(PACKAGE_NAME)
|
||||||
|
.setAnomalyType(ANOMALY_TYPE)
|
||||||
|
.setScreenOnTimeMs(SCREEN_TIME_MS)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParcel() {
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
mAppInfo.writeToParcel(parcel, mAppInfo.describeContents());
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
|
||||||
|
final AppInfo appInfo = new AppInfo(parcel);
|
||||||
|
|
||||||
|
assertThat(appInfo.packageName).isEqualTo(PACKAGE_NAME);
|
||||||
|
assertThat(appInfo.anomalyType).isEqualTo(ANOMALY_TYPE);
|
||||||
|
assertThat(appInfo.screenOnTimeMs).isEqualTo(SCREEN_TIME_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompareTo_hasCorrectOrder() {
|
||||||
|
final AppInfo appInfo = new AppInfo.Builder()
|
||||||
|
.setPackageName(PACKAGE_NAME)
|
||||||
|
.setAnomalyType(ANOMALY_TYPE)
|
||||||
|
.setScreenOnTimeMs(SCREEN_TIME_MS + 100)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<AppInfo> appInfos = new ArrayList<>();
|
||||||
|
appInfos.add(appInfo);
|
||||||
|
appInfos.add(mAppInfo);
|
||||||
|
|
||||||
|
Collections.sort(appInfos);
|
||||||
|
assertThat(appInfos.get(0).screenOnTimeMs).isLessThan(appInfos.get(1).screenOnTimeMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuilder() {
|
||||||
|
assertThat(mAppInfo.packageName).isEqualTo(PACKAGE_NAME);
|
||||||
|
assertThat(mAppInfo.anomalyType).isEqualTo(ANOMALY_TYPE);
|
||||||
|
assertThat(mAppInfo.screenOnTimeMs).isEqualTo(SCREEN_TIME_MS);
|
||||||
|
}
|
||||||
|
}
|
@@ -22,7 +22,7 @@ import android.os.Parcel;
|
|||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
import com.android.settings.TestConfig;
|
import com.android.settings.TestConfig;
|
||||||
import com.android.settings.fuelgauge.batterytip.HighUsageApp;
|
import com.android.settings.fuelgauge.batterytip.AppInfo;
|
||||||
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
import com.android.settings.testutils.SettingsRobolectricTestRunner;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -42,14 +42,17 @@ public class HighUsageTipTest {
|
|||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private HighUsageTip mBatteryTip;
|
private HighUsageTip mBatteryTip;
|
||||||
private List<HighUsageApp> mUsageAppList;
|
private List<AppInfo> mUsageAppList;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mContext = RuntimeEnvironment.application;
|
mContext = RuntimeEnvironment.application;
|
||||||
|
|
||||||
mUsageAppList = new ArrayList<>();
|
mUsageAppList = new ArrayList<>();
|
||||||
mUsageAppList.add(new HighUsageApp(PACKAGE_NAME, SCREEN_TIME));
|
mUsageAppList.add(new AppInfo.Builder()
|
||||||
|
.setPackageName(PACKAGE_NAME)
|
||||||
|
.setScreenOnTimeMs(SCREEN_TIME)
|
||||||
|
.build());
|
||||||
mBatteryTip = new HighUsageTip(SCREEN_TIME, mUsageAppList);
|
mBatteryTip = new HighUsageTip(SCREEN_TIME, mUsageAppList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +70,7 @@ public class HighUsageTipTest {
|
|||||||
assertThat(parcelTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
|
assertThat(parcelTip.getState()).isEqualTo(BatteryTip.StateType.NEW);
|
||||||
assertThat(parcelTip.getScreenTimeMs()).isEqualTo(SCREEN_TIME);
|
assertThat(parcelTip.getScreenTimeMs()).isEqualTo(SCREEN_TIME);
|
||||||
assertThat(parcelTip.mHighUsageAppList.size()).isEqualTo(1);
|
assertThat(parcelTip.mHighUsageAppList.size()).isEqualTo(1);
|
||||||
final HighUsageApp app = parcelTip.mHighUsageAppList.get(0);
|
final AppInfo app = parcelTip.mHighUsageAppList.get(0);
|
||||||
assertThat(app.packageName).isEqualTo(PACKAGE_NAME);
|
assertThat(app.packageName).isEqualTo(PACKAGE_NAME);
|
||||||
assertThat(app.screenOnTimeMs).isEqualTo(SCREEN_TIME);
|
assertThat(app.screenOnTimeMs).isEqualTo(SCREEN_TIME);
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ package com.android.settings.testutils;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
|
||||||
import com.android.settings.search.IndexDatabaseHelper;
|
import com.android.settings.search.IndexDatabaseHelper;
|
||||||
import com.android.settings.slices.SlicesDatabaseHelper;
|
import com.android.settings.slices.SlicesDatabaseHelper;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ public class DatabaseTestUtils {
|
|||||||
public static void clearDb(Context context) {
|
public static void clearDb(Context context) {
|
||||||
clearSearchDb(context);
|
clearSearchDb(context);
|
||||||
clearSlicesDb(context);
|
clearSlicesDb(context);
|
||||||
|
clearAnomalyDb(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void clearSlicesDb(Context context) {
|
private static void clearSlicesDb(Context context) {
|
||||||
@@ -45,6 +47,21 @@ public class DatabaseTestUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void clearAnomalyDb(Context context) {
|
||||||
|
AnomalyDatabaseHelper helper = AnomalyDatabaseHelper.getInstance(context);
|
||||||
|
helper.close();
|
||||||
|
|
||||||
|
Field instance;
|
||||||
|
Class clazz = AnomalyDatabaseHelper.class;
|
||||||
|
try {
|
||||||
|
instance = clazz.getDeclaredField("sSingleton");
|
||||||
|
instance.setAccessible(true);
|
||||||
|
instance.set(null, null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void clearSearchDb(Context context) {
|
private static void clearSearchDb(Context context) {
|
||||||
IndexDatabaseHelper helper = IndexDatabaseHelper.getInstance(context);
|
IndexDatabaseHelper helper = IndexDatabaseHelper.getInstance(context);
|
||||||
helper.close();
|
helper.close();
|
||||||
|
Reference in New Issue
Block a user