Merge changes from topic "opt_mode_ds" into main

* changes:
  Add AppOptimizationModeEventsUtils to save & update app optimization mode expiration events.
  Dump app optimization mode expiration event data in bug report.
This commit is contained in:
Treehugger Robot
2024-06-03 13:50:05 +00:00
committed by Android (Google) Code Review
12 changed files with 487 additions and 44 deletions

View File

@@ -95,15 +95,11 @@ android_library {
"SettingsLibActivityEmbedding", "SettingsLibActivityEmbedding",
"aconfig_settings_flags_lib", "aconfig_settings_flags_lib",
"accessibility_settings_flags_lib", "accessibility_settings_flags_lib",
"app-usage-event-protos-lite",
"battery-event-protos-lite",
"battery-usage-slot-protos-lite",
"contextualcards", "contextualcards",
"development_settings_flag_lib", "development_settings_flag_lib",
"factory_reset_flags_lib", "factory_reset_flags_lib",
"fuelgauge-log-protos-lite", "fuelgauge-log-protos-lite",
"fuelgauge-usage-state-protos-lite", "fuelgauge-protos-lite",
"power-anomaly-event-protos-lite",
"settings-contextual-card-protos-lite", "settings-contextual-card-protos-lite",
"settings-log-bridge-protos-lite", "settings-log-bridge-protos-lite",
"settings-logtags", "settings-logtags",

View File

@@ -21,6 +21,7 @@ message BatteryOptimizeHistoricalLogEntry {
BACKUP = 5; BACKUP = 5;
FORCE_RESET = 6; FORCE_RESET = 6;
EXTERNAL_UPDATE = 7; EXTERNAL_UPDATE = 7;
EXPIRATION_RESET = 8;
} }
optional string package_name = 1; optional string package_name = 1;

View File

