Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 48bd637d authored by Lei Yu's avatar Lei Yu Committed by Android (Google) Code Review
Browse files

Merge "Add database to store anomaly data"

parents 18a9d7ba 963b7cca
Loading
Loading
Loading
Loading
+114 −0
Original line number Original line Diff line number Diff line
/*
 * 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 −0
Original line number Original line Diff line number Diff line
@@ -18,26 +18,37 @@ package com.android.settings.fuelgauge.batterytip;


import android.os.Parcel;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.support.annotation.VisibleForTesting;

import com.android.settings.fuelgauge.anomaly.Anomaly;


/**
/**
 * Class representing app with high screen usage
 * Model class stores app info(e.g. package name, type..) that used in battery tip
 */
 */
public class HighUsageApp implements Comparable<HighUsageApp>, Parcelable {
public class AppInfo implements Comparable<AppInfo>, Parcelable {
    public final String packageName;
    public final String packageName;
    /**
     * Anomaly type of the app
     * @see Anomaly.AnomalyType
     */
    public final int anomalyType;
    public final long screenOnTimeMs;
    public final long screenOnTimeMs;


    public HighUsageApp(String packageName, long screenOnTimeMs) {
    private AppInfo(AppInfo.Builder builder) {
        this.packageName = packageName;
        packageName = builder.mPackageName;
        this.screenOnTimeMs = screenOnTimeMs;
        anomalyType = builder.mAnomalyType;
        screenOnTimeMs = builder.mScreenOnTimeMs;
    }
    }


    private HighUsageApp(Parcel in) {
    @VisibleForTesting
    AppInfo(Parcel in) {
        packageName = in.readString();
        packageName = in.readString();
        anomalyType = in.readInt();
        screenOnTimeMs = in.readLong();
        screenOnTimeMs = in.readLong();
    }
    }


    @Override
    @Override
    public int compareTo(HighUsageApp o) {
    public int compareTo(AppInfo o) {
        return Long.compare(screenOnTimeMs, o.screenOnTimeMs);
        return Long.compare(screenOnTimeMs, o.screenOnTimeMs);
    }
    }


@@ -49,16 +60,42 @@ public class HighUsageApp implements Comparable<HighUsageApp>, Parcelable {
    @Override
    @Override
    public void writeToParcel(Parcel dest, int flags) {
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(packageName);
        dest.writeString(packageName);
        dest.writeInt(anomalyType);
        dest.writeLong(screenOnTimeMs);
        dest.writeLong(screenOnTimeMs);
    }
    }


    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        public HighUsageApp createFromParcel(Parcel in) {
        public AppInfo createFromParcel(Parcel in) {
            return new HighUsageApp(in);
            return new AppInfo(in);
        }
        }


        public HighUsageApp[] newArray(int size) {
        public AppInfo[] newArray(int size) {
            return new HighUsageApp[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);
        }
    }
}
}
 No newline at end of file
+93 −0
Original line number Original line Diff line number Diff line
/*
 * 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)});
        }
    }
}
+3 −3
Original line number Original line Diff line number Diff line
@@ -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()));
+6 −7
Original line number Original line Diff line number Diff line
@@ -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());
                }
                }
            }
            }


Loading