Exclude screen on time in battery usage page when the device is in charging.
Bug: 265751163 Fix: 265751163 Test: manual Change-Id: I4ed71e1d6fad56a7cbfc9cd47ed4d791f45261ce
This commit is contained in:
@@ -81,6 +81,7 @@ android_library {
|
|||||||
"jsr305",
|
"jsr305",
|
||||||
"net-utils-framework-common",
|
"net-utils-framework-common",
|
||||||
"app-usage-event-protos-lite",
|
"app-usage-event-protos-lite",
|
||||||
|
"battery-event-protos-lite",
|
||||||
"settings-contextual-card-protos-lite",
|
"settings-contextual-card-protos-lite",
|
||||||
"settings-log-bridge-protos-lite",
|
"settings-log-bridge-protos-lite",
|
||||||
"settings-telephony-protos-lite",
|
"settings-telephony-protos-lite",
|
||||||
|
@@ -3110,6 +3110,7 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BATTERY_LEVEL_CHANGED"/>
|
<action android:name="android.intent.action.BATTERY_LEVEL_CHANGED"/>
|
||||||
<action android:name="com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA"/>
|
<action android:name="com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA"/>
|
||||||
|
<action android:name="com.android.settings.battery.action.ACTION_BATTERY_PLUGGING"/>
|
||||||
<action android:name="com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING"/>
|
<action android:name="com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
@@ -30,6 +30,8 @@ import com.android.settings.overlay.FeatureFactory;
|
|||||||
import com.android.settingslib.fuelgauge.BatteryStatus;
|
import com.android.settingslib.fuelgauge.BatteryStatus;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
/** A {@link BatteryUsageBroadcastReceiver} for battery usage data requesting. */
|
/** A {@link BatteryUsageBroadcastReceiver} for battery usage data requesting. */
|
||||||
public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
|
public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
|
||||||
@@ -37,7 +39,10 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
|
|||||||
/** An intent action to request Settings to clear cache data. */
|
/** An intent action to request Settings to clear cache data. */
|
||||||
public static final String ACTION_CLEAR_BATTERY_CACHE_DATA =
|
public static final String ACTION_CLEAR_BATTERY_CACHE_DATA =
|
||||||
"com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA";
|
"com.android.settings.battery.action.CLEAR_BATTERY_CACHE_DATA";
|
||||||
/** An intent action to request Settings to clear cache data. */
|
/** An intent action for power is plugging. */
|
||||||
|
public static final String ACTION_BATTERY_PLUGGING =
|
||||||
|
"com.android.settings.battery.action.ACTION_BATTERY_PLUGGING";
|
||||||
|
/** An intent action for power is unplugging. */
|
||||||
public static final String ACTION_BATTERY_UNPLUGGING =
|
public static final String ACTION_BATTERY_UNPLUGGING =
|
||||||
"com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING";
|
"com.android.settings.battery.action.ACTION_BATTERY_UNPLUGGING";
|
||||||
|
|
||||||
@@ -49,6 +54,8 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean mFetchBatteryUsageData = false;
|
boolean mFetchBatteryUsageData = false;
|
||||||
|
|
||||||
|
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (intent == null || intent.getAction() == null) {
|
if (intent == null || intent.getAction() == null) {
|
||||||
@@ -70,7 +77,11 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
|
|||||||
tryToFetchUsageData(context);
|
tryToFetchUsageData(context);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ACTION_BATTERY_PLUGGING:
|
||||||
|
sendBatteryEventData(context, BatteryEventType.POWER_CONNECTED);
|
||||||
|
break;
|
||||||
case ACTION_BATTERY_UNPLUGGING:
|
case ACTION_BATTERY_UNPLUGGING:
|
||||||
|
sendBatteryEventData(context, BatteryEventType.POWER_DISCONNECTED);
|
||||||
// Only when fullChargeIntentAction is ACTION_POWER_DISCONNECTED,
|
// Only when fullChargeIntentAction is ACTION_POWER_DISCONNECTED,
|
||||||
// ACTION_BATTERY_UNPLUGGING will be considered as the full charge event and then
|
// ACTION_BATTERY_UNPLUGGING will be considered as the full charge event and then
|
||||||
// start usage events fetching.
|
// start usage events fetching.
|
||||||
@@ -106,4 +117,12 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
|
|||||||
mFetchBatteryUsageData = true;
|
mFetchBatteryUsageData = true;
|
||||||
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ true);
|
BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendBatteryEventData(Context context, BatteryEventType batteryEventType) {
|
||||||
|
final long timestamp = System.currentTimeMillis();
|
||||||
|
final Intent intent = BatteryUtils.getBatteryIntent(context);
|
||||||
|
final int batteryLevel = BatteryStatus.getBatteryLevel(intent);
|
||||||
|
mExecutor.execute(() -> DatabaseUtils.sendBatteryEventData(context,
|
||||||
|
ConvertUtils.convertToBatteryEvent(timestamp, batteryEventType, batteryLevel)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,6 +31,8 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
|
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
|
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventDao;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
|
||||||
@@ -52,6 +54,7 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
|||||||
private static final int BATTERY_STATE_CODE = 1;
|
private static final int BATTERY_STATE_CODE = 1;
|
||||||
private static final int APP_USAGE_LATEST_TIMESTAMP_CODE = 2;
|
private static final int APP_USAGE_LATEST_TIMESTAMP_CODE = 2;
|
||||||
private static final int APP_USAGE_EVENT_CODE = 3;
|
private static final int APP_USAGE_EVENT_CODE = 3;
|
||||||
|
private static final int BATTERY_EVENT_CODE = 4;
|
||||||
|
|
||||||
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
|
|
||||||
@@ -68,11 +71,16 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
|||||||
DatabaseUtils.AUTHORITY,
|
DatabaseUtils.AUTHORITY,
|
||||||
/*path=*/ DatabaseUtils.APP_USAGE_EVENT_TABLE,
|
/*path=*/ DatabaseUtils.APP_USAGE_EVENT_TABLE,
|
||||||
/*code=*/ APP_USAGE_EVENT_CODE);
|
/*code=*/ APP_USAGE_EVENT_CODE);
|
||||||
|
sUriMatcher.addURI(
|
||||||
|
DatabaseUtils.AUTHORITY,
|
||||||
|
/*path=*/ DatabaseUtils.BATTERY_EVENT_TABLE,
|
||||||
|
/*code=*/ BATTERY_EVENT_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Clock mClock;
|
private Clock mClock;
|
||||||
private BatteryStateDao mBatteryStateDao;
|
private BatteryStateDao mBatteryStateDao;
|
||||||
private AppUsageEventDao mAppUsageEventDao;
|
private AppUsageEventDao mAppUsageEventDao;
|
||||||
|
private BatteryEventDao mBatteryEventDao;
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||||
public void setClock(Clock clock) {
|
public void setClock(Clock clock) {
|
||||||
@@ -88,6 +96,7 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
|||||||
mClock = Clock.systemUTC();
|
mClock = Clock.systemUTC();
|
||||||
mBatteryStateDao = BatteryStateDatabase.getInstance(getContext()).batteryStateDao();
|
mBatteryStateDao = BatteryStateDatabase.getInstance(getContext()).batteryStateDao();
|
||||||
mAppUsageEventDao = BatteryStateDatabase.getInstance(getContext()).appUsageEventDao();
|
mAppUsageEventDao = BatteryStateDatabase.getInstance(getContext()).appUsageEventDao();
|
||||||
|
mBatteryEventDao = BatteryStateDatabase.getInstance(getContext()).batteryEventDao();
|
||||||
Log.w(TAG, "create content provider from " + getCallingPackage());
|
Log.w(TAG, "create content provider from " + getCallingPackage());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -107,6 +116,8 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
|||||||
return getAppUsageEvents(uri);
|
return getAppUsageEvents(uri);
|
||||||
case APP_USAGE_LATEST_TIMESTAMP_CODE:
|
case APP_USAGE_LATEST_TIMESTAMP_CODE:
|
||||||
return getAppUsageLatestTimestamp(uri);
|
return getAppUsageLatestTimestamp(uri);
|
||||||
|
case BATTERY_EVENT_CODE:
|
||||||
|
return getBatteryEvents(uri);
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("unknown URI: " + uri);
|
throw new IllegalArgumentException("unknown URI: " + uri);
|
||||||
}
|
}
|
||||||
@@ -138,6 +149,14 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
|||||||
Log.e(TAG, "insert() from:" + uri + " error:" + e);
|
Log.e(TAG, "insert() from:" + uri + " error:" + e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
case BATTERY_EVENT_CODE:
|
||||||
|
try {
|
||||||
|
mBatteryEventDao.insert(BatteryEventEntity.create(contentValues));
|
||||||
|
return uri;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Log.e(TAG, "insert() from:" + uri + " error:" + e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("unknown URI: " + uri);
|
throw new IllegalArgumentException("unknown URI: " + uri);
|
||||||
}
|
}
|
||||||
@@ -190,7 +209,6 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
|||||||
}
|
}
|
||||||
Log.w(TAG, "query app usage events in " + (mClock.millis() - timestamp) + "/ms");
|
Log.w(TAG, "query app usage events in " + (mClock.millis() - timestamp) + "/ms");
|
||||||
return cursor;
|
return cursor;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cursor getAppUsageLatestTimestamp(Uri uri) {
|
private Cursor getAppUsageLatestTimestamp(Uri uri) {
|
||||||
@@ -210,6 +228,19 @@ public class BatteryUsageContentProvider extends ContentProvider {
|
|||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Cursor getBatteryEvents(Uri uri) {
|
||||||
|
final long queryTimestamp = getQueryTimestamp(uri);
|
||||||
|
final long timestamp = mClock.millis();
|
||||||
|
Cursor cursor = null;
|
||||||
|
try {
|
||||||
|
cursor = mBatteryEventDao.getAllAfter(queryTimestamp);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
Log.e(TAG, "query() from:" + uri + " error:" + e);
|
||||||
|
}
|
||||||
|
Log.w(TAG, "query app usage events in " + (mClock.millis() - timestamp) + "/ms");
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
// If URI contains query parameter QUERY_KEY_USERID, use the value directly.
|
// If URI contains query parameter QUERY_KEY_USERID, use the value directly.
|
||||||
// Otherwise, return null.
|
// Otherwise, return null.
|
||||||
private List<Long> getQueryUserIds(Uri uri) {
|
private List<Long> getQueryUserIds(Uri uri) {
|
||||||
|
@@ -38,6 +38,7 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
import com.android.settings.fuelgauge.BatteryUtils;
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
@@ -125,6 +126,15 @@ public final class ConvertUtils {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Converts {@link BatteryEvent} to content values */
|
||||||
|
public static ContentValues convertBatteryEventToContentValues(final BatteryEvent event) {
|
||||||
|
final ContentValues values = new ContentValues();
|
||||||
|
values.put(BatteryEventEntity.KEY_TIMESTAMP, event.getTimestamp());
|
||||||
|
values.put(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE, event.getType().getNumber());
|
||||||
|
values.put(BatteryEventEntity.KEY_BATTERY_LEVEL, event.getBatteryLevel());
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
/** Gets the encoded string from {@link BatteryInformation} instance. */
|
/** Gets the encoded string from {@link BatteryInformation} instance. */
|
||||||
public static String convertBatteryInformationToString(
|
public static String convertBatteryInformationToString(
|
||||||
final BatteryInformation batteryInformation) {
|
final BatteryInformation batteryInformation) {
|
||||||
@@ -237,6 +247,29 @@ public final class ConvertUtils {
|
|||||||
return eventBuilder.build();
|
return eventBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Converts to {@link BatteryEvent} from {@link BatteryEventType} */
|
||||||
|
public static BatteryEvent convertToBatteryEvent(
|
||||||
|
long timestamp, BatteryEventType type, int batteryLevel) {
|
||||||
|
final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
|
||||||
|
eventBuilder.setTimestamp(timestamp);
|
||||||
|
eventBuilder.setType(type);
|
||||||
|
eventBuilder.setBatteryLevel(batteryLevel);
|
||||||
|
return eventBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Converts to {@link BatteryEvent} from {@link Cursor} */
|
||||||
|
public static BatteryEvent convertToBatteryEventFromCursor(final Cursor cursor) {
|
||||||
|
final BatteryEvent.Builder eventBuilder = BatteryEvent.newBuilder();
|
||||||
|
eventBuilder.setTimestamp(getLongFromCursor(cursor, BatteryEventEntity.KEY_TIMESTAMP));
|
||||||
|
eventBuilder.setType(
|
||||||
|
BatteryEventType.forNumber(
|
||||||
|
getIntegerFromCursor(
|
||||||
|
cursor, BatteryEventEntity.KEY_BATTERY_EVENT_TYPE)));
|
||||||
|
eventBuilder.setBatteryLevel(
|
||||||
|
getIntegerFromCursor(cursor, BatteryEventEntity.KEY_BATTERY_LEVEL));
|
||||||
|
return eventBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
/** Converts UTC timestamp to local time string for logging only, so use the US locale for
|
/** Converts UTC timestamp to local time string for logging only, so use the US locale for
|
||||||
* better readability in debugging. */
|
* better readability in debugging. */
|
||||||
public static String utcToLocalTimeForLogging(long timestamp) {
|
public static String utcToLocalTimeForLogging(long timestamp) {
|
||||||
|
@@ -72,6 +72,8 @@ public class DataProcessManager {
|
|||||||
|
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
private final DataProcessor.UsageMapAsyncResponse mCallbackFunction;
|
private final DataProcessor.UsageMapAsyncResponse mCallbackFunction;
|
||||||
|
private final List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
|
||||||
|
private final List<BatteryEvent> mBatteryEventList = new ArrayList<>();
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private UserManager mUserManager;
|
private UserManager mUserManager;
|
||||||
@@ -84,12 +86,12 @@ public class DataProcessManager {
|
|||||||
private boolean mIsCurrentBatteryHistoryLoaded = false;
|
private boolean mIsCurrentBatteryHistoryLoaded = false;
|
||||||
private boolean mIsCurrentAppUsageLoaded = false;
|
private boolean mIsCurrentAppUsageLoaded = false;
|
||||||
private boolean mIsDatabaseAppUsageLoaded = false;
|
private boolean mIsDatabaseAppUsageLoaded = false;
|
||||||
|
private boolean mIsBatteryEventLoaded = false;
|
||||||
// Used to identify whether screen-on time data should be shown in the UI.
|
// Used to identify whether screen-on time data should be shown in the UI.
|
||||||
private boolean mShowScreenOnTime = true;
|
private boolean mShowScreenOnTime = true;
|
||||||
// Used to identify whether battery level data should be shown in the UI.
|
// Used to identify whether battery level data should be shown in the UI.
|
||||||
private boolean mShowBatteryLevel = true;
|
private boolean mShowBatteryLevel = true;
|
||||||
|
|
||||||
private List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
|
|
||||||
/**
|
/**
|
||||||
* The indexed {@link AppUsagePeriod} list data for each corresponding time slot.
|
* The indexed {@link AppUsagePeriod} list data for each corresponding time slot.
|
||||||
* <p>{@code Long} stands for the userId.</p>
|
* <p>{@code Long} stands for the userId.</p>
|
||||||
@@ -146,6 +148,8 @@ public class DataProcessManager {
|
|||||||
loadDatabaseAppUsageList();
|
loadDatabaseAppUsageList();
|
||||||
// Loads the latest app usage list from the service.
|
// Loads the latest app usage list from the service.
|
||||||
loadCurrentAppUsageList();
|
loadCurrentAppUsageList();
|
||||||
|
// Loads the battery event list from database.
|
||||||
|
loadBatteryEventList();
|
||||||
} else {
|
} else {
|
||||||
// If there is no battery level data, only load the battery history data from service
|
// If there is no battery level data, only load the battery history data from service
|
||||||
// and show it as the app list directly.
|
// and show it as the app list directly.
|
||||||
@@ -174,6 +178,11 @@ public class DataProcessManager {
|
|||||||
return mIsDatabaseAppUsageLoaded;
|
return mIsDatabaseAppUsageLoaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean getIsBatteryEventLoaded() {
|
||||||
|
return mIsBatteryEventLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean getIsCurrentBatteryHistoryLoaded() {
|
boolean getIsCurrentBatteryHistoryLoaded() {
|
||||||
return mIsCurrentBatteryHistoryLoaded;
|
return mIsCurrentBatteryHistoryLoaded;
|
||||||
@@ -290,7 +299,7 @@ public class DataProcessManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final long startTime = System.currentTimeMillis();
|
final long startTime = System.currentTimeMillis();
|
||||||
// Loads the current battery usage data from the battery stats service.
|
// Loads the app usage data from the database.
|
||||||
final List<AppUsageEvent> appUsageEventList =
|
final List<AppUsageEvent> appUsageEventList =
|
||||||
DatabaseUtils.getAppUsageEventForUsers(
|
DatabaseUtils.getAppUsageEventForUsers(
|
||||||
mContext, Calendar.getInstance(), getCurrentUserIds(),
|
mContext, Calendar.getInstance(), getCurrentUserIds(),
|
||||||
@@ -314,6 +323,35 @@ public class DataProcessManager {
|
|||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadBatteryEventList() {
|
||||||
|
new AsyncTask<Void, Void, List<BatteryEvent>>() {
|
||||||
|
@Override
|
||||||
|
protected List<BatteryEvent> doInBackground(Void... voids) {
|
||||||
|
final long startTime = System.currentTimeMillis();
|
||||||
|
// Loads the battery event data from the database.
|
||||||
|
final List<BatteryEvent> batteryEventList =
|
||||||
|
DatabaseUtils.getBatteryEvents(
|
||||||
|
mContext, Calendar.getInstance(), mRawStartTimestamp);
|
||||||
|
Log.d(TAG, String.format("execute loadBatteryEventList size=%d in %d/ms",
|
||||||
|
batteryEventList.size(), (System.currentTimeMillis() - startTime)));
|
||||||
|
return batteryEventList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(
|
||||||
|
final List<BatteryEvent> batteryEventList) {
|
||||||
|
if (batteryEventList == null || batteryEventList.isEmpty()) {
|
||||||
|
Log.d(TAG, "batteryEventList is null or empty");
|
||||||
|
} else {
|
||||||
|
mBatteryEventList.clear();
|
||||||
|
mBatteryEventList.addAll(batteryEventList);
|
||||||
|
}
|
||||||
|
mIsBatteryEventLoaded = true;
|
||||||
|
tryToProcessAppUsageData();
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
|
||||||
private void loadAndApplyBatteryMapFromServiceOnly() {
|
private void loadAndApplyBatteryMapFromServiceOnly() {
|
||||||
new AsyncTask<Void, Void, BatteryCallbackData>() {
|
new AsyncTask<Void, Void, BatteryCallbackData>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -344,9 +382,8 @@ public class DataProcessManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void tryToProcessAppUsageData() {
|
private void tryToProcessAppUsageData() {
|
||||||
// Only when all app usage events has been loaded, start processing app usage data to an
|
// Ignore processing the data if any required data is not loaded.
|
||||||
// intermediate result for further use.
|
if (!mIsCurrentAppUsageLoaded || !mIsDatabaseAppUsageLoaded || !mIsBatteryEventLoaded) {
|
||||||
if (!mIsCurrentAppUsageLoaded || !mIsDatabaseAppUsageLoaded) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
processAppUsageData();
|
processAppUsageData();
|
||||||
@@ -360,16 +397,16 @@ public class DataProcessManager {
|
|||||||
}
|
}
|
||||||
// Generates the indexed AppUsagePeriod list data for each corresponding time slot for
|
// Generates the indexed AppUsagePeriod list data for each corresponding time slot for
|
||||||
// further use.
|
// further use.
|
||||||
mAppUsagePeriodMap = DataProcessor.generateAppUsagePeriodMap(
|
mAppUsagePeriodMap = DataProcessor.generateAppUsagePeriodMap(mRawStartTimestamp,
|
||||||
mRawStartTimestamp, mHourlyBatteryLevelsPerDay, mAppUsageEventList);
|
mHourlyBatteryLevelsPerDay, mAppUsageEventList, mBatteryEventList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryToGenerateFinalDataAndApplyCallback() {
|
private void tryToGenerateFinalDataAndApplyCallback() {
|
||||||
// Only when both battery history data and app usage events data has been loaded, start the
|
// Ignore processing the data if any required data is not loaded.
|
||||||
// final data processing.
|
|
||||||
if (!mIsCurrentBatteryHistoryLoaded
|
if (!mIsCurrentBatteryHistoryLoaded
|
||||||
|| !mIsCurrentAppUsageLoaded
|
|| !mIsCurrentAppUsageLoaded
|
||||||
|| !mIsDatabaseAppUsageLoaded) {
|
|| !mIsDatabaseAppUsageLoaded
|
||||||
|
|| !mIsBatteryEventLoaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
generateFinalDataAndApplyCallback();
|
generateFinalDataAndApplyCallback();
|
||||||
|
@@ -94,6 +94,14 @@ public final class DataProcessor {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static final int SELECTED_INDEX_ALL = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
static final int SELECTED_INDEX_ALL = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final Comparator<AppUsageEvent> APP_USAGE_EVENT_TIMESTAMP_COMPARATOR =
|
||||||
|
Comparator.comparing(AppUsageEvent::getTimestamp);
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static final Comparator<BatteryEvent> BATTERY_EVENT_TIMESTAMP_COMPARATOR =
|
||||||
|
Comparator.comparing(BatteryEvent::getTimestamp);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static boolean sDebug = false;
|
static boolean sDebug = false;
|
||||||
|
|
||||||
@@ -110,8 +118,6 @@ public final class DataProcessor {
|
|||||||
|
|
||||||
public static final String CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER =
|
public static final String CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER =
|
||||||
"CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER";
|
"CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER";
|
||||||
public static final Comparator<AppUsageEvent> TIMESTAMP_COMPARATOR =
|
|
||||||
Comparator.comparing(AppUsageEvent::getTimestamp);
|
|
||||||
|
|
||||||
/** A callback listener when battery usage loading async task is executed. */
|
/** A callback listener when battery usage loading async task is executed. */
|
||||||
public interface UsageMapAsyncResponse {
|
public interface UsageMapAsyncResponse {
|
||||||
@@ -266,14 +272,16 @@ public final class DataProcessor {
|
|||||||
generateAppUsagePeriodMap(
|
generateAppUsagePeriodMap(
|
||||||
final long rawStartTimestamp,
|
final long rawStartTimestamp,
|
||||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
|
||||||
final List<AppUsageEvent> appUsageEventList) {
|
final List<AppUsageEvent> appUsageEventList,
|
||||||
|
final List<BatteryEvent> batteryEventList) {
|
||||||
if (appUsageEventList.isEmpty()) {
|
if (appUsageEventList.isEmpty()) {
|
||||||
Log.w(TAG, "appUsageEventList is empty");
|
Log.w(TAG, "appUsageEventList is empty");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Sorts the appUsageEventList in ascending order based on the timestamp before
|
// Sorts the appUsageEventList and batteryEventList in ascending order based on the
|
||||||
// distribution.
|
// timestamp before distribution.
|
||||||
Collections.sort(appUsageEventList, TIMESTAMP_COMPARATOR);
|
Collections.sort(appUsageEventList, APP_USAGE_EVENT_TIMESTAMP_COMPARATOR);
|
||||||
|
Collections.sort(batteryEventList, BATTERY_EVENT_TIMESTAMP_COMPARATOR);
|
||||||
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> resultMap =
|
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> resultMap =
|
||||||
new ArrayMap<>();
|
new ArrayMap<>();
|
||||||
|
|
||||||
@@ -309,8 +317,8 @@ public final class DataProcessor {
|
|||||||
// The value could be null when there is no data in the hourly slot.
|
// The value could be null when there is no data in the hourly slot.
|
||||||
dailyMap.put(
|
dailyMap.put(
|
||||||
hourlyIndex,
|
hourlyIndex,
|
||||||
buildAppUsagePeriodList(
|
buildAppUsagePeriodList(hourlyAppUsageEventList, batteryEventList,
|
||||||
hourlyAppUsageEventList, startTimestamp, endTimestamp));
|
startTimestamp, endTimestamp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resultMap;
|
return resultMap;
|
||||||
@@ -717,7 +725,8 @@ public final class DataProcessor {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@Nullable
|
@Nullable
|
||||||
static Map<Long, Map<String, List<AppUsagePeriod>>> buildAppUsagePeriodList(
|
static Map<Long, Map<String, List<AppUsagePeriod>>> buildAppUsagePeriodList(
|
||||||
final List<AppUsageEvent> allAppUsageEvents, final long startTime, final long endTime) {
|
final List<AppUsageEvent> allAppUsageEvents, final List<BatteryEvent> batteryEventList,
|
||||||
|
final long startTime, final long endTime) {
|
||||||
if (allAppUsageEvents.isEmpty()) {
|
if (allAppUsageEvents.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -763,12 +772,14 @@ public final class DataProcessor {
|
|||||||
usageEvents.addAll(deviceEvents);
|
usageEvents.addAll(deviceEvents);
|
||||||
// Sorts the usageEvents in ascending order based on the timestamp before computing the
|
// Sorts the usageEvents in ascending order based on the timestamp before computing the
|
||||||
// period.
|
// period.
|
||||||
Collections.sort(usageEvents, TIMESTAMP_COMPARATOR);
|
Collections.sort(usageEvents, APP_USAGE_EVENT_TIMESTAMP_COMPARATOR);
|
||||||
|
|
||||||
// A package might have multiple instances. Computes the usage period per instance id
|
// A package might have multiple instances. Computes the usage period per instance id
|
||||||
// and then merges them into the same user-package map.
|
// and then merges them into the same user-package map.
|
||||||
final List<AppUsagePeriod> usagePeriodList =
|
final List<AppUsagePeriod> usagePeriodList =
|
||||||
buildAppUsagePeriodListPerInstance(usageEvents, startTime, endTime);
|
excludePowerConnectedTimeFromAppUsagePeriodList(
|
||||||
|
buildAppUsagePeriodListPerInstance(usageEvents, startTime, endTime),
|
||||||
|
batteryEventList);
|
||||||
if (!usagePeriodList.isEmpty()) {
|
if (!usagePeriodList.isEmpty()) {
|
||||||
addToUsagePeriodMap(allUsagePeriods, usagePeriodList, eventUserId, packageName);
|
addToUsagePeriodMap(allUsagePeriods, usagePeriodList, eventUserId, packageName);
|
||||||
}
|
}
|
||||||
@@ -836,6 +847,53 @@ public final class DataProcessor {
|
|||||||
return usagePeriodList;
|
return usagePeriodList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static List<AppUsagePeriod> excludePowerConnectedTimeFromAppUsagePeriodList(
|
||||||
|
final List<AppUsagePeriod> usagePeriodList,
|
||||||
|
final List<BatteryEvent> batteryEventList) {
|
||||||
|
final List<AppUsagePeriod> resultList = new ArrayList<>();
|
||||||
|
for (AppUsagePeriod inputPeriod : usagePeriodList) {
|
||||||
|
long lastStartTime = inputPeriod.getStartTime();
|
||||||
|
for (BatteryEvent batteryEvent : batteryEventList) {
|
||||||
|
if (batteryEvent.getTimestamp() < inputPeriod.getStartTime()) {
|
||||||
|
// Because the batteryEventList has been sorted, here is to mark the power
|
||||||
|
// connection state when the usage period starts. If power is connected when
|
||||||
|
// the usage period starts, the starting period will be ignored; otherwise it
|
||||||
|
// will be added.
|
||||||
|
if (batteryEvent.getType() == BatteryEventType.POWER_CONNECTED) {
|
||||||
|
lastStartTime = 0;
|
||||||
|
} else if (batteryEvent.getType() == BatteryEventType.POWER_DISCONNECTED) {
|
||||||
|
lastStartTime = inputPeriod.getStartTime();
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (batteryEvent.getTimestamp() > inputPeriod.getEndTime()) {
|
||||||
|
// Because the batteryEventList has been sorted, if any event is already after
|
||||||
|
// the end time, all the following events should be able to drop directly.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batteryEvent.getType() == BatteryEventType.POWER_CONNECTED
|
||||||
|
&& lastStartTime != 0) {
|
||||||
|
resultList.add(AppUsagePeriod.newBuilder()
|
||||||
|
.setStartTime(lastStartTime)
|
||||||
|
.setEndTime(batteryEvent.getTimestamp())
|
||||||
|
.build());
|
||||||
|
lastStartTime = 0;
|
||||||
|
} else if (batteryEvent.getType() == BatteryEventType.POWER_DISCONNECTED) {
|
||||||
|
lastStartTime = batteryEvent.getTimestamp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastStartTime != 0) {
|
||||||
|
resultList.add(AppUsagePeriod.newBuilder()
|
||||||
|
.setStartTime(lastStartTime)
|
||||||
|
.setEndTime(inputPeriod.getEndTime())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static long getScreenOnTime(
|
static long getScreenOnTime(
|
||||||
final Map<Long, Map<String, List<AppUsagePeriod>>> appUsageMap,
|
final Map<Long, Map<String, List<AppUsagePeriod>>> appUsageMap,
|
||||||
|
@@ -64,10 +64,12 @@ public final class DatabaseUtils {
|
|||||||
|
|
||||||
/** An authority name of the battery content provider. */
|
/** An authority name of the battery content provider. */
|
||||||
public static final String AUTHORITY = "com.android.settings.battery.usage.provider";
|
public static final String AUTHORITY = "com.android.settings.battery.usage.provider";
|
||||||
/** A table name for battery usage history. */
|
|
||||||
public static final String BATTERY_STATE_TABLE = "BatteryState";
|
|
||||||
/** A table name for app usage events. */
|
/** A table name for app usage events. */
|
||||||
public static final String APP_USAGE_EVENT_TABLE = "AppUsageEvent";
|
public static final String APP_USAGE_EVENT_TABLE = "AppUsageEvent";
|
||||||
|
/** A table name for battery events. */
|
||||||
|
public static final String BATTERY_EVENT_TABLE = "BatteryEvent";
|
||||||
|
/** A table name for battery usage history. */
|
||||||
|
public static final String BATTERY_STATE_TABLE = "BatteryState";
|
||||||
/** A path name for app usage latest timestamp query. */
|
/** A path name for app usage latest timestamp query. */
|
||||||
public static final String APP_USAGE_LATEST_TIMESTAMP_PATH = "appUsageLatestTimestamp";
|
public static final String APP_USAGE_LATEST_TIMESTAMP_PATH = "appUsageLatestTimestamp";
|
||||||
/** A class name for battery usage data provider. */
|
/** A class name for battery usage data provider. */
|
||||||
@@ -84,13 +86,6 @@ public final class DatabaseUtils {
|
|||||||
*/
|
*/
|
||||||
public static final long USAGE_QUERY_BUFFER_HOURS = Duration.ofHours(3).toMillis();
|
public static final long USAGE_QUERY_BUFFER_HOURS = Duration.ofHours(3).toMillis();
|
||||||
|
|
||||||
/** A content URI to access battery usage states data. */
|
|
||||||
public static final Uri BATTERY_CONTENT_URI =
|
|
||||||
new Uri.Builder()
|
|
||||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
|
||||||
.authority(AUTHORITY)
|
|
||||||
.appendPath(BATTERY_STATE_TABLE)
|
|
||||||
.build();
|
|
||||||
/** A content URI to access app usage events data. */
|
/** A content URI to access app usage events data. */
|
||||||
public static final Uri APP_USAGE_EVENT_URI =
|
public static final Uri APP_USAGE_EVENT_URI =
|
||||||
new Uri.Builder()
|
new Uri.Builder()
|
||||||
@@ -98,6 +93,20 @@ public final class DatabaseUtils {
|
|||||||
.authority(AUTHORITY)
|
.authority(AUTHORITY)
|
||||||
.appendPath(APP_USAGE_EVENT_TABLE)
|
.appendPath(APP_USAGE_EVENT_TABLE)
|
||||||
.build();
|
.build();
|
||||||
|
/** A content URI to access battery events data. */
|
||||||
|
public static final Uri BATTERY_EVENT_URI =
|
||||||
|
new Uri.Builder()
|
||||||
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||||
|
.authority(AUTHORITY)
|
||||||
|
.appendPath(BATTERY_EVENT_TABLE)
|
||||||
|
.build();
|
||||||
|
/** A content URI to access battery usage states data. */
|
||||||
|
public static final Uri BATTERY_CONTENT_URI =
|
||||||
|
new Uri.Builder()
|
||||||
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||||
|
.authority(AUTHORITY)
|
||||||
|
.appendPath(BATTERY_STATE_TABLE)
|
||||||
|
.build();
|
||||||
|
|
||||||
// For testing only.
|
// For testing only.
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -106,6 +115,8 @@ public final class DatabaseUtils {
|
|||||||
static Supplier<Cursor> sFakeAppUsageEventSupplier;
|
static Supplier<Cursor> sFakeAppUsageEventSupplier;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static Supplier<Cursor> sFakeAppUsageLatestTimestampSupplier;
|
static Supplier<Cursor> sFakeAppUsageLatestTimestampSupplier;
|
||||||
|
@VisibleForTesting
|
||||||
|
static Supplier<Cursor> sFakeBatteryEventSupplier;
|
||||||
|
|
||||||
private DatabaseUtils() {
|
private DatabaseUtils() {
|
||||||
}
|
}
|
||||||
@@ -176,6 +187,32 @@ public final class DatabaseUtils {
|
|||||||
return appUsageEventList;
|
return appUsageEventList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the battery event data since the query timestamp in battery event table. */
|
||||||
|
public static List<BatteryEvent> getBatteryEvents(
|
||||||
|
Context context,
|
||||||
|
final Calendar calendar,
|
||||||
|
final long rawStartTimestamp) {
|
||||||
|
final long startTime = System.currentTimeMillis();
|
||||||
|
final long sixDaysAgoTimestamp = getTimestampSixDaysAgo(calendar);
|
||||||
|
final long queryTimestamp = Math.max(rawStartTimestamp, sixDaysAgoTimestamp);
|
||||||
|
Log.d(TAG, "getBatteryEvents for timestamp: " + queryTimestamp);
|
||||||
|
// Builds the content uri everytime to avoid cache.
|
||||||
|
final Uri batteryEventUri =
|
||||||
|
new Uri.Builder()
|
||||||
|
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||||
|
.authority(AUTHORITY)
|
||||||
|
.appendPath(BATTERY_EVENT_TABLE)
|
||||||
|
.appendQueryParameter(
|
||||||
|
QUERY_KEY_TIMESTAMP, Long.toString(queryTimestamp))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final List<BatteryEvent> batteryEventList =
|
||||||
|
loadBatteryEventsFromContentProvider(context, batteryEventUri);
|
||||||
|
Log.d(TAG, String.format("getBatteryEvents size=%d in %d/ms", batteryEventList.size(),
|
||||||
|
(System.currentTimeMillis() - startTime)));
|
||||||
|
return batteryEventList;
|
||||||
|
}
|
||||||
|
|
||||||
/** Long: for timestamp and String: for BatteryHistEntry.getKey() */
|
/** Long: for timestamp and String: for BatteryHistEntry.getKey() */
|
||||||
public static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceLastFullCharge(
|
public static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceLastFullCharge(
|
||||||
Context context, Calendar calendar) {
|
Context context, Calendar calendar) {
|
||||||
@@ -210,8 +247,9 @@ public final class DatabaseUtils {
|
|||||||
try {
|
try {
|
||||||
final BatteryStateDatabase database = BatteryStateDatabase
|
final BatteryStateDatabase database = BatteryStateDatabase
|
||||||
.getInstance(context.getApplicationContext());
|
.getInstance(context.getApplicationContext());
|
||||||
database.batteryStateDao().clearAll();
|
|
||||||
database.appUsageEventDao().clearAll();
|
database.appUsageEventDao().clearAll();
|
||||||
|
database.batteryEventDao().clearAll();
|
||||||
|
database.batteryStateDao().clearAll();
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
Log.e(TAG, "clearAll() failed", e);
|
Log.e(TAG, "clearAll() failed", e);
|
||||||
}
|
}
|
||||||
@@ -226,8 +264,9 @@ public final class DatabaseUtils {
|
|||||||
.getInstance(context.getApplicationContext());
|
.getInstance(context.getApplicationContext());
|
||||||
final long earliestTimestamp = Clock.systemUTC().millis()
|
final long earliestTimestamp = Clock.systemUTC().millis()
|
||||||
- Duration.ofDays(DATA_RETENTION_INTERVAL_DAY).toMillis();
|
- Duration.ofDays(DATA_RETENTION_INTERVAL_DAY).toMillis();
|
||||||
database.batteryStateDao().clearAllBefore(earliestTimestamp);
|
|
||||||
database.appUsageEventDao().clearAllBefore(earliestTimestamp);
|
database.appUsageEventDao().clearAllBefore(earliestTimestamp);
|
||||||
|
database.batteryEventDao().clearAllBefore(earliestTimestamp);
|
||||||
|
database.batteryStateDao().clearAllBefore(earliestTimestamp);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
Log.e(TAG, "clearAllBefore() failed", e);
|
Log.e(TAG, "clearAllBefore() failed", e);
|
||||||
}
|
}
|
||||||
@@ -292,6 +331,23 @@ public final class DatabaseUtils {
|
|||||||
return valuesList;
|
return valuesList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ContentValues sendBatteryEventData(
|
||||||
|
final Context context, final BatteryEvent batteryEvent) {
|
||||||
|
final long startTime = System.currentTimeMillis();
|
||||||
|
ContentValues contentValues = ConvertUtils.convertBatteryEventToContentValues(batteryEvent);
|
||||||
|
final ContentResolver resolver = context.getContentResolver();
|
||||||
|
try {
|
||||||
|
resolver.insert(BATTERY_EVENT_URI, contentValues);
|
||||||
|
Log.d(TAG, "insert() battery event data into database: " + batteryEvent.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "insert() battery event data into database error:\n" + e);
|
||||||
|
}
|
||||||
|
Log.d(TAG, String.format("sendBatteryEventData() in %d/ms",
|
||||||
|
(System.currentTimeMillis() - startTime)));
|
||||||
|
clearMemory();
|
||||||
|
return contentValues;
|
||||||
|
}
|
||||||
|
|
||||||
static List<ContentValues> sendBatteryEntryData(
|
static List<ContentValues> sendBatteryEntryData(
|
||||||
final Context context,
|
final Context context,
|
||||||
final List<BatteryEntry> batteryEntryList,
|
final List<BatteryEntry> batteryEntryList,
|
||||||
@@ -392,6 +448,8 @@ public final class DatabaseUtils {
|
|||||||
public static void dump(Context context, PrintWriter writer) {
|
public static void dump(Context context, PrintWriter writer) {
|
||||||
writeString(context, writer, "BatteryLevelChanged",
|
writeString(context, writer, "BatteryLevelChanged",
|
||||||
Intent.ACTION_BATTERY_LEVEL_CHANGED);
|
Intent.ACTION_BATTERY_LEVEL_CHANGED);
|
||||||
|
writeString(context, writer, "BatteryPlugging",
|
||||||
|
BatteryUsageBroadcastReceiver.ACTION_BATTERY_PLUGGING);
|
||||||
writeString(context, writer, "BatteryUnplugging",
|
writeString(context, writer, "BatteryUnplugging",
|
||||||
BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING);
|
BatteryUsageBroadcastReceiver.ACTION_BATTERY_UNPLUGGING);
|
||||||
writeString(context, writer, "ClearBatteryCacheData",
|
writeString(context, writer, "ClearBatteryCacheData",
|
||||||
@@ -475,6 +533,32 @@ public final class DatabaseUtils {
|
|||||||
return appUsageEventList;
|
return appUsageEventList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<BatteryEvent> loadBatteryEventsFromContentProvider(
|
||||||
|
Context context, Uri batteryEventUri) {
|
||||||
|
final List<BatteryEvent> batteryEventList = new ArrayList<>();
|
||||||
|
context = getParentContext(context);
|
||||||
|
if (context == null) {
|
||||||
|
return batteryEventList;
|
||||||
|
}
|
||||||
|
try (Cursor cursor = sFakeBatteryEventSupplier != null
|
||||||
|
? sFakeBatteryEventSupplier.get()
|
||||||
|
: context.getContentResolver().query(batteryEventUri, null, null, null)) {
|
||||||
|
if (cursor == null || cursor.getCount() == 0) {
|
||||||
|
return batteryEventList;
|
||||||
|
}
|
||||||
|
// Loads and recovers all AppUsageEvent data from cursor.
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
batteryEventList.add(ConvertUtils.convertToBatteryEventFromCursor(cursor));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cursor.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "cursor.close() failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return batteryEventList;
|
||||||
|
}
|
||||||
|
|
||||||
private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
|
private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
|
||||||
Context context, Uri batteryStateUri) {
|
Context context, Uri batteryStateUri) {
|
||||||
context = DatabaseUtils.getParentContext(context);
|
context = DatabaseUtils.getParentContext(context);
|
||||||
|
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.batteryusage.db;
|
||||||
|
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import androidx.room.Dao;
|
||||||
|
import androidx.room.Insert;
|
||||||
|
import androidx.room.OnConflictStrategy;
|
||||||
|
import androidx.room.Query;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Data access object for accessing {@link BatteryEventEntity} in the database. */
|
||||||
|
@Dao
|
||||||
|
public interface BatteryEventDao {
|
||||||
|
/** Inserts a {@link BatteryEventEntity} data into the database. */
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
void insert(BatteryEventEntity event);
|
||||||
|
|
||||||
|
/** Gets all recorded data. */
|
||||||
|
@Query("SELECT * FROM BatteryEventEntity ORDER BY timestamp DESC")
|
||||||
|
List<BatteryEventEntity> getAll();
|
||||||
|
|
||||||
|
/** Gets the {@link Cursor} of all recorded data after a specific timestamp. */
|
||||||
|
@Query("SELECT * FROM BatteryEventEntity WHERE timestamp > :timestamp ORDER BY timestamp DESC")
|
||||||
|
Cursor getAllAfter(long timestamp);
|
||||||
|
|
||||||
|
/** Deletes all recorded data before a specific timestamp. */
|
||||||
|
@Query("DELETE FROM BatteryEventEntity WHERE timestamp <= :timestamp")
|
||||||
|
void clearAllBefore(long timestamp);
|
||||||
|
|
||||||
|
/** Clears all recorded data in the database. */
|
||||||
|
@Query("DELETE FROM BatteryEventEntity")
|
||||||
|
void clearAll();
|
||||||
|
}
|
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.batteryusage.db;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
|
||||||
|
import androidx.room.Entity;
|
||||||
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
|
||||||
|
|
||||||
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/** A {@link Entity} class to save battery events into database. */
|
||||||
|
@Entity
|
||||||
|
public class BatteryEventEntity {
|
||||||
|
/** Keys for accessing {@link ContentValues}. */
|
||||||
|
public static final String KEY_TIMESTAMP = "timestamp";
|
||||||
|
public static final String KEY_BATTERY_EVENT_TYPE = "batteryEventType";
|
||||||
|
public static final String KEY_BATTERY_LEVEL = "batteryLevel";
|
||||||
|
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
private long mId;
|
||||||
|
|
||||||
|
public final long timestamp;
|
||||||
|
public final int batteryEventType;
|
||||||
|
public final int batteryLevel;
|
||||||
|
|
||||||
|
public BatteryEventEntity(
|
||||||
|
final long timestamp,
|
||||||
|
final int batteryEventType,
|
||||||
|
final int batteryLevel) {
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.batteryEventType = batteryEventType;
|
||||||
|
this.batteryLevel = batteryLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the auto-generated content ID. */
|
||||||
|
public void setId(long id) {
|
||||||
|
this.mId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the auto-generated content ID. */
|
||||||
|
public long getId() {
|
||||||
|
return mId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(timestamp);
|
||||||
|
final StringBuilder builder = new StringBuilder()
|
||||||
|
.append("\nBatteryEvent{")
|
||||||
|
.append(String.format(Locale.US,
|
||||||
|
"\n\ttimestamp=%s|batteryEventType=%d|batteryLevel=%d",
|
||||||
|
recordAtDateTime, batteryEventType, batteryLevel))
|
||||||
|
.append("\n}");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates new {@link BatteryEventEntity} from {@link ContentValues}. */
|
||||||
|
public static BatteryEventEntity create(ContentValues contentValues) {
|
||||||
|
Builder builder = BatteryEventEntity.newBuilder();
|
||||||
|
if (contentValues.containsKey(KEY_TIMESTAMP)) {
|
||||||
|
builder.setTimestamp(contentValues.getAsLong(KEY_TIMESTAMP));
|
||||||
|
}
|
||||||
|
if (contentValues.containsKey(KEY_BATTERY_EVENT_TYPE)) {
|
||||||
|
builder.setBatteryEventType(contentValues.getAsInteger(KEY_BATTERY_EVENT_TYPE));
|
||||||
|
}
|
||||||
|
if (contentValues.containsKey(KEY_BATTERY_LEVEL)) {
|
||||||
|
builder.setBatteryLevel(contentValues.getAsInteger(KEY_BATTERY_LEVEL));
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a new {@link Builder} instance. */
|
||||||
|
public static Builder newBuilder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A convenience builder class to improve readability. */
|
||||||
|
public static class Builder {
|
||||||
|
private long mTimestamp;
|
||||||
|
private int mBatteryEventType;
|
||||||
|
private int mBatteryLevel;
|
||||||
|
|
||||||
|
/** Sets the timestamp. */
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setTimestamp(final long timestamp) {
|
||||||
|
mTimestamp = timestamp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the battery event type. */
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setBatteryEventType(final int batteryEventType) {
|
||||||
|
mBatteryEventType = batteryEventType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the battery level. */
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public Builder setBatteryLevel(final int batteryLevel) {
|
||||||
|
mBatteryLevel = batteryLevel;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builds the {@link BatteryEventEntity}. */
|
||||||
|
public BatteryEventEntity build() {
|
||||||
|
return new BatteryEventEntity(
|
||||||
|
mTimestamp,
|
||||||
|
mBatteryEventType,
|
||||||
|
mBatteryLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Builder() {}
|
||||||
|
}
|
||||||
|
}
|
@@ -25,24 +25,26 @@ import androidx.room.RoomDatabase;
|
|||||||
|
|
||||||
/** A {@link RoomDatabase} for battery usage states history. */
|
/** A {@link RoomDatabase} for battery usage states history. */
|
||||||
@Database(
|
@Database(
|
||||||
entities = {BatteryState.class, AppUsageEventEntity.class},
|
entities = {AppUsageEventEntity.class, BatteryEventEntity.class, BatteryState.class},
|
||||||
version = 1)
|
version = 1)
|
||||||
public abstract class BatteryStateDatabase extends RoomDatabase {
|
public abstract class BatteryStateDatabase extends RoomDatabase {
|
||||||
private static final String TAG = "BatteryStateDatabase";
|
private static final String TAG = "BatteryStateDatabase";
|
||||||
|
|
||||||
private static BatteryStateDatabase sBatteryStateDatabase;
|
private static BatteryStateDatabase sBatteryStateDatabase;
|
||||||
|
|
||||||
/** Provides DAO for battery state table. */
|
|
||||||
public abstract BatteryStateDao batteryStateDao();
|
|
||||||
/** Provides DAO for app usage event table. */
|
/** Provides DAO for app usage event table. */
|
||||||
public abstract AppUsageEventDao appUsageEventDao();
|
public abstract AppUsageEventDao appUsageEventDao();
|
||||||
|
/** Provides DAO for battery event table. */
|
||||||
|
public abstract BatteryEventDao batteryEventDao();
|
||||||
|
/** Provides DAO for battery state table. */
|
||||||
|
public abstract BatteryStateDao batteryStateDao();
|
||||||
|
|
||||||
/** Gets or creates an instance of {@link RoomDatabase}. */
|
/** Gets or creates an instance of {@link RoomDatabase}. */
|
||||||
public static BatteryStateDatabase getInstance(Context context) {
|
public static BatteryStateDatabase getInstance(Context context) {
|
||||||
if (sBatteryStateDatabase == null) {
|
if (sBatteryStateDatabase == null) {
|
||||||
sBatteryStateDatabase =
|
sBatteryStateDatabase =
|
||||||
Room.databaseBuilder(
|
Room.databaseBuilder(
|
||||||
context, BatteryStateDatabase.class, "battery-usage-db-v7")
|
context, BatteryStateDatabase.class, "battery-usage-db-v8")
|
||||||
// Allows accessing data in the main thread for dumping bugreport.
|
// Allows accessing data in the main thread for dumping bugreport.
|
||||||
.allowMainThreadQueries()
|
.allowMainThreadQueries()
|
||||||
.fallbackToDestructiveMigration()
|
.fallbackToDestructiveMigration()
|
||||||
|
@@ -7,14 +7,6 @@ package {
|
|||||||
default_applicable_licenses: ["packages_apps_Settings_license"],
|
default_applicable_licenses: ["packages_apps_Settings_license"],
|
||||||
}
|
}
|
||||||
|
|
||||||
java_library {
|
|
||||||
name: "fuelgauge-usage-state-protos-lite",
|
|
||||||
proto: {
|
|
||||||
type: "lite",
|
|
||||||
},
|
|
||||||
srcs: ["fuelgauge_usage_state.proto"],
|
|
||||||
}
|
|
||||||
|
|
||||||
java_library {
|
java_library {
|
||||||
name: "app-usage-event-protos-lite",
|
name: "app-usage-event-protos-lite",
|
||||||
proto: {
|
proto: {
|
||||||
@@ -22,3 +14,19 @@ java_library {
|
|||||||
},
|
},
|
||||||
srcs: ["app_usage_event.proto"],
|
srcs: ["app_usage_event.proto"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "battery-event-protos-lite",
|
||||||
|
proto: {
|
||||||
|
type: "lite",
|
||||||
|
},
|
||||||
|
srcs: ["battery_event.proto"],
|
||||||
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "fuelgauge-usage-state-protos-lite",
|
||||||
|
proto: {
|
||||||
|
type: "lite",
|
||||||
|
},
|
||||||
|
srcs: ["fuelgauge_usage_state.proto"],
|
||||||
|
}
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
option java_multiple_files = true;
|
||||||
|
option java_package = "com.android.settings.fuelgauge.batteryusage";
|
||||||
|
option java_outer_classname = "BatteryEventProto";
|
||||||
|
|
||||||
|
enum BatteryEventType {
|
||||||
|
UNKNOWN_EVENT = 0;
|
||||||
|
POWER_CONNECTED = 1;
|
||||||
|
POWER_DISCONNECTED = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BatteryEvent {
|
||||||
|
optional int64 timestamp = 1;
|
||||||
|
optional BatteryEventType type = 2;
|
||||||
|
optional int32 battery_level = 3;
|
||||||
|
}
|
@@ -31,6 +31,7 @@ import android.net.Uri;
|
|||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
|
||||||
import com.android.settings.testutils.BatteryTestUtils;
|
import com.android.settings.testutils.BatteryTestUtils;
|
||||||
@@ -353,6 +354,28 @@ public final class BatteryUsageContentProviderTest {
|
|||||||
assertThat(entities.get(0).taskRootPackageName).isEqualTo("com.android.settings2");
|
assertThat(entities.get(0).taskRootPackageName).isEqualTo("com.android.settings2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void insert_batteryEvent_returnsExpectedResult() {
|
||||||
|
mProvider.onCreate();
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(BatteryEventEntity.KEY_TIMESTAMP, 10001L);
|
||||||
|
values.put(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE,
|
||||||
|
BatteryEventType.POWER_CONNECTED.getNumber());
|
||||||
|
values.put(BatteryEventEntity.KEY_BATTERY_LEVEL, 66);
|
||||||
|
|
||||||
|
final Uri uri = mProvider.insert(DatabaseUtils.BATTERY_EVENT_URI, values);
|
||||||
|
|
||||||
|
assertThat(uri).isEqualTo(DatabaseUtils.BATTERY_EVENT_URI);
|
||||||
|
// Verifies the AppUsageEventEntity content.
|
||||||
|
final List<BatteryEventEntity> entities =
|
||||||
|
BatteryStateDatabase.getInstance(mContext).batteryEventDao().getAll();
|
||||||
|
assertThat(entities).hasSize(1);
|
||||||
|
assertThat(entities.get(0).timestamp).isEqualTo(10001L);
|
||||||
|
assertThat(entities.get(0).batteryEventType).isEqualTo(
|
||||||
|
BatteryEventType.POWER_CONNECTED.getNumber());
|
||||||
|
assertThat(entities.get(0).batteryLevel).isEqualTo(66);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void delete_throwsUnsupportedOperationException() {
|
public void delete_throwsUnsupportedOperationException() {
|
||||||
assertThrows(
|
assertThrows(
|
||||||
|
@@ -39,6 +39,7 @@ import android.os.RemoteException;
|
|||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -194,6 +195,22 @@ public final class ConvertUtilsTest {
|
|||||||
.isEqualTo("com.android.settings2");
|
.isEqualTo("com.android.settings2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertBatteryEventToContentValues_normalCase_returnsExpectedContentValues() {
|
||||||
|
final BatteryEvent batteryEvent =
|
||||||
|
BatteryEvent.newBuilder()
|
||||||
|
.setTimestamp(10001L)
|
||||||
|
.setType(BatteryEventType.POWER_CONNECTED)
|
||||||
|
.setBatteryLevel(66)
|
||||||
|
.build();
|
||||||
|
final ContentValues values =
|
||||||
|
ConvertUtils.convertBatteryEventToContentValues(batteryEvent);
|
||||||
|
assertThat(values.getAsLong(BatteryEventEntity.KEY_TIMESTAMP)).isEqualTo(10001L);
|
||||||
|
assertThat(values.getAsInteger(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE)).isEqualTo(
|
||||||
|
BatteryEventType.POWER_CONNECTED.getNumber());
|
||||||
|
assertThat(values.getAsInteger(BatteryEventEntity.KEY_BATTERY_LEVEL)).isEqualTo(66);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void convertToBatteryHistEntry_returnsExpectedResult() {
|
public void convertToBatteryHistEntry_returnsExpectedResult() {
|
||||||
final int expectedType = 3;
|
final int expectedType = 3;
|
||||||
@@ -405,6 +422,15 @@ public final class ConvertUtilsTest {
|
|||||||
assertThat(appUsageEvent.getInstanceId()).isEqualTo(0);
|
assertThat(appUsageEvent.getInstanceId()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void convertToBatteryEvent_normalCase_returnsExpectedResult() {
|
||||||
|
final BatteryEvent batteryEvent = ConvertUtils.convertToBatteryEvent(
|
||||||
|
666L, BatteryEventType.POWER_DISCONNECTED, 88);
|
||||||
|
assertThat(batteryEvent.getTimestamp()).isEqualTo(666L);
|
||||||
|
assertThat(batteryEvent.getType()).isEqualTo(BatteryEventType.POWER_DISCONNECTED);
|
||||||
|
assertThat(batteryEvent.getBatteryLevel()).isEqualTo(88);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getLocale_nullContext_returnDefaultLocale() {
|
public void getLocale_nullContext_returnDefaultLocale() {
|
||||||
assertThat(ConvertUtils.getLocale(/*context=*/ null))
|
assertThat(ConvertUtils.getLocale(/*context=*/ null))
|
||||||
|
@@ -183,7 +183,7 @@ public final class DataProcessManagerTest {
|
|||||||
assertThat(dataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
|
assertThat(dataProcessManager.getIsCurrentBatteryHistoryLoaded()).isTrue();
|
||||||
assertThat(dataProcessManager.getShowScreenOnTime()).isTrue();
|
assertThat(dataProcessManager.getShowScreenOnTime()).isTrue();
|
||||||
final List<AppUsageEvent> appUsageEventList = dataProcessManager.getAppUsageEventList();
|
final List<AppUsageEvent> appUsageEventList = dataProcessManager.getAppUsageEventList();
|
||||||
Collections.sort(appUsageEventList, DataProcessor.TIMESTAMP_COMPARATOR);
|
Collections.sort(appUsageEventList, DataProcessor.APP_USAGE_EVENT_TIMESTAMP_COMPARATOR);
|
||||||
assertThat(appUsageEventList.size()).isEqualTo(6);
|
assertThat(appUsageEventList.size()).isEqualTo(6);
|
||||||
assertAppUsageEvent(
|
assertAppUsageEvent(
|
||||||
appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1);
|
appUsageEventList.get(0), AppUsageEventType.ACTIVITY_RESUMED, /*timestamp=*/ 1);
|
||||||
|
@@ -250,7 +250,7 @@ public final class DataProcessorTest {
|
|||||||
|
|
||||||
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> periodMap =
|
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> periodMap =
|
||||||
DataProcessor.generateAppUsagePeriodMap(
|
DataProcessor.generateAppUsagePeriodMap(
|
||||||
14400000L, hourlyBatteryLevelsPerDay, appUsageEventList);
|
14400000L, hourlyBatteryLevelsPerDay, appUsageEventList, new ArrayList<>());
|
||||||
|
|
||||||
assertThat(periodMap).hasSize(3);
|
assertThat(periodMap).hasSize(3);
|
||||||
// Day 1
|
// Day 1
|
||||||
@@ -288,7 +288,7 @@ public final class DataProcessorTest {
|
|||||||
hourlyBatteryLevelsPerDay.add(
|
hourlyBatteryLevelsPerDay.add(
|
||||||
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
|
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
|
||||||
assertThat(DataProcessor.generateAppUsagePeriodMap(
|
assertThat(DataProcessor.generateAppUsagePeriodMap(
|
||||||
0L, hourlyBatteryLevelsPerDay, new ArrayList<>())).isNull();
|
0L, hourlyBatteryLevelsPerDay, new ArrayList<>(), new ArrayList<>())).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1669,7 +1669,8 @@ public final class DataProcessorTest {
|
|||||||
/*instanceId=*/ 4, packageName2));
|
/*instanceId=*/ 4, packageName2));
|
||||||
|
|
||||||
final Map<Long, Map<String, List<AppUsagePeriod>>> appUsagePeriodMap =
|
final Map<Long, Map<String, List<AppUsagePeriod>>> appUsagePeriodMap =
|
||||||
DataProcessor.buildAppUsagePeriodList(appUsageEvents, 0, 5);
|
DataProcessor.buildAppUsagePeriodList(
|
||||||
|
appUsageEvents, new ArrayList<>(), 0, 5);
|
||||||
|
|
||||||
assertThat(appUsagePeriodMap).hasSize(2);
|
assertThat(appUsagePeriodMap).hasSize(2);
|
||||||
final Map<String, List<AppUsagePeriod>> userMap1 = appUsagePeriodMap.get(1L);
|
final Map<String, List<AppUsagePeriod>> userMap1 = appUsagePeriodMap.get(1L);
|
||||||
@@ -1693,7 +1694,7 @@ public final class DataProcessorTest {
|
|||||||
@Test
|
@Test
|
||||||
public void buildAppUsagePeriodList_emptyEventList_returnNull() {
|
public void buildAppUsagePeriodList_emptyEventList_returnNull() {
|
||||||
assertThat(DataProcessor.buildAppUsagePeriodList(
|
assertThat(DataProcessor.buildAppUsagePeriodList(
|
||||||
new ArrayList<>(), 0, 1)).isNull();
|
new ArrayList<>(), new ArrayList<>(), 0, 1)).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1705,7 +1706,7 @@ public final class DataProcessorTest {
|
|||||||
AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 2));
|
AppUsageEventType.DEVICE_SHUTDOWN, /*timestamp=*/ 2));
|
||||||
|
|
||||||
assertThat(DataProcessor.buildAppUsagePeriodList(
|
assertThat(DataProcessor.buildAppUsagePeriodList(
|
||||||
appUsageEvents, 0, 3)).isNull();
|
appUsageEvents, new ArrayList<>(), 0, 3)).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1765,6 +1766,89 @@ public final class DataProcessorTest {
|
|||||||
assertAppUsagePeriod(appUsagePeriodList.get(6), 1000000, 1100000);
|
assertAppUsagePeriod(appUsagePeriodList.get(6), 1000000, 1100000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void excludePowerConnectedTime_startEndNotCharging_returnExpectedResult() {
|
||||||
|
final List<AppUsagePeriod> appUsagePeriodList = List.of(
|
||||||
|
AppUsagePeriod.newBuilder().setStartTime(100).setEndTime(200).build());
|
||||||
|
final List<BatteryEvent> batteryEventList = List.of(
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(50).setType(
|
||||||
|
BatteryEventType.POWER_DISCONNECTED).build(),
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(166).setType(
|
||||||
|
BatteryEventType.POWER_CONNECTED).build(),
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(188).setType(
|
||||||
|
BatteryEventType.POWER_DISCONNECTED).build(),
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(280).setType(
|
||||||
|
BatteryEventType.POWER_CONNECTED).build());
|
||||||
|
|
||||||
|
final List<AppUsagePeriod> resultList =
|
||||||
|
DataProcessor.excludePowerConnectedTimeFromAppUsagePeriodList(
|
||||||
|
appUsagePeriodList, batteryEventList);
|
||||||
|
|
||||||
|
assertThat(resultList).hasSize(2);
|
||||||
|
assertAppUsagePeriod(resultList.get(0), 100, 166);
|
||||||
|
assertAppUsagePeriod(resultList.get(1), 188, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void excludePowerConnectedTime_startEndInCharging_returnExpectedResult() {
|
||||||
|
final List<AppUsagePeriod> appUsagePeriodList = List.of(
|
||||||
|
AppUsagePeriod.newBuilder().setStartTime(100).setEndTime(200).build());
|
||||||
|
final List<BatteryEvent> batteryEventList = List.of(
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(50).setType(
|
||||||
|
BatteryEventType.POWER_DISCONNECTED).build(),
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(80).setType(
|
||||||
|
BatteryEventType.POWER_CONNECTED).build(),
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(120).setType(
|
||||||
|
BatteryEventType.POWER_DISCONNECTED).build(),
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(150).setType(
|
||||||
|
BatteryEventType.POWER_CONNECTED).build(),
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(160).setType(
|
||||||
|
BatteryEventType.POWER_DISCONNECTED).build(),
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(180).setType(
|
||||||
|
BatteryEventType.POWER_CONNECTED).build());
|
||||||
|
|
||||||
|
final List<AppUsagePeriod> resultList =
|
||||||
|
DataProcessor.excludePowerConnectedTimeFromAppUsagePeriodList(
|
||||||
|
appUsagePeriodList, batteryEventList);
|
||||||
|
|
||||||
|
assertThat(resultList).hasSize(2);
|
||||||
|
assertAppUsagePeriod(resultList.get(0), 120, 150);
|
||||||
|
assertAppUsagePeriod(resultList.get(1), 160, 180);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void excludePowerConnectedTime_wholePeriodNotCharging_returnExpectedResult() {
|
||||||
|
final List<AppUsagePeriod> appUsagePeriodList = List.of(
|
||||||
|
AppUsagePeriod.newBuilder().setStartTime(100).setEndTime(200).build());
|
||||||
|
final List<BatteryEvent> batteryEventList = List.of(
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(50).setType(
|
||||||
|
BatteryEventType.POWER_DISCONNECTED).build(),
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(80).setType(
|
||||||
|
BatteryEventType.POWER_CONNECTED).build());
|
||||||
|
|
||||||
|
final List<AppUsagePeriod> resultList =
|
||||||
|
DataProcessor.excludePowerConnectedTimeFromAppUsagePeriodList(
|
||||||
|
appUsagePeriodList, batteryEventList);
|
||||||
|
|
||||||
|
assertThat(resultList).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void excludePowerConnectedTime_wholePeriodInCharging_returnExpectedResult() {
|
||||||
|
final List<AppUsagePeriod> appUsagePeriodList = List.of(
|
||||||
|
AppUsagePeriod.newBuilder().setStartTime(100).setEndTime(200).build());
|
||||||
|
final List<BatteryEvent> batteryEventList = List.of(
|
||||||
|
BatteryEvent.newBuilder().setTimestamp(50).setType(
|
||||||
|
BatteryEventType.POWER_DISCONNECTED).build());
|
||||||
|
|
||||||
|
final List<AppUsagePeriod> resultList =
|
||||||
|
DataProcessor.excludePowerConnectedTimeFromAppUsagePeriodList(
|
||||||
|
appUsagePeriodList, batteryEventList);
|
||||||
|
|
||||||
|
assertThat(resultList).hasSize(1);
|
||||||
|
assertAppUsagePeriod(resultList.get(0), 100, 200);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getScreenOnTime_returnExpectedResult() {
|
public void getScreenOnTime_returnExpectedResult() {
|
||||||
final long userId = 1;
|
final long userId = 1;
|
||||||
|
@@ -36,6 +36,7 @@ import android.os.UserHandle;
|
|||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
|
|
||||||
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
|
||||||
|
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
|
||||||
import com.android.settings.testutils.BatteryTestUtils;
|
import com.android.settings.testutils.BatteryTestUtils;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -136,6 +137,28 @@ public final class DatabaseUtilsTest {
|
|||||||
verifyNoMoreInteractions(mMockContentResolver);
|
verifyNoMoreInteractions(mMockContentResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sendBatteryEventData_returnsExpectedList() {
|
||||||
|
final BatteryEvent batteryEvent =
|
||||||
|
BatteryEvent.newBuilder()
|
||||||
|
.setTimestamp(10001L)
|
||||||
|
.setType(BatteryEventType.POWER_CONNECTED)
|
||||||
|
.setBatteryLevel(66)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final ContentValues contentValues =
|
||||||
|
DatabaseUtils.sendBatteryEventData(mContext, batteryEvent);
|
||||||
|
|
||||||
|
assertThat(contentValues.getAsInteger(BatteryEventEntity.KEY_TIMESTAMP))
|
||||||
|
.isEqualTo(10001L);
|
||||||
|
assertThat(contentValues.getAsInteger(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE))
|
||||||
|
.isEqualTo(BatteryEventType.POWER_CONNECTED.getNumber());
|
||||||
|
assertThat(contentValues.getAsInteger(BatteryEventEntity.KEY_BATTERY_LEVEL))
|
||||||
|
.isEqualTo(66);
|
||||||
|
// Verifies the inserted ContentValues into content provider.
|
||||||
|
verify(mMockContentResolver).insert(DatabaseUtils.BATTERY_EVENT_URI, contentValues);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sendBatteryEntryData_nullBatteryIntent_returnsNullValue() {
|
public void sendBatteryEntryData_nullBatteryIntent_returnsNullValue() {
|
||||||
doReturn(null).when(mContext).registerReceiver(any(), any());
|
doReturn(null).when(mContext).registerReceiver(any(), any());
|
||||||
|
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.batteryusage.db;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.testutils.BatteryTestUtils;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
/** Tests for {@link BatteryEventDao}. */
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public final class BatteryEventDaoTest {
|
||||||
|
private Context mContext;
|
||||||
|
private BatteryStateDatabase mDatabase;
|
||||||
|
private BatteryEventDao mBatteryEventDao;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
|
||||||
|
mBatteryEventDao = mDatabase.batteryEventDao();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void closeDb() {
|
||||||
|
mDatabase.close();
|
||||||
|
BatteryStateDatabase.setBatteryStateDatabase(/*database=*/ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAllAfter_returnExpectedResult() {
|
||||||
|
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
|
||||||
|
.setTimestamp(100L)
|
||||||
|
.setBatteryEventType(1)
|
||||||
|
.setBatteryLevel(66)
|
||||||
|
.build());
|
||||||
|
mBatteryEventDao.insert(BatteryEventEntity.newBuilder()
|
||||||
|
.setTimestamp(200L)
|
||||||
|
.setBatteryEventType(2)
|
||||||
|
.setBatteryLevel(88)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
final Cursor cursor = mBatteryEventDao.getAllAfter(160L);
|
||||||
|
assertThat(cursor.getCount()).isEqualTo(1);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
assertThat(cursor.getLong(cursor.getColumnIndex(BatteryEventEntity.KEY_TIMESTAMP)))
|
||||||
|
.isEqualTo(200L);
|
||||||
|
assertThat(cursor.getInt(cursor.getColumnIndex(BatteryEventEntity.KEY_BATTERY_EVENT_TYPE)))
|
||||||
|
.isEqualTo(2);
|
||||||
|
assertThat(cursor.getInt(cursor.getColumnIndex(BatteryEventEntity.KEY_BATTERY_LEVEL)))
|
||||||
|
.isEqualTo(88);
|
||||||
|
|
||||||
|
mBatteryEventDao.clearAll();
|
||||||
|
assertThat(mBatteryEventDao.getAll()).isEmpty();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.batteryusage.db;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
/** Tests for {@link BatteryEventEntity}. */
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public final class BatteryEventEntityTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBuilder_returnsExpectedResult() {
|
||||||
|
final long timestamp = 10001L;
|
||||||
|
final int batteryEventType = 1;
|
||||||
|
final int batteryLevel = 66;
|
||||||
|
|
||||||
|
BatteryEventEntity entity = BatteryEventEntity
|
||||||
|
.newBuilder()
|
||||||
|
.setTimestamp(timestamp)
|
||||||
|
.setBatteryEventType(batteryEventType)
|
||||||
|
.setBatteryLevel(batteryLevel)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Verifies the app relative information.
|
||||||
|
assertThat(entity.timestamp).isEqualTo(timestamp);
|
||||||
|
assertThat(entity.batteryEventType).isEqualTo(batteryEventType);
|
||||||
|
assertThat(entity.batteryLevel).isEqualTo(batteryLevel);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user