@@ -43,6 +43,7 @@ import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action;
import com.android.settings.fuelgauge.batteryusage.AppOptModeSharedPreferencesUtils;
import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry; import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry;
import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.fuelgauge.batteryusage.BatteryEntry;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
@@ -274,9 +275,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment
final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode();
mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode);
logMetricCategory(currentOptimizeMode); logMetricCategory(currentOptimizeMode);
mExecutor.execute( mExecutor.execute(
() -> { () -> {
if (currentOptimizeMode != mOptimizationMode) {
AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(
getContext(), mBatteryOptimizeUtils.getUid());
}
BatteryOptimizeLogUtils.writeLog( BatteryOptimizeLogUtils.writeLog(
getContext().getApplicationContext(), getContext().getApplicationContext(),
Action.LEAVE, Action.LEAVE,

View File

@@ -182,6 +182,14 @@ public class BatteryOptimizeUtils {
&& getAppOptimizationMode() != BatteryOptimizeUtils.MODE_RESTRICTED; && getAppOptimizationMode() != BatteryOptimizeUtils.MODE_RESTRICTED;
} }
String getPackageName() {
return mPackageName == null ? UNKNOWN_PACKAGE : mPackageName;
}
int getUid() {
return mUid;
}
/** Gets the list of installed applications. */ /** Gets the list of installed applications. */
public static ArraySet<ApplicationInfo> getInstalledApplications( public static ArraySet<ApplicationInfo> getInstalledApplications(
Context context, IPackageManager ipm) { Context context, IPackageManager ipm) {
@@ -257,10 +265,6 @@ public class BatteryOptimizeUtils {
} }
} }
String getPackageName() {
return mPackageName == null ? UNKNOWN_PACKAGE : mPackageName;
}
static int getMode(AppOpsManager appOpsManager, int uid, String packageName) { static int getMode(AppOpsManager appOpsManager, int uid, String packageName) {
return appOpsManager.checkOpNoThrow( return appOpsManager.checkOpNoThrow(
AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName); AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName);

View File

@@ -35,6 +35,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.fuelgauge.batteryusage.AppOptModeSharedPreferencesUtils;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.EntityHeaderController; import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.HelpUtils; import com.android.settingslib.HelpUtils;
@@ -121,6 +122,10 @@ public class PowerBackgroundUsageDetail extends DashboardFragment
mExecutor.execute( mExecutor.execute(
() -> { () -> {
if (currentOptimizeMode != mOptimizationMode) {
AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(
getContext(), mBatteryOptimizeUtils.getUid());
}
BatteryOptimizeLogUtils.writeLog( BatteryOptimizeLogUtils.writeLog(
getContext().getApplicationContext(), getContext().getApplicationContext(),
Action.LEAVE, Action.LEAVE,

View File

@@ -0,0 +1,223 @@
/*
* Copyright (C) 2024 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
import android.content.Context
import android.content.SharedPreferences
import android.util.ArrayMap
import android.util.Base64
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action
import com.android.settings.fuelgauge.BatteryOptimizeUtils
import com.android.settings.fuelgauge.BatteryUtils
/** A util to store and update app optimization mode expiration event data. */
object AppOptModeSharedPreferencesUtils {
private const val TAG: String = "AppOptModeSharedPreferencesUtils"
private const val SHARED_PREFS_FILE: String = "app_optimization_mode_shared_prefs"
@VisibleForTesting const val UNLIMITED_EXPIRE_TIME: Long = -1L
private val appOptimizationModeLock = Any()
private val defaultInstance = AppOptimizationModeEvent.getDefaultInstance()
/** Returns all app optimization mode events for log. */
@JvmStatic
fun getAllEvents(context: Context): List<AppOptimizationModeEvent> =
synchronized(appOptimizationModeLock) { getAppOptModeEventsMap(context).values.toList() }
/** Updates the app optimization mode event data. */
@JvmStatic
fun updateAppOptModeExpiration(
context: Context,
uids: List<Int>,
packageNames: List<String>,
optimizationModes: List<Int>,
expirationTimes: LongArray,
) =
// The internal fun with an additional lambda parameter is used to
// 1) get true BatteryOptimizeUtils in production environment
// 2) get fake BatteryOptimizeUtils for testing environment
updateAppOptModeExpirationInternal(
context,
uids,
packageNames,
optimizationModes,
expirationTimes
) { uid: Int, packageName: String ->
BatteryOptimizeUtils(context, uid, packageName)
}
/** Resets the app optimization mode event data since the query timestamp. */
@JvmStatic
fun resetExpiredAppOptModeBeforeTimestamp(context: Context, queryTimestamp: Long) =
synchronized(appOptimizationModeLock) {
val eventsMap = getAppOptModeEventsMap(context)
val expirationUids = ArrayList<Int>(eventsMap.size)
for ((uid, event) in eventsMap) {
if (event.expirationTime > queryTimestamp) {
continue
}
updateBatteryOptimizationMode(
context,
event.uid,
event.packageName,
event.resetOptimizationMode,
Action.EXPIRATION_RESET,
)
expirationUids.add(uid)
}
// Remove the expired AppOptimizationModeEvent data from storage
clearSharedPreferences(context, expirationUids)
}
/** Deletes all app optimization mode event data with a specific uid. */
@JvmStatic
fun deleteAppOptimizationModeEventByUid(context: Context, uid: Int) =
synchronized(appOptimizationModeLock) { clearSharedPreferences(context, listOf(uid)) }
@VisibleForTesting
fun updateAppOptModeExpirationInternal(
context: Context,
uids: List<Int>,
packageNames: List<String>,
optimizationModes: List<Int>,
expirationTimes: LongArray,
getBatteryOptimizeUtils: (Int, String) -> BatteryOptimizeUtils
) =
synchronized(appOptimizationModeLock) {
val eventsMap = getAppOptModeEventsMap(context)
val expirationEvents: MutableMap<Int, AppOptimizationModeEvent> = ArrayMap()
for (i in uids.indices) {
val uid = uids[i]
val packageName = packageNames[i]
val optimizationMode = optimizationModes[i]
val originalOptMode: Int =
updateBatteryOptimizationMode(
context,
uid,
packageName,
optimizationMode,
Action.EXTERNAL_UPDATE,
getBatteryOptimizeUtils(uid, packageName)
)
if (originalOptMode == BatteryOptimizeUtils.MODE_UNKNOWN) {
continue
}
// Make sure the reset mode is consistent with the expiration event in storage.
val resetOptMode = eventsMap[uid]?.resetOptimizationMode ?: originalOptMode
val expireTimeMs: Long = expirationTimes[i]
if (expireTimeMs != UNLIMITED_EXPIRE_TIME) {
Log.d(
TAG,
"setOptimizationMode($packageName) from $originalOptMode " +
"to $optimizationMode with expiration time $expireTimeMs",
)
expirationEvents[uid] =
AppOptimizationModeEvent.newBuilder()
.setUid(uid)
.setPackageName(packageName)
.setResetOptimizationMode(resetOptMode)
.setExpirationTime(expireTimeMs)
.build()
}
}
// Append and update the AppOptimizationModeEvent.
if (expirationEvents.isNotEmpty()) {
updateSharedPreferences(context, expirationEvents)
}
}
@VisibleForTesting
fun updateBatteryOptimizationMode(
context: Context,
uid: Int,
packageName: String,
optimizationMode: Int,
action: Action,
batteryOptimizeUtils: BatteryOptimizeUtils = BatteryOptimizeUtils(context, uid, packageName)
): Int {
if (!batteryOptimizeUtils.isOptimizeModeMutable) {
Log.w(TAG, "Fail to update immutable optimization mode for: $packageName")
return BatteryOptimizeUtils.MODE_UNKNOWN
}
val currentOptMode = batteryOptimizeUtils.appOptimizationMode
batteryOptimizeUtils.setAppUsageState(optimizationMode, action)
Log.d(
TAG,
"setAppUsageState($packageName) to $optimizationMode with action = ${action.name}",
)
return currentOptMode
}
private fun getSharedPreferences(context: Context): SharedPreferences {
return context.applicationContext.getSharedPreferences(
SHARED_PREFS_FILE,
Context.MODE_PRIVATE,
)
}
private fun getAppOptModeEventsMap(context: Context): ArrayMap<Int, AppOptimizationModeEvent> {
val sharedPreferences = getSharedPreferences(context)
val allKeys = sharedPreferences.all?.keys ?: emptySet()
if (allKeys.isEmpty()) {
return ArrayMap()
}
val eventsMap = ArrayMap<Int, AppOptimizationModeEvent>(allKeys.size)
for (key in allKeys) {
sharedPreferences.getString(key, null)?.let {
eventsMap[key.toInt()] = deserializeAppOptimizationModeEvent(it)
}
}
return eventsMap
}
private fun updateSharedPreferences(
context: Context,
eventsMap: Map<Int, AppOptimizationModeEvent>
) {
val sharedPreferences = getSharedPreferences(context)
sharedPreferences.edit().run {
for ((uid, event) in eventsMap) {
putString(uid.toString(), serializeAppOptimizationModeEvent(event))
}
apply()
}
}
private fun clearSharedPreferences(context: Context, uids: List<Int>) {
val sharedPreferences = getSharedPreferences(context)
sharedPreferences.edit().run {
for (uid in uids) {
remove(uid.toString())
}
apply()
}
}
private fun serializeAppOptimizationModeEvent(event: AppOptimizationModeEvent): String {
return Base64.encodeToString(event.toByteArray(), Base64.DEFAULT)
}
private fun deserializeAppOptimizationModeEvent(
encodedProtoString: String
): AppOptimizationModeEvent {
return BatteryUtils.parseProtoFromString(encodedProtoString, defaultInstance)
}
}

View File

@@ -167,6 +167,8 @@ public final class BatteryUsageDataLoader {
try { try {
final long start = System.currentTimeMillis(); final long start = System.currentTimeMillis();
loadBatteryStatsData(context, isFullChargeStart); loadBatteryStatsData(context, isFullChargeStart);
AppOptModeSharedPreferencesUtils.resetExpiredAppOptModeBeforeTimestamp(
context, System.currentTimeMillis());
if (!isFullChargeStart) { if (!isFullChargeStart) {
// No app usage data or battery diff data at this time. // No app usage data or battery diff data at this time.
final UserIdsSeries userIdsSeries = final UserIdsSeries userIdsSeries =

View File

@@ -54,6 +54,7 @@ public final class BugReportContentProvider extends ContentProvider {
return; return;
} }
writer.println("dump BatteryUsage and AppUsage states:"); writer.println("dump BatteryUsage and AppUsage states:");
LogUtils.dumpAppOptimizationModeEventHist(context, writer);
LogUtils.dumpBatteryUsageDatabaseHist(context, writer); LogUtils.dumpBatteryUsageDatabaseHist(context, writer);
LogUtils.dumpAppUsageDatabaseHist(context, writer); LogUtils.dumpAppUsageDatabaseHist(context, writer);
LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer); LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer);

View File

@@ -20,6 +20,8 @@ import android.content.Context;
import android.util.Log; import android.util.Log;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batteryusage.AppOptModeSharedPreferencesUtils;
import com.android.settings.fuelgauge.batteryusage.AppOptimizationModeEvent;
import com.android.settings.fuelgauge.batteryusage.BatteryUsageSlot; import com.android.settings.fuelgauge.batteryusage.BatteryUsageSlot;
import com.android.settings.fuelgauge.batteryusage.ConvertUtils; import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
import com.android.settings.fuelgauge.batteryusage.DatabaseUtils; import com.android.settings.fuelgauge.batteryusage.DatabaseUtils;
@@ -47,6 +49,13 @@ public final class LogUtils {
private static final Duration DUMP_TIME_OFFSET = Duration.ofHours(24); private static final Duration DUMP_TIME_OFFSET = Duration.ofHours(24);
private static final Duration DUMP_TIME_OFFSET_FOR_ENTRY = Duration.ofHours(4); private static final Duration DUMP_TIME_OFFSET_FOR_ENTRY = Duration.ofHours(4);
static void dumpAppOptimizationModeEventHist(Context context, PrintWriter writer) {
writer.println("\n\tApp Optimization Mode Event History:");
final List<AppOptimizationModeEvent> events =
AppOptModeSharedPreferencesUtils.getAllEvents(context);
dumpListItems(writer, events, event -> event);
}
static void dumpBatteryUsageDatabaseHist(Context context, PrintWriter writer) { static void dumpBatteryUsageDatabaseHist(Context context, PrintWriter writer) {
// Dumps periodic job events. // Dumps periodic job events.
writer.println("\nBattery PeriodicJob History:"); writer.println("\nBattery PeriodicJob History:");

View File

@@ -9,41 +9,9 @@ package {
} }
java_library { java_library {
name: "app-usage-event-protos-lite", name: "fuelgauge-protos-lite",
proto: { proto: {
type: "lite", type: "lite",
}, },
srcs: ["app_usage_event.proto"], srcs: ["*.proto"],
}
java_library {
name: "battery-event-protos-lite",
proto: {
type: "lite",
},
srcs: ["battery_event.proto"],
}
java_library {
name: "battery-usage-slot-protos-lite",
proto: {
type: "lite",
},
srcs: ["battery_usage_slot.proto"],
}
java_library {
name: "fuelgauge-usage-state-protos-lite",
proto: {
type: "lite",
},
srcs: ["fuelgauge_usage_state.proto"],
}
java_library {
name: "power-anomaly-event-protos-lite",
proto: {
type: "lite",
},
srcs: ["power_anomaly_event.proto"],
} }

View File

@@ -0,0 +1,18 @@
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.android.settings.fuelgauge.batteryusage";
option java_outer_classname = "AppOptimizationModeEventProto";
message AppOptimizationModeEvents {
// Map of uid to AppOptimizationModeEvent
map<int32, AppOptimizationModeEvent> events = 1;
}
message AppOptimizationModeEvent {
optional int32 uid = 1;
optional string package_name = 2;
// Value of BatteryUsageSlot.BatteryOptimizationMode, range = [0,3]
optional int32 reset_optimization_mode = 3;
optional int64 expiration_time = 4;
}

View File

@@ -0,0 +1,212 @@
/*
* Copyright (C) 2024 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
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action
import com.android.settings.fuelgauge.BatteryOptimizeUtils
import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_OPTIMIZED
import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_RESTRICTED
import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_UNKNOWN
import com.android.settings.fuelgauge.BatteryOptimizeUtils.MODE_UNRESTRICTED
import com.android.settings.fuelgauge.batteryusage.AppOptModeSharedPreferencesUtils.UNLIMITED_EXPIRE_TIME
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class AppOptModeSharedPreferencesUtilsTest {
@JvmField @Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
@Spy private var context: Context = ApplicationProvider.getApplicationContext()
@Spy
private var testBatteryOptimizeUtils = spy(BatteryOptimizeUtils(context, UID, PACKAGE_NAME))
@Before
fun setup() {
AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(context, UID)
}
@Test
fun getAllEvents_emptyData_verifyEmptyList() {
assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
}
@Test
fun updateAppOptModeExpirationInternal_withExpirationTime_verifyData() {
insertAppOptModeEventForTest(/* expirationTime= */ 1000L)
val events = AppOptModeSharedPreferencesUtils.getAllEvents(context)
assertThat(events.size).isEqualTo(1)
assertAppOptimizationModeEventInfo(events.get(0), UID, PACKAGE_NAME, MODE_OPTIMIZED, 1000L)
}
@Test
fun updateAppOptModeExpirationInternal_withoutExpirationTime_verifyEmptyList() {
insertAppOptModeEventForTest(/* expirationTime= */ UNLIMITED_EXPIRE_TIME)
assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
}
@Test
fun deleteAppOptimizationModeEventByUid_uidNotContained_verifyData() {
insertAppOptModeEventForTest(/* expirationTime= */ 1000L)
assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context).size).isEqualTo(1)
AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(context, UNSET_UID)
val events = AppOptModeSharedPreferencesUtils.getAllEvents(context)
assertThat(events.size).isEqualTo(1)
assertAppOptimizationModeEventInfo(events.get(0), UID, PACKAGE_NAME, MODE_OPTIMIZED, 1000L)
}
@Test
fun deleteAppOptimizationModeEventByUid_uidExisting_verifyData() {
insertAppOptModeEventForTest(/* expirationTime= */ 1000L)
AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid(context, UID)
assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
}
@Test
fun resetExpiredAppOptModeBeforeTimestamp_noExpiredData_verifyData() {
insertAppOptModeEventForTest(/* expirationTime= */ 1000L)
AppOptModeSharedPreferencesUtils.resetExpiredAppOptModeBeforeTimestamp(context, 999L)
val events = AppOptModeSharedPreferencesUtils.getAllEvents(context)
assertThat(events.size).isEqualTo(1)
assertAppOptimizationModeEventInfo(events.get(0), UID, PACKAGE_NAME, MODE_OPTIMIZED, 1000L)
}
@Test
fun resetExpiredAppOptModeBeforeTimestamp_hasExpiredData_verifyEmptyList() {
insertAppOptModeEventForTest(/* expirationTime= */ 1000L)
AppOptModeSharedPreferencesUtils.resetExpiredAppOptModeBeforeTimestamp(context, 1001L)
assertThat(AppOptModeSharedPreferencesUtils.getAllEvents(context)).isEmpty()
}
@Test
fun updateBatteryOptimizationMode_updateToOptimizedMode_verifyAction() {
whenever(testBatteryOptimizeUtils?.isOptimizeModeMutable).thenReturn(true)
whenever(testBatteryOptimizeUtils?.getAppOptimizationMode(true))
.thenReturn(MODE_UNRESTRICTED)
val currentOptMode =
AppOptModeSharedPreferencesUtils.updateBatteryOptimizationMode(
context,
UID,
PACKAGE_NAME,
MODE_OPTIMIZED,
Action.EXTERNAL_UPDATE,
testBatteryOptimizeUtils
)
verify(testBatteryOptimizeUtils)?.setAppUsageState(MODE_OPTIMIZED, Action.EXTERNAL_UPDATE)
assertThat(currentOptMode).isEqualTo(MODE_UNRESTRICTED)
}
@Test
fun updateBatteryOptimizationMode_optimizationModeNotChanged_verifyAction() {
whenever(testBatteryOptimizeUtils?.isOptimizeModeMutable).thenReturn(false)
whenever(testBatteryOptimizeUtils?.getAppOptimizationMode(true))
.thenReturn(MODE_UNRESTRICTED)
val currentOptMode =
AppOptModeSharedPreferencesUtils.updateBatteryOptimizationMode(
context,
UID,
PACKAGE_NAME,
MODE_OPTIMIZED,
Action.EXTERNAL_UPDATE,
testBatteryOptimizeUtils
)
verify(testBatteryOptimizeUtils, never())?.setAppUsageState(anyInt(), any())
assertThat(currentOptMode).isEqualTo(MODE_UNKNOWN)
}
@Test
fun updateBatteryOptimizationMode_updateToSameOptimizationMode_verifyAction() {
whenever(testBatteryOptimizeUtils?.isOptimizeModeMutable).thenReturn(true)
whenever(testBatteryOptimizeUtils?.getAppOptimizationMode(true)).thenReturn(MODE_RESTRICTED)
val currentOptMode =
AppOptModeSharedPreferencesUtils.updateBatteryOptimizationMode(
context,
UID,
PACKAGE_NAME,
MODE_RESTRICTED,
Action.EXTERNAL_UPDATE,
testBatteryOptimizeUtils
)
verify(testBatteryOptimizeUtils)?.setAppUsageState(MODE_RESTRICTED, Action.EXTERNAL_UPDATE)
assertThat(currentOptMode).isEqualTo(MODE_RESTRICTED)
}
private fun insertAppOptModeEventForTest(expirationTime: Long) {
whenever(testBatteryOptimizeUtils?.isOptimizeModeMutable).thenReturn(true)
whenever(testBatteryOptimizeUtils?.getAppOptimizationMode(true)).thenReturn(MODE_OPTIMIZED)
AppOptModeSharedPreferencesUtils.updateAppOptModeExpirationInternal(
context,
mutableListOf(UID),
mutableListOf(PACKAGE_NAME),
mutableListOf(MODE_OPTIMIZED),
longArrayOf(expirationTime)
) { _: Int, _: String ->
testBatteryOptimizeUtils
}
}
companion object {
const val UID: Int = 12345
const val UNSET_UID: Int = 15432
const val PACKAGE_NAME: String = "com.android.app"
private fun assertAppOptimizationModeEventInfo(
event: AppOptimizationModeEvent,
uid: Int,
packageName: String,
resetOptimizationMode: Int,
expirationTime: Long
) {
assertThat(event.uid).isEqualTo(uid)
assertThat(event.packageName).isEqualTo(packageName)
assertThat(event.resetOptimizationMode).isEqualTo(resetOptimizationMode)
assertThat(event.expirationTime).isEqualTo(expirationTime)
}
}
}