Use the field isFullChargeCycleStart in database to get the last full

charge cycle start and remove the unused SharedPreference.

Test: make RunSettingsRoboTests + manual
Bug: 253553141
Change-Id: I74ef71775fa5d4871077f6d5573467aa5843bc6b
This commit is contained in:
Kuan Wang
2022-11-17 21:33:40 +08:00
parent bf26483af5
commit 2b93d40683
14 changed files with 159 additions and 255 deletions

View File

@@ -80,6 +80,6 @@ public final class BatteryUsageBroadcastReceiver extends BroadcastReceiver {
} }
mFetchBatteryUsageData = true; mFetchBatteryUsageData = true;
BatteryUsageDataLoader.enqueueWork(context); BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ true);
} }
} }

View File

@@ -40,9 +40,8 @@ import java.time.Duration;
public class BatteryUsageContentProvider extends ContentProvider { public class BatteryUsageContentProvider extends ContentProvider {
private static final String TAG = "BatteryUsageContentProvider"; private static final String TAG = "BatteryUsageContentProvider";
// TODO: Updates the duration to a more reasonable value for since-last-full-charge.
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public static final Duration QUERY_DURATION_HOURS = Duration.ofHours(28); public static final Duration QUERY_DURATION_HOURS = Duration.ofDays(6);
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public static final String QUERY_KEY_TIMESTAMP = "timestamp"; public static final String QUERY_KEY_TIMESTAMP = "timestamp";
@@ -141,7 +140,7 @@ public class BatteryUsageContentProvider extends ContentProvider {
final long timestamp = mClock.millis(); final long timestamp = mClock.millis();
Cursor cursor = null; Cursor cursor = null;
try { try {
cursor = mBatteryStateDao.getCursorAfter(firstTimestamp); cursor = mBatteryStateDao.getCursorSinceLastFullCharge(firstTimestamp);
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "query() from:" + uri + " error:" + e); Log.e(TAG, "query() from:" + uri + " error:" + e);
} }

View File

@@ -37,15 +37,15 @@ public final class BatteryUsageDataLoader {
private BatteryUsageDataLoader() { private BatteryUsageDataLoader() {
} }
static void enqueueWork(Context context) { static void enqueueWork(final Context context, final boolean isFullChargeStart) {
AsyncTask.execute(() -> { AsyncTask.execute(() -> {
Log.d(TAG, "loadUsageDataSafely() in the AsyncTask"); Log.d(TAG, "loadUsageDataSafely() in the AsyncTask");
loadUsageDataSafely(context.getApplicationContext()); loadUsageDataSafely(context.getApplicationContext(), isFullChargeStart);
}); });
} }
@VisibleForTesting @VisibleForTesting
static void loadUsageData(Context context) { static void loadUsageData(final Context context, final boolean isFullChargeStart) {
final long start = System.currentTimeMillis(); final long start = System.currentTimeMillis();
final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context); final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context);
final List<BatteryEntry> batteryEntryList = final List<BatteryEntry> batteryEntryList =
@@ -60,13 +60,14 @@ public final class BatteryUsageDataLoader {
// Uploads the BatteryEntry data into SettingsIntelligence. // Uploads the BatteryEntry data into SettingsIntelligence.
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
context, batteryEntryList, batteryUsageStats); context, batteryEntryList, batteryUsageStats, isFullChargeStart);
DataProcessor.closeBatteryUsageStats(batteryUsageStats); DataProcessor.closeBatteryUsageStats(batteryUsageStats);
} }
private static void loadUsageDataSafely(Context context) { private static void loadUsageDataSafely(
final Context context, final boolean isFullChargeStart) {
try { try {
loadUsageData(context); loadUsageData(context, isFullChargeStart);
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.e(TAG, "loadUsageData:" + e); Log.e(TAG, "loadUsageData:" + e);
} }

View File

@@ -92,7 +92,8 @@ public final class ConvertUtils {
final int batteryStatus, final int batteryStatus,
final int batteryHealth, final int batteryHealth,
final long bootTimestamp, final long bootTimestamp,
final long timestamp) { final long timestamp,
final boolean isFullChargeStart) {
final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();
if (entry != null && batteryUsageStats != null) { if (entry != null && batteryUsageStats != null) {
values.put(BatteryHistEntry.KEY_UID, Long.valueOf(entry.getUid())); values.put(BatteryHistEntry.KEY_UID, Long.valueOf(entry.getUid()));
@@ -106,6 +107,8 @@ public final class ConvertUtils {
values.put(BatteryHistEntry.KEY_PACKAGE_NAME, FAKE_PACKAGE_NAME); values.put(BatteryHistEntry.KEY_PACKAGE_NAME, FAKE_PACKAGE_NAME);
} }
values.put(BatteryHistEntry.KEY_TIMESTAMP, Long.valueOf(timestamp)); values.put(BatteryHistEntry.KEY_TIMESTAMP, Long.valueOf(timestamp));
values.put(BatteryHistEntry.KEY_IS_FULL_CHARGE_CYCLE_START,
Boolean.valueOf(isFullChargeStart));
final BatteryInformation batteryInformation = final BatteryInformation batteryInformation =
constructBatteryInformation( constructBatteryInformation(
entry, entry,
@@ -163,7 +166,8 @@ public final class ConvertUtils {
/*batteryStatus=*/ 0, /*batteryStatus=*/ 0,
/*batteryHealth=*/ 0, /*batteryHealth=*/ 0,
/*bootTimestamp=*/ 0, /*bootTimestamp=*/ 0,
/*timestamp=*/ 0)); /*timestamp=*/ 0,
/*isFullChargeStart=*/ false));
} }
/** Converts UTC timestamp to human readable local time string. */ /** Converts UTC timestamp to human readable local time string. */

View File

@@ -19,7 +19,6 @@ import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
@@ -37,7 +36,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase; import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
import com.android.settingslib.fuelgauge.BatteryStatus;
import java.time.Clock; import java.time.Clock;
import java.time.Duration; import java.time.Duration;
@@ -50,8 +48,6 @@ import java.util.Map;
/** A utility class to operate battery usage database. */ /** A utility class to operate battery usage database. */
public final class DatabaseUtils { public final class DatabaseUtils {
private static final String TAG = "DatabaseUtils"; private static final String TAG = "DatabaseUtils";
private static final String PREF_FILE_NAME = "battery_module_preference";
private static final String PREF_FULL_CHARGE_TIMESTAMP_KEY = "last_full_charge_timestamp_key";
/** Key for query parameter timestamp used in BATTERY_CONTENT_URI **/ /** Key for query parameter timestamp used in BATTERY_CONTENT_URI **/
private static final String QUERY_KEY_TIMESTAMP = "timestamp"; private static final String QUERY_KEY_TIMESTAMP = "timestamp";
/** Clear memory threshold for device booting phase. **/ /** Clear memory threshold for device booting phase. **/
@@ -89,8 +85,8 @@ public final class DatabaseUtils {
public static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceLastFullCharge( public static Map<Long, Map<String, BatteryHistEntry>> getHistoryMapSinceLastFullCharge(
Context context, Calendar calendar) { Context context, Calendar calendar) {
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
final long lastFullChargeTimestamp = final long sixDaysAgoTimestamp = getTimestampSixDaysAgo(calendar);
getStartTimestampForLastFullCharge(context, calendar); Log.d(TAG, "sixDayAgoTimestamp: " + sixDaysAgoTimestamp);
// Builds the content uri everytime to avoid cache. // Builds the content uri everytime to avoid cache.
final Uri batteryStateUri = final Uri batteryStateUri =
new Uri.Builder() new Uri.Builder()
@@ -98,7 +94,7 @@ public final class DatabaseUtils {
.authority(AUTHORITY) .authority(AUTHORITY)
.appendPath(BATTERY_STATE_TABLE) .appendPath(BATTERY_STATE_TABLE)
.appendQueryParameter( .appendQueryParameter(
QUERY_KEY_TIMESTAMP, Long.toString(lastFullChargeTimestamp)) QUERY_KEY_TIMESTAMP, Long.toString(sixDaysAgoTimestamp))
.build(); .build();
final Map<Long, Map<String, BatteryHistEntry>> resultMap = final Map<Long, Map<String, BatteryHistEntry>> resultMap =
@@ -142,9 +138,10 @@ public final class DatabaseUtils {
} }
static List<ContentValues> sendBatteryEntryData( static List<ContentValues> sendBatteryEntryData(
Context context, final Context context,
List<BatteryEntry> batteryEntryList, final List<BatteryEntry> batteryEntryList,
BatteryUsageStats batteryUsageStats) { final BatteryUsageStats batteryUsageStats,
final boolean isFullChargeStart) {
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
final Intent intent = BatteryUtils.getBatteryIntent(context); final Intent intent = BatteryUtils.getBatteryIntent(context);
if (intent == null) { if (intent == null) {
@@ -186,7 +183,8 @@ public final class DatabaseUtils {
batteryStatus, batteryStatus,
batteryHealth, batteryHealth,
snapshotBootTimestamp, snapshotBootTimestamp,
snapshotTimestamp))); snapshotTimestamp,
isFullChargeStart)));
} }
int size = 1; int size = 1;
@@ -197,6 +195,8 @@ public final class DatabaseUtils {
valuesList.toArray(valuesArray); valuesList.toArray(valuesArray);
try { try {
size = resolver.bulkInsert(BATTERY_CONTENT_URI, valuesArray); size = resolver.bulkInsert(BATTERY_CONTENT_URI, valuesArray);
Log.d(TAG, "insert() data into database with isFullChargeStart:"
+ isFullChargeStart);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "bulkInsert() data into database error:\n" + e); Log.e(TAG, "bulkInsert() data into database error:\n" + e);
} }
@@ -210,15 +210,18 @@ public final class DatabaseUtils {
batteryStatus, batteryStatus,
batteryHealth, batteryHealth,
snapshotBootTimestamp, snapshotBootTimestamp,
snapshotTimestamp); snapshotTimestamp,
isFullChargeStart);
try { try {
resolver.insert(BATTERY_CONTENT_URI, contentValues); resolver.insert(BATTERY_CONTENT_URI, contentValues);
Log.d(TAG, "insert() data into database with isFullChargeStart:"
+ isFullChargeStart);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "insert() data into database error:\n" + e); Log.e(TAG, "insert() data into database error:\n" + e);
} }
valuesList.add(contentValues); valuesList.add(contentValues);
} }
saveLastFullChargeTimestampPref(context, batteryStatus, batteryLevel, snapshotTimestamp);
resolver.notifyChange(BATTERY_CONTENT_URI, /*observer=*/ null); resolver.notifyChange(BATTERY_CONTENT_URI, /*observer=*/ null);
Log.d(TAG, String.format("sendBatteryEntryData() size=%d in %d/ms", Log.d(TAG, String.format("sendBatteryEntryData() size=%d in %d/ms",
size, (System.currentTimeMillis() - startTime))); size, (System.currentTimeMillis() - startTime)));
@@ -226,42 +229,6 @@ public final class DatabaseUtils {
return valuesList; return valuesList;
} }
@VisibleForTesting
static void saveLastFullChargeTimestampPref(
Context context, int batteryStatus, int batteryLevel, long timestamp) {
// Updates the SharedPreference only when timestamp is valid and phone is full charge.
if (!BatteryStatus.isCharged(batteryStatus, batteryLevel)) {
return;
}
final boolean success =
getSharedPreferences(context)
.edit()
.putLong(PREF_FULL_CHARGE_TIMESTAMP_KEY, timestamp)
.commit();
if (!success) {
Log.w(TAG, "saveLastFullChargeTimestampPref() fail: value=" + timestamp);
}
}
@VisibleForTesting
static long getLastFullChargeTimestampPref(Context context) {
return getSharedPreferences(context).getLong(PREF_FULL_CHARGE_TIMESTAMP_KEY, 0);
}
/**
* Returns the start timestamp for "since last full charge" battery usage chart.
* If the last full charge happens within the last 7 days, returns the timestamp of last full
* charge. Otherwise, returns the timestamp for 00:00 6 days before the calendar date.
*/
@VisibleForTesting
static long getStartTimestampForLastFullCharge(
Context context, Calendar calendar) {
final long lastFullChargeTimestamp = getLastFullChargeTimestampPref(context);
final long sixDayAgoTimestamp = getTimestampSixDaysAgo(calendar);
return Math.max(lastFullChargeTimestamp, sixDayAgoTimestamp);
}
private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider( private static Map<Long, Map<String, BatteryHistEntry>> loadHistoryMapFromContentProvider(
Context context, Uri batteryStateUri) { Context context, Uri batteryStateUri) {
final boolean isWorkProfileUser = isWorkProfile(context); final boolean isWorkProfileUser = isWorkProfile(context);
@@ -313,13 +280,6 @@ public final class DatabaseUtils {
}, CLEAR_MEMORY_DELAYED_MS); }, CLEAR_MEMORY_DELAYED_MS);
} }
private static SharedPreferences getSharedPreferences(Context context) {
return context
.getApplicationContext() // ensures we bind it with application
.createDeviceProtectedStorageContext()
.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE);
}
/** Returns the timestamp for 00:00 6 days before the calendar date. */ /** Returns the timestamp for 00:00 6 days before the calendar date. */
private static long getTimestampSixDaysAgo(Calendar calendar) { private static long getTimestampSixDaysAgo(Calendar calendar) {
Calendar startCalendar = Calendar startCalendar =

View File

@@ -39,7 +39,7 @@ public final class PeriodicJobReceiver extends BroadcastReceiver {
Log.w(TAG, "do not refresh job for work profile action=" + action); Log.w(TAG, "do not refresh job for work profile action=" + action);
return; return;
} }
BatteryUsageDataLoader.enqueueWork(context); BatteryUsageDataLoader.enqueueWork(context, /*isFullChargeStart=*/ false);
Log.d(TAG, "refresh periodic job from action=" + action); Log.d(TAG, "refresh periodic job from action=" + action);
PeriodicJobManager.getInstance(context).refreshJob(); PeriodicJobManager.getInstance(context).refreshJob();
DatabaseUtils.clearExpiredDataIfNeeded(context); DatabaseUtils.clearExpiredDataIfNeeded(context);

View File

@@ -41,9 +41,11 @@ public interface BatteryStateDao {
@Query("SELECT * FROM BatteryState WHERE timestamp > :timestamp ORDER BY timestamp DESC") @Query("SELECT * FROM BatteryState WHERE timestamp > :timestamp ORDER BY timestamp DESC")
List<BatteryState> getAllAfter(long timestamp); List<BatteryState> getAllAfter(long timestamp);
/** Gets the {@link Cursor} of all recorded data from a specific timestamp. */ /** Gets the {@link Cursor} of all recorded data since last full charge within 7 days. */
@Query("SELECT * FROM BatteryState WHERE timestamp >= :timestamp ORDER BY timestamp DESC") @Query("SELECT * FROM BatteryState WHERE timestamp >= :timestampSixDaysAgo AND timestamp >= "
Cursor getCursorAfter(long timestamp); + "(SELECT MAX(timestamp) FROM BatteryState WHERE isFullChargeCycleStart = 1)"
+ " ORDER BY timestamp ASC")
Cursor getCursorSinceLastFullCharge(long timestampSixDaysAgo);
/** Get the count of distinct timestamp after a specific timestamp. */ /** Get the count of distinct timestamp after a specific timestamp. */
@Query("SELECT COUNT(DISTINCT timestamp) FROM BatteryState WHERE timestamp > :timestamp") @Query("SELECT COUNT(DISTINCT timestamp) FROM BatteryState WHERE timestamp > :timestamp")

View File

@@ -71,7 +71,8 @@ public final class BatteryHistEntryTest {
/*batteryStatus=*/ BatteryManager.BATTERY_STATUS_FULL, /*batteryStatus=*/ BatteryManager.BATTERY_STATUS_FULL,
/*batteryHealth=*/ BatteryManager.BATTERY_HEALTH_COLD, /*batteryHealth=*/ BatteryManager.BATTERY_HEALTH_COLD,
/*bootTimestamp=*/ 101L, /*bootTimestamp=*/ 101L,
/*timestamp=*/ 10001L); /*timestamp=*/ 10001L,
/*isFullChargeStart=*/ false);
assertBatteryHistEntry( assertBatteryHistEntry(
new BatteryHistEntry(values), new BatteryHistEntry(values),

View File

@@ -49,6 +49,9 @@ import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public final class BatteryUsageContentProviderTest { public final class BatteryUsageContentProviderTest {
private static final Uri VALID_BATTERY_STATE_CONTENT_URI = DatabaseUtils.BATTERY_CONTENT_URI; private static final Uri VALID_BATTERY_STATE_CONTENT_URI = DatabaseUtils.BATTERY_CONTENT_URI;
private static final String PACKAGE_NAME1 = "com.android.settings1";
private static final String PACKAGE_NAME2 = "com.android.settings2";
private static final String PACKAGE_NAME3 = "com.android.settings3";
private Context mContext; private Context mContext;
private BatteryUsageContentProvider mProvider; private BatteryUsageContentProvider mProvider;
@@ -118,31 +121,60 @@ public final class BatteryUsageContentProviderTest {
public void query_batteryState_returnsExpectedResult() throws Exception { public void query_batteryState_returnsExpectedResult() throws Exception {
mProvider.onCreate(); mProvider.onCreate();
final Duration currentTime = Duration.ofHours(52); final Duration currentTime = Duration.ofHours(52);
final long expiredTimeCutoff = currentTime.toMillis() final long expiredTimeCutoff = currentTime.toMillis() - 3;
- BatteryUsageContentProvider.QUERY_DURATION_HOURS.toMillis();
testQueryBatteryState(currentTime, expiredTimeCutoff, /*hasQueryTimestamp=*/ false); final Cursor cursor = insertBatteryState(currentTime, Long.toString(expiredTimeCutoff));
// Verifies the result not include expired data.
assertThat(cursor.getCount()).isEqualTo(3);
final int packageNameIndex = cursor.getColumnIndex("packageName");
// Verifies the first data package name.
cursor.moveToFirst();
final String actualPackageName1 = cursor.getString(packageNameIndex);
assertThat(actualPackageName1).isEqualTo(PACKAGE_NAME1);
// Verifies the second data package name.
cursor.moveToNext();
final String actualPackageName2 = cursor.getString(packageNameIndex);
assertThat(actualPackageName2).isEqualTo(PACKAGE_NAME2);
// Verifies the third data package name.
cursor.moveToNext();
final String actualPackageName3 = cursor.getString(packageNameIndex);
assertThat(actualPackageName3).isEqualTo(PACKAGE_NAME3);
cursor.close();
// Verifies the broadcast intent.
TimeUnit.SECONDS.sleep(1);
final List<Intent> intents = Shadows.shadowOf((Application) mContext).getBroadcastIntents();
assertThat(intents).hasSize(1);
assertThat(intents.get(0).getAction()).isEqualTo(
BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
} }
@Test @Test
public void query_batteryStateTimestamp_returnsExpectedResult() throws Exception { public void query_batteryStateTimestamp_returnsExpectedResult() throws Exception {
mProvider.onCreate(); mProvider.onCreate();
final Duration currentTime = Duration.ofHours(52); final Duration currentTime = Duration.ofHours(52);
final long expiredTimeCutoff = currentTime.toMillis() - Duration.ofHours(10).toMillis(); final long expiredTimeCutoff = currentTime.toMillis() - 1;
testQueryBatteryState(currentTime, expiredTimeCutoff, /*hasQueryTimestamp=*/ true);
}
@Test final Cursor cursor = insertBatteryState(currentTime, Long.toString(expiredTimeCutoff));
public void query_incorrectParameterFormat_returnsExpectedResult() throws Exception {
mProvider.onCreate(); // Verifies the result not include expired data.
final Duration currentTime = Duration.ofHours(52); assertThat(cursor.getCount()).isEqualTo(2);
final long expiredTimeCutoff = final int packageNameIndex = cursor.getColumnIndex("packageName");
currentTime.toMillis() // Verifies the first data package name.
- BatteryUsageContentProvider.QUERY_DURATION_HOURS.toMillis(); cursor.moveToFirst();
testQueryBatteryState( final String actualPackageName1 = cursor.getString(packageNameIndex);
currentTime, assertThat(actualPackageName1).isEqualTo(PACKAGE_NAME2);
expiredTimeCutoff, // Verifies the third data package name.
/*hasQueryTimestamp=*/ false, cursor.moveToNext();
/*customParameter=*/ "invalid number format"); final String actualPackageName2 = cursor.getString(packageNameIndex);
assertThat(actualPackageName2).isEqualTo(PACKAGE_NAME3);
cursor.close();
// Verifies the broadcast intent.
TimeUnit.SECONDS.sleep(1);
final List<Intent> intents = Shadows.shadowOf((Application) mContext).getBroadcastIntents();
assertThat(intents).hasSize(1);
assertThat(intents.get(0).getAction()).isEqualTo(
BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
} }
@Test @Test
@@ -247,54 +279,32 @@ public final class BatteryUsageContentProviderTest {
/*strings=*/ null)); /*strings=*/ null));
} }
private void testQueryBatteryState( private Cursor insertBatteryState(
Duration currentTime, long expiredTimeCutoff, boolean hasQueryTimestamp)
throws Exception {
testQueryBatteryState(currentTime, expiredTimeCutoff, hasQueryTimestamp, null);
}
private void testQueryBatteryState(
Duration currentTime, Duration currentTime,
long expiredTimeCutoff, String queryTimestamp)
boolean hasQueryTimestamp,
String customParameter)
throws Exception { throws Exception {
mProvider.onCreate(); mProvider.onCreate();
final FakeClock fakeClock = new FakeClock(); final FakeClock fakeClock = new FakeClock();
fakeClock.setCurrentTime(currentTime); fakeClock.setCurrentTime(currentTime);
mProvider.setClock(fakeClock); mProvider.setClock(fakeClock);
// Inserts some expired testing data. final long currentTimestamp = currentTime.toMillis();
BatteryTestUtils.insertDataToBatteryStateDatabase(
mContext, expiredTimeCutoff - 1, "com.android.sysui1");
BatteryTestUtils.insertDataToBatteryStateDatabase(
mContext, expiredTimeCutoff - 2, "com.android.sysui2");
BatteryTestUtils.insertDataToBatteryStateDatabase(
mContext, expiredTimeCutoff - 3, "com.android.sysui3");
// Inserts some valid testing data. // Inserts some valid testing data.
final String packageName1 = "com.android.settings1";
final String packageName2 = "com.android.settings2";
final String packageName3 = "com.android.settings3";
BatteryTestUtils.insertDataToBatteryStateDatabase( BatteryTestUtils.insertDataToBatteryStateDatabase(
mContext, currentTime.toMillis(), packageName1); mContext, currentTimestamp - 2, PACKAGE_NAME1,
/*isFullChargeStart=*/ true);
BatteryTestUtils.insertDataToBatteryStateDatabase( BatteryTestUtils.insertDataToBatteryStateDatabase(
mContext, expiredTimeCutoff + 2, packageName2); mContext, currentTimestamp - 1, PACKAGE_NAME2);
BatteryTestUtils.insertDataToBatteryStateDatabase( BatteryTestUtils.insertDataToBatteryStateDatabase(
mContext, expiredTimeCutoff, packageName3); mContext, currentTimestamp, PACKAGE_NAME3);
final Uri.Builder builder = final Uri batteryStateQueryContentUri =
new Uri.Builder() new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT) .scheme(ContentResolver.SCHEME_CONTENT)
.authority(DatabaseUtils.AUTHORITY) .authority(DatabaseUtils.AUTHORITY)
.appendPath(DatabaseUtils.BATTERY_STATE_TABLE); .appendPath(DatabaseUtils.BATTERY_STATE_TABLE)
if (customParameter != null) { .appendQueryParameter(
builder.appendQueryParameter( BatteryUsageContentProvider.QUERY_KEY_TIMESTAMP, queryTimestamp)
BatteryUsageContentProvider.QUERY_KEY_TIMESTAMP, customParameter); .build();
} else if (hasQueryTimestamp) {
builder.appendQueryParameter(
BatteryUsageContentProvider.QUERY_KEY_TIMESTAMP,
Long.toString(expiredTimeCutoff));
}
final Uri batteryStateQueryContentUri = builder.build();
final Cursor cursor = final Cursor cursor =
mProvider.query( mProvider.query(
@@ -304,27 +314,6 @@ public final class BatteryUsageContentProviderTest {
/*strings1=*/ null, /*strings1=*/ null,
/*s1=*/ null); /*s1=*/ null);
// Verifies the result not include expired data. return cursor;
assertThat(cursor.getCount()).isEqualTo(3);
final int packageNameIndex = cursor.getColumnIndex("packageName");
// Verifies the first data package name.
cursor.moveToFirst();
final String actualPackageName1 = cursor.getString(packageNameIndex);
assertThat(actualPackageName1).isEqualTo(packageName1);
// Verifies the second data package name.
cursor.moveToNext();
final String actualPackageName2 = cursor.getString(packageNameIndex);
assertThat(actualPackageName2).isEqualTo(packageName2);
// Verifies the third data package name.
cursor.moveToNext();
final String actualPackageName3 = cursor.getString(packageNameIndex);
assertThat(actualPackageName3).isEqualTo(packageName3);
cursor.close();
// Verifies the broadcast intent.
TimeUnit.SECONDS.sleep(1);
final List<Intent> intents = Shadows.shadowOf((Application) mContext).getBroadcastIntents();
assertThat(intents).hasSize(1);
assertThat(intents.get(0).getAction()).isEqualTo(
BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
} }
} }

View File

@@ -82,7 +82,7 @@ public final class BatteryUsageDataLoaderTest {
.thenReturn(mBatteryUsageStats); .thenReturn(mBatteryUsageStats);
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> batteryEntryList; BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> batteryEntryList;
BatteryUsageDataLoader.loadUsageData(mContext); BatteryUsageDataLoader.loadUsageData(mContext, /*isFullChargeStart=*/ false);
final int queryFlags = mStatsQueryCaptor.getValue().getFlags(); final int queryFlags = mStatsQueryCaptor.getValue().getFlags();
assertThat(queryFlags assertThat(queryFlags
@@ -97,7 +97,7 @@ public final class BatteryUsageDataLoaderTest {
.thenReturn(mBatteryUsageStats); .thenReturn(mBatteryUsageStats);
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> null; BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> null;
BatteryUsageDataLoader.loadUsageData(mContext); BatteryUsageDataLoader.loadUsageData(mContext, /*isFullChargeStart=*/ false);
verify(mMockContentResolver).insert(any(), any()); verify(mMockContentResolver).insert(any(), any());
} }
@@ -106,9 +106,9 @@ public final class BatteryUsageDataLoaderTest {
public void loadUsageData_emptyBatteryEntryList_insertFakeDataIntoProvider() { public void loadUsageData_emptyBatteryEntryList_insertFakeDataIntoProvider() {
when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture())) when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
.thenReturn(mBatteryUsageStats); .thenReturn(mBatteryUsageStats);
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> new ArrayList<BatteryEntry>(); BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> new ArrayList<>();
BatteryUsageDataLoader.loadUsageData(mContext); BatteryUsageDataLoader.loadUsageData(mContext, /*isFullChargeStart=*/ false);
verify(mMockContentResolver).insert(any(), any()); verify(mMockContentResolver).insert(any(), any());
} }

View File

@@ -95,7 +95,8 @@ public final class ConvertUtilsTest {
/*batteryStatus=*/ BatteryManager.BATTERY_STATUS_FULL, /*batteryStatus=*/ BatteryManager.BATTERY_STATUS_FULL,
/*batteryHealth=*/ BatteryManager.BATTERY_HEALTH_COLD, /*batteryHealth=*/ BatteryManager.BATTERY_HEALTH_COLD,
/*bootTimestamp=*/ 101L, /*bootTimestamp=*/ 101L,
/*timestamp=*/ 10001L); /*timestamp=*/ 10001L,
/*isFullChargeStart=*/ true);
final BatteryInformation batteryInformation = final BatteryInformation batteryInformation =
ConvertUtils.getBatteryInformation( ConvertUtils.getBatteryInformation(
values, BatteryHistEntry.KEY_BATTERY_INFORMATION); values, BatteryHistEntry.KEY_BATTERY_INFORMATION);
@@ -109,6 +110,7 @@ public final class ConvertUtilsTest {
assertThat(values.getAsLong(BatteryHistEntry.KEY_TIMESTAMP)).isEqualTo(10001L); assertThat(values.getAsLong(BatteryHistEntry.KEY_TIMESTAMP)).isEqualTo(10001L);
assertThat(values.getAsInteger(BatteryHistEntry.KEY_CONSUMER_TYPE)) assertThat(values.getAsInteger(BatteryHistEntry.KEY_CONSUMER_TYPE))
.isEqualTo(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY); .isEqualTo(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
assertThat(values.getAsBoolean(BatteryHistEntry.KEY_IS_FULL_CHARGE_CYCLE_START)).isTrue();
assertThat(batteryInformation.getAppLabel()).isEqualTo("Settings"); assertThat(batteryInformation.getAppLabel()).isEqualTo("Settings");
assertThat(batteryInformation.getIsHidden()).isTrue(); assertThat(batteryInformation.getIsHidden()).isTrue();
assertThat(batteryInformation.getBootTimestamp()).isEqualTo(101L); assertThat(batteryInformation.getBootTimestamp()).isEqualTo(101L);
@@ -136,7 +138,8 @@ public final class ConvertUtilsTest {
/*batteryStatus=*/ BatteryManager.BATTERY_STATUS_FULL, /*batteryStatus=*/ BatteryManager.BATTERY_STATUS_FULL,
/*batteryHealth=*/ BatteryManager.BATTERY_HEALTH_COLD, /*batteryHealth=*/ BatteryManager.BATTERY_HEALTH_COLD,
/*bootTimestamp=*/ 101L, /*bootTimestamp=*/ 101L,
/*timestamp=*/ 10001L); /*timestamp=*/ 10001L,
/*isFullChargeStart=*/ false);
final BatteryInformation batteryInformation = final BatteryInformation batteryInformation =
ConvertUtils.getBatteryInformation( ConvertUtils.getBatteryInformation(
@@ -144,6 +147,7 @@ public final class ConvertUtilsTest {
final DeviceBatteryState deviceBatteryState = batteryInformation.getDeviceBatteryState(); final DeviceBatteryState deviceBatteryState = batteryInformation.getDeviceBatteryState();
assertThat(batteryInformation.getBootTimestamp()).isEqualTo(101L); assertThat(batteryInformation.getBootTimestamp()).isEqualTo(101L);
assertThat(batteryInformation.getZoneId()).isEqualTo(TimeZone.getDefault().getID()); assertThat(batteryInformation.getZoneId()).isEqualTo(TimeZone.getDefault().getID());
assertThat(values.getAsBoolean(BatteryHistEntry.KEY_IS_FULL_CHARGE_CYCLE_START)).isFalse();
assertThat(deviceBatteryState.getBatteryLevel()).isEqualTo(12); assertThat(deviceBatteryState.getBatteryLevel()).isEqualTo(12);
assertThat(deviceBatteryState.getBatteryStatus()) assertThat(deviceBatteryState.getBatteryStatus())
.isEqualTo(BatteryManager.BATTERY_STATUS_FULL); .isEqualTo(BatteryManager.BATTERY_STATUS_FULL);

View File

@@ -46,7 +46,6 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows; import org.robolectric.Shadows;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -99,7 +98,8 @@ public final class DatabaseUtilsTest {
doReturn(null).when(mContext).registerReceiver(any(), any()); doReturn(null).when(mContext).registerReceiver(any(), any());
assertThat( assertThat(
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, /*batteryEntryList=*/ null, mBatteryUsageStats)) mContext, /*batteryEntryList=*/ null, mBatteryUsageStats,
/*isFullChargeStart=*/ false))
.isNull(); .isNull();
} }
@@ -118,7 +118,8 @@ public final class DatabaseUtilsTest {
final List<ContentValues> valuesList = final List<ContentValues> valuesList =
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, batteryEntryList, mBatteryUsageStats); mContext, batteryEntryList, mBatteryUsageStats,
/*isFullChargeStart=*/ false);
assertThat(valuesList).hasSize(2); assertThat(valuesList).hasSize(2);
// Verifies the ContentValues content. // Verifies the ContentValues content.
@@ -141,7 +142,8 @@ public final class DatabaseUtilsTest {
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, mContext,
new ArrayList<>(), new ArrayList<>(),
mBatteryUsageStats); mBatteryUsageStats,
/*isFullChargeStart=*/ false);
assertThat(valuesList).hasSize(1); assertThat(valuesList).hasSize(1);
verifyFakeContentValues(valuesList.get(0)); verifyFakeContentValues(valuesList.get(0));
@@ -159,7 +161,8 @@ public final class DatabaseUtilsTest {
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, mContext,
/*batteryEntryList=*/ null, /*batteryEntryList=*/ null,
mBatteryUsageStats); mBatteryUsageStats,
/*isFullChargeStart=*/ false);
assertThat(valuesList).hasSize(1); assertThat(valuesList).hasSize(1);
verifyFakeContentValues(valuesList.get(0)); verifyFakeContentValues(valuesList.get(0));
@@ -177,7 +180,8 @@ public final class DatabaseUtilsTest {
DatabaseUtils.sendBatteryEntryData( DatabaseUtils.sendBatteryEntryData(
mContext, mContext,
/*batteryEntryList=*/ null, /*batteryEntryList=*/ null,
/*batteryUsageStats=*/ null); /*batteryUsageStats=*/ null,
/*isFullChargeStart=*/ false);
assertThat(valuesList).hasSize(1); assertThat(valuesList).hasSize(1);
verifyFakeContentValues(valuesList.get(0)); verifyFakeContentValues(valuesList.get(0));
@@ -258,88 +262,6 @@ public final class DatabaseUtilsTest {
assertThat(batteryHistMap).isEmpty(); assertThat(batteryHistMap).isEmpty();
} }
@Test
public void saveLastFullChargeTimestampPref_notFullCharge_returnsFalse() {
DatabaseUtils.saveLastFullChargeTimestampPref(
mContext,
BatteryManager.BATTERY_STATUS_UNKNOWN,
/* level */ 10,
/* timestamp */ 1);
assertThat(DatabaseUtils.getLastFullChargeTimestampPref(mContext)).isEqualTo(0);
}
@Test
public void saveLastFullChargeTimestampPref_fullStatus_returnsTrue() {
long expectedTimestamp = 1;
DatabaseUtils.saveLastFullChargeTimestampPref(
mContext,
BatteryManager.BATTERY_STATUS_FULL,
/* level */ 10,
/* timestamp */ expectedTimestamp);
assertThat(DatabaseUtils.getLastFullChargeTimestampPref(mContext))
.isEqualTo(expectedTimestamp);
}
@Test
public void saveLastFullChargeTimestampPref_level100_returnsTrue() {
long expectedTimestamp = 1;
DatabaseUtils.saveLastFullChargeTimestampPref(
mContext,
BatteryManager.BATTERY_STATUS_UNKNOWN,
/* level */ 100,
/* timestamp */ expectedTimestamp);
assertThat(DatabaseUtils.getLastFullChargeTimestampPref(mContext))
.isEqualTo(expectedTimestamp);
}
@Test
public void getStartTimestampForLastFullCharge_noTimestampPreference_returnsSixDaysAgo() {
Calendar currentCalendar = Calendar.getInstance();
currentCalendar.set(2022, 6, 5, 6, 30, 50); // 2022-07-05 06:30:50
Calendar expectedCalendar = Calendar.getInstance();
expectedCalendar.set(2022, 5, 29, 0, 0, 0); // 2022-06-29 00:00:00
expectedCalendar.set(Calendar.MILLISECOND, 0);
assertThat(DatabaseUtils.getStartTimestampForLastFullCharge(mContext, currentCalendar))
.isEqualTo(expectedCalendar.getTimeInMillis());
}
@Test
public void getStartTimestampForLastFullCharge_lastFullChargeEarlier_returnsSixDaysAgo() {
Calendar lastFullCalendar = Calendar.getInstance();
lastFullCalendar.set(2021, 11, 25, 6, 30, 50); // 2021-12-25 06:30:50
DatabaseUtils.saveLastFullChargeTimestampPref(
mContext,
BatteryManager.BATTERY_STATUS_UNKNOWN,
/* level */ 100,
/* timestamp */ lastFullCalendar.getTimeInMillis());
Calendar currentCalendar = Calendar.getInstance();
currentCalendar.set(2022, 0, 2, 6, 30, 50); // 2022-01-02 06:30:50
Calendar expectedCalendar = Calendar.getInstance();
expectedCalendar.set(2021, 11, 27, 0, 0, 0); // 2021-12-27 00:00:00
expectedCalendar.set(Calendar.MILLISECOND, 0);
assertThat(DatabaseUtils.getStartTimestampForLastFullCharge(mContext, currentCalendar))
.isEqualTo(expectedCalendar.getTimeInMillis());
}
@Test
public void getStartTimestampForLastFullCharge_lastFullChargeLater_returnsLastFullCharge() {
Calendar lastFullCalendar = Calendar.getInstance();
lastFullCalendar.set(2022, 6, 1, 6, 30, 50); // 2022-07-01 06:30:50
long expectedTimestamp = lastFullCalendar.getTimeInMillis();
DatabaseUtils.saveLastFullChargeTimestampPref(
mContext,
BatteryManager.BATTERY_STATUS_UNKNOWN,
/* level */ 100,
/* timestamp */ expectedTimestamp);
Calendar currentCalendar = Calendar.getInstance();
currentCalendar.set(2022, 6, 5, 6, 30, 50); // 2022-07-05 06:30:50
assertThat(DatabaseUtils.getStartTimestampForLastFullCharge(mContext, currentCalendar))
.isEqualTo(expectedTimestamp);
}
private static void verifyContentValues(double consumedPower, ContentValues values) { private static void verifyContentValues(double consumedPower, ContentValues values) {
final BatteryInformation batteryInformation = final BatteryInformation batteryInformation =
ConvertUtils.getBatteryInformation( ConvertUtils.getBatteryInformation(

View File

@@ -53,10 +53,13 @@ public final class BatteryStateDaoTest {
mContext = ApplicationProvider.getApplicationContext(); mContext = ApplicationProvider.getApplicationContext();
mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext); mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
mBatteryStateDao = mDatabase.batteryStateDao(); mBatteryStateDao = mDatabase.batteryStateDao();
BatteryTestUtils.insertDataToBatteryStateDatabase(mContext, TIMESTAMP3, PACKAGE_NAME3);
BatteryTestUtils.insertDataToBatteryStateDatabase(mContext, TIMESTAMP2, PACKAGE_NAME2);
BatteryTestUtils.insertDataToBatteryStateDatabase( BatteryTestUtils.insertDataToBatteryStateDatabase(
mContext, TIMESTAMP1, PACKAGE_NAME1, /*multiple=*/ true); mContext, TIMESTAMP3, PACKAGE_NAME3);
BatteryTestUtils.insertDataToBatteryStateDatabase(
mContext, TIMESTAMP2, PACKAGE_NAME2);
BatteryTestUtils.insertDataToBatteryStateDatabase(
mContext, TIMESTAMP1, PACKAGE_NAME1, /*multiple=*/ true,
/*isFullChargeStart=*/ true);
} }
@After @After
@@ -75,16 +78,26 @@ public final class BatteryStateDaoTest {
} }
@Test @Test
public void batteryStateDao_getCursorAfter() throws Exception { public void batteryStateDao_getCursorSinceLastFullCharge() throws Exception {
final Cursor cursor = mBatteryStateDao.getCursorAfter(TIMESTAMP2); final Cursor cursor1 = mBatteryStateDao.getCursorSinceLastFullCharge(TIMESTAMP1);
assertThat(cursor.getCount()).isEqualTo(2); assertThat(cursor1.getCount()).isEqualTo(3);
assertThat(cursor.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE); assertThat(cursor1.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
// Verifies the queried first battery state. // Verifies the queried first battery state.
cursor.moveToFirst(); cursor1.moveToFirst();
assertThat(cursor.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME3); assertThat(cursor1.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME1);
// Verifies the queried second battery state. // Verifies the queried second battery state.
cursor.moveToNext(); cursor1.moveToNext();
assertThat(cursor.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME2); assertThat(cursor1.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME2);
// Verifies the queried third battery state.
cursor1.moveToNext();
assertThat(cursor1.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME3);
final Cursor cursor2 = mBatteryStateDao.getCursorSinceLastFullCharge(TIMESTAMP3);
assertThat(cursor2.getCount()).isEqualTo(1);
assertThat(cursor2.getColumnCount()).isEqualTo(CURSOR_COLUMN_SIZE);
// Verifies the queried first battery state.
cursor2.moveToFirst();
assertThat(cursor2.getString(3 /*packageName*/)).isEqualTo(PACKAGE_NAME3);
} }
@Test @Test

View File

@@ -72,12 +72,21 @@ public class BatteryTestUtils {
/** Inserts a fake data into the database for testing. */ /** Inserts a fake data into the database for testing. */
public static void insertDataToBatteryStateDatabase( public static void insertDataToBatteryStateDatabase(
Context context, long timestamp, String packageName) { Context context, long timestamp, String packageName) {
insertDataToBatteryStateDatabase(context, timestamp, packageName, /*multiple=*/ false); insertDataToBatteryStateDatabase(
context, timestamp, packageName, /*multiple=*/ false, /*isFullChargeStart=*/ false);
} }
/** Inserts a fake data into the database for testing. */ /** Inserts a fake data into the database for testing. */
public static void insertDataToBatteryStateDatabase( public static void insertDataToBatteryStateDatabase(
Context context, long timestamp, String packageName, boolean multiple) { Context context, long timestamp, String packageName, boolean isFullChargeStart) {
insertDataToBatteryStateDatabase(
context, timestamp, packageName, /*multiple=*/ false, isFullChargeStart);
}
/** Inserts a fake data into the database for testing. */
public static void insertDataToBatteryStateDatabase(
Context context, long timestamp, String packageName, boolean multiple,
boolean isFullChargeStart) {
DeviceBatteryState deviceBatteryState = DeviceBatteryState deviceBatteryState =
DeviceBatteryState DeviceBatteryState
.newBuilder() .newBuilder()
@@ -113,7 +122,7 @@ public class BatteryTestUtils {
packageName, packageName,
timestamp, timestamp,
/*consumerType=*/ 2, /*consumerType=*/ 2,
/*isFullChargeCycleStart=*/ false, isFullChargeStart,
ConvertUtils.convertBatteryInformationToString(batteryInformation), ConvertUtils.convertBatteryInformationToString(batteryInformation),
""); "");
BatteryStateDao dao = BatteryStateDao dao =