diff --git a/Android.bp b/Android.bp index 06ce8ab22f5..b96d0dc2928 100644 --- a/Android.bp +++ b/Android.bp @@ -95,15 +95,11 @@ android_library { "SettingsLibActivityEmbedding", "aconfig_settings_flags_lib", "accessibility_settings_flags_lib", - "app-usage-event-protos-lite", - "battery-event-protos-lite", - "battery-usage-slot-protos-lite", "contextualcards", "development_settings_flag_lib", "factory_reset_flags_lib", "fuelgauge-log-protos-lite", - "fuelgauge-usage-state-protos-lite", - "power-anomaly-event-protos-lite", + "fuelgauge-protos-lite", "settings-contextual-card-protos-lite", "settings-log-bridge-protos-lite", "settings-logtags", diff --git a/protos/fuelgauge_log.proto b/protos/fuelgauge_log.proto index b16958d8e2b..3be173e87ca 100644 --- a/protos/fuelgauge_log.proto +++ b/protos/fuelgauge_log.proto @@ -21,6 +21,7 @@ message BatteryOptimizeHistoricalLogEntry { BACKUP = 5; FORCE_RESET = 6; EXTERNAL_UPDATE = 7; + EXPIRATION_RESET = 8; } optional string package_name = 1; diff --git a/res/xml/more_security_privacy_settings.xml b/res/xml/more_security_privacy_settings.xml index f1678bc2f3f..799ff1e815a 100644 --- a/res/xml/more_security_privacy_settings.xml +++ b/res/xml/more_security_privacy_settings.xml @@ -121,6 +121,7 @@ android:title="@string/cellular_security_title" android:summary="@string/cellular_security_summary" android:fragment="com.android.settings.network.telephony.CellularSecuritySettingsFragment" + settings:controller="com.android.settings.network.CellularSecurityPreferenceController" settings:searchable="false"/> diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index 4e77243b09d..b5df0e22c80 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -210,24 +210,31 @@ public class AccessibilitySettings extends DashboardFragment implements public void onCreate(Bundle icicle) { super.onCreate(icicle); initializeAllPreferences(); + updateAllPreferences(); + mNeedPreferencesUpdate = false; registerContentMonitors(); registerInputDeviceListener(); } @Override - public void onResume() { - super.onResume(); - updateAllPreferences(); + public void onStart() { + super.onStart(); + mIsForeground = true; } @Override - public void onStart() { + public void onResume() { + super.onResume(); if (mNeedPreferencesUpdate) { updateAllPreferences(); mNeedPreferencesUpdate = false; } - mIsForeground = true; - super.onStart(); + } + + @Override + public void onPause() { + super.onPause(); + mNeedPreferencesUpdate = true; } @Override diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index 42e6d9c4b68..005c0730343 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -43,6 +43,7 @@ import com.android.settings.core.InstrumentedPreferenceFragment; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; 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.BatteryEntry; import com.android.settings.overlay.FeatureFactory; @@ -274,9 +275,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment final int currentOptimizeMode = mBatteryOptimizeUtils.getAppOptimizationMode(); mLogStringBuilder.append(", onPause mode = ").append(currentOptimizeMode); logMetricCategory(currentOptimizeMode); - mExecutor.execute( () -> { + if (currentOptimizeMode != mOptimizationMode) { + AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid( + getContext(), mBatteryOptimizeUtils.getUid()); + } BatteryOptimizeLogUtils.writeLog( getContext().getApplicationContext(), Action.LEAVE, diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java index 9c7f00701d5..3e376184ac2 100644 --- a/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java +++ b/src/com/android/settings/fuelgauge/BatteryOptimizeUtils.java @@ -182,6 +182,14 @@ public class BatteryOptimizeUtils { && getAppOptimizationMode() != BatteryOptimizeUtils.MODE_RESTRICTED; } + String getPackageName() { + return mPackageName == null ? UNKNOWN_PACKAGE : mPackageName; + } + + int getUid() { + return mUid; + } + /** Gets the list of installed applications. */ public static ArraySet getInstalledApplications( 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) { return appOpsManager.checkOpNoThrow( AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName); diff --git a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java index b662d3ef908..2d2c838bc36 100644 --- a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java @@ -35,6 +35,7 @@ import androidx.annotation.VisibleForTesting; import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.fuelgauge.batteryusage.AppOptModeSharedPreferencesUtils; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.HelpUtils; @@ -121,6 +122,10 @@ public class PowerBackgroundUsageDetail extends DashboardFragment mExecutor.execute( () -> { + if (currentOptimizeMode != mOptimizationMode) { + AppOptModeSharedPreferencesUtils.deleteAppOptimizationModeEventByUid( + getContext(), mBatteryOptimizeUtils.getUid()); + } BatteryOptimizeLogUtils.writeLog( getContext().getApplicationContext(), Action.LEAVE, diff --git a/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtils.kt b/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtils.kt new file mode 100644 index 00000000000..60db0310040 --- /dev/null +++ b/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtils.kt @@ -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 = + synchronized(appOptimizationModeLock) { getAppOptModeEventsMap(context).values.toList() } + + /** Updates the app optimization mode event data. */ + @JvmStatic + fun updateAppOptModeExpiration( + context: Context, + uids: List, + packageNames: List, + optimizationModes: List, + 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(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, + packageNames: List, + optimizationModes: List, + expirationTimes: LongArray, + getBatteryOptimizeUtils: (Int, String) -> BatteryOptimizeUtils + ) = + synchronized(appOptimizationModeLock) { + val eventsMap = getAppOptModeEventsMap(context) + val expirationEvents: MutableMap = 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 { + val sharedPreferences = getSharedPreferences(context) + val allKeys = sharedPreferences.all?.keys ?: emptySet() + if (allKeys.isEmpty()) { + return ArrayMap() + } + val eventsMap = ArrayMap(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 + ) { + 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) { + 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) + } +} diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java index 26bb6dd0a01..08369127b6d 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java +++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java @@ -167,6 +167,8 @@ public final class BatteryUsageDataLoader { try { final long start = System.currentTimeMillis(); loadBatteryStatsData(context, isFullChargeStart); + AppOptModeSharedPreferencesUtils.resetExpiredAppOptModeBeforeTimestamp( + context, System.currentTimeMillis()); if (!isFullChargeStart) { // No app usage data or battery diff data at this time. final UserIdsSeries userIdsSeries = diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java index ff953e77f86..7613d9ab097 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java +++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/BugReportContentProvider.java @@ -54,6 +54,7 @@ public final class BugReportContentProvider extends ContentProvider { return; } writer.println("dump BatteryUsage and AppUsage states:"); + LogUtils.dumpAppOptimizationModeEventHist(context, writer); LogUtils.dumpBatteryUsageDatabaseHist(context, writer); LogUtils.dumpAppUsageDatabaseHist(context, writer); LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer); diff --git a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java index 88bd4adf463..b2300308fd4 100644 --- a/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java +++ b/src/com/android/settings/fuelgauge/batteryusage/bugreport/LogUtils.java @@ -20,6 +20,8 @@ import android.content.Context; import android.util.Log; 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.ConvertUtils; 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_FOR_ENTRY = Duration.ofHours(4); + static void dumpAppOptimizationModeEventHist(Context context, PrintWriter writer) { + writer.println("\n\tApp Optimization Mode Event History:"); + final List events = + AppOptModeSharedPreferencesUtils.getAllEvents(context); + dumpListItems(writer, events, event -> event); + } + static void dumpBatteryUsageDatabaseHist(Context context, PrintWriter writer) { // Dumps periodic job events. writer.println("\nBattery PeriodicJob History:"); diff --git a/src/com/android/settings/fuelgauge/protos/Android.bp b/src/com/android/settings/fuelgauge/protos/Android.bp index 462962b63c5..40fb987e62c 100644 --- a/src/com/android/settings/fuelgauge/protos/Android.bp +++ b/src/com/android/settings/fuelgauge/protos/Android.bp @@ -9,41 +9,9 @@ package { } java_library { - name: "app-usage-event-protos-lite", + name: "fuelgauge-protos-lite", proto: { type: "lite", }, - srcs: ["app_usage_event.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"], + srcs: ["*.proto"], } diff --git a/src/com/android/settings/fuelgauge/protos/app_optimization_mode_event.proto b/src/com/android/settings/fuelgauge/protos/app_optimization_mode_event.proto new file mode 100644 index 00000000000..81d51bc0d6b --- /dev/null +++ b/src/com/android/settings/fuelgauge/protos/app_optimization_mode_event.proto @@ -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 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; +} diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java index 21ff6c7266b..1463cd0b7f9 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java @@ -385,6 +385,7 @@ public class AccessibilitySettingsTest { mFragment.onContentChanged(); mFragment.onStart(); + mFragment.onResume(); RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference( COMPONENT_NAME.flattenToString()); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonControllerTest.java index adcc6172b92..cbf14321e4f 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamButtonControllerTest.java @@ -34,12 +34,12 @@ import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadow import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settingslib.widget.ActionButtonsPreference; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; @@ -59,7 +59,7 @@ public class AudioStreamButtonControllerTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private static final String KEY = "audio_stream_button"; private static final int BROADCAST_ID = 1; - @Spy Context mContext = ApplicationProvider.getApplicationContext(); + private final Context mContext = ApplicationProvider.getApplicationContext(); @Mock private AudioStreamsHelper mAudioStreamsHelper; @Mock private PreferenceScreen mScreen; @Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState; @@ -80,6 +80,11 @@ public class AudioStreamButtonControllerTest { .thenReturn(mPreference); } + @After + public void tearDown() { + ShadowAudioStreamsHelper.reset(); + } + @Test public void testDisplayPreference_sourceConnected_setDisconnectButton() { when(mAudioStreamsHelper.getAllConnectedSources()) diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderControllerTest.java index 0af9c17543b..0cd5d61168b 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamHeaderControllerTest.java @@ -34,12 +34,12 @@ import com.android.settings.testutils.shadow.ShadowThreadUtils; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.widget.LayoutPreference; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; @@ -61,7 +61,7 @@ public class AudioStreamHeaderControllerTest { private static final String KEY = "audio_stream_header"; private static final int BROADCAST_ID = 1; private static final String BROADCAST_NAME = "broadcast name"; - @Spy Context mContext = ApplicationProvider.getApplicationContext(); + private final Context mContext = ApplicationProvider.getApplicationContext(); @Mock private AudioStreamsHelper mAudioStreamsHelper; @Mock private PreferenceScreen mScreen; @Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState; @@ -81,6 +81,12 @@ public class AudioStreamHeaderControllerTest { when(mPreference.getContext()).thenReturn(mContext); } + @After + public void tearDown() { + ShadowEntityHeaderController.reset(); + ShadowAudioStreamsHelper.reset(); + } + @Test public void testDisplayPreference_sourceConnected_setSummary() { when(mAudioStreamsHelper.getAllConnectedSources()) diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java index 0c93e3e1879..456e45d302a 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamPreferenceTest.java @@ -30,6 +30,7 @@ import android.view.View; import androidx.preference.Preference.OnPreferenceClickListener; import androidx.preference.PreferenceViewHolder; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsProgressCategoryController.AudioStreamState; @@ -42,7 +43,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import java.util.Collections; @@ -53,7 +53,7 @@ public class AudioStreamPreferenceTest { private static final String PROGRAM_NAME = "program_name"; private static final int BROADCAST_RSSI = 1; @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); - private Context mContext; + private final Context mContext = ApplicationProvider.getApplicationContext(); private AudioStreamPreference mPreference; @Mock private BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata; @Mock private BluetoothLeBroadcastReceiveState mBluetoothLeBroadcastReceiveState; @@ -61,7 +61,6 @@ public class AudioStreamPreferenceTest { @Before public void setUp() { - mContext = RuntimeEnvironment.application; mPreference = new AudioStreamPreference(mContext, null); when(mBluetoothLeBroadcastMetadata.getBroadcastId()).thenReturn(BROADCAST_ID); when(mBluetoothLeBroadcastMetadata.getBroadcastName()).thenReturn(BROADCAST_NAME); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceControllerTest.java index c0296350db4..3fd257f100c 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceControllerTest.java @@ -28,6 +28,7 @@ import android.content.Context; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; import org.junit.Before; import org.junit.Rule; @@ -37,7 +38,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; @RunWith(RobolectricTestRunner.class) public class AudioStreamsActiveDeviceControllerTest { @@ -48,7 +48,7 @@ public class AudioStreamsActiveDeviceControllerTest { @Before public void setUp() { - Context context = RuntimeEnvironment.application; + Context context = ApplicationProvider.getApplicationContext(); mController = new AudioStreamsActiveDeviceController( context, AudioStreamsActiveDeviceController.KEY); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdaterTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdaterTest.java index 3bcc9a3d079..4403528e2fb 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsActiveDeviceSummaryUpdaterTest.java @@ -30,12 +30,12 @@ import com.android.settings.R; import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper; import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; @@ -49,7 +49,7 @@ import org.robolectric.annotation.Config; public class AudioStreamsActiveDeviceSummaryUpdaterTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private static final String DEVICE_NAME = "device_name"; - @Spy private final Context mContext = ApplicationProvider.getApplicationContext(); + private final Context mContext = ApplicationProvider.getApplicationContext(); private final AudioStreamsActiveDeviceSummaryUpdater.OnSummaryChangeListener mFakeListener = summary -> mUpdatedSummary = summary; @Mock private CachedBluetoothDevice mCachedBluetoothDevice; @@ -60,10 +60,14 @@ public class AudioStreamsActiveDeviceSummaryUpdaterTest { @Before public void setUp() { ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper); - ShadowAudioStreamsHelper.resetCachedBluetoothDevice(); mUpdater = new AudioStreamsActiveDeviceSummaryUpdater(mContext, mFakeListener); } + @After + public void tearDown() { + ShadowAudioStreamsHelper.reset(); + } + @Test public void register_summaryUpdated() { mUpdater.register(true); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/MediaControlHelperTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/MediaControlHelperTest.java index 113fc722284..2506d863a56 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/MediaControlHelperTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/MediaControlHelperTest.java @@ -23,6 +23,8 @@ import android.media.session.MediaController; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; +import androidx.test.core.app.ApplicationProvider; + import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper; import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowLocalMediaManager; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -30,6 +32,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.media.BluetoothMediaDevice; import com.android.settingslib.media.LocalMediaManager; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -38,7 +41,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import java.util.List; @@ -64,15 +66,20 @@ public class MediaControlHelperTest { @Before public void setUp() { - mContext = spy(RuntimeEnvironment.application); + mContext = spy(ApplicationProvider.getApplicationContext()); when(mContext.getSystemService(MediaSessionManager.class)).thenReturn(mMediaSessionManager); when(mMediaSessionManager.getActiveSessions(any())).thenReturn(List.of(mMediaController)); when(mMediaController.getPackageName()).thenReturn(FAKE_PACKAGE); when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState); - ShadowAudioStreamsHelper.resetCachedBluetoothDevice(); ShadowLocalMediaManager.setUseMock(mLocalMediaManager); } + @After + public void tearDown() { + ShadowAudioStreamsHelper.reset(); + ShadowLocalMediaManager.reset(); + } + @Test public void testStart_noBluetoothManager_doNothing() { MediaControlHelper helper = new MediaControlHelper(mContext, null); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SyncedStateTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SyncedStateTest.java index 64e1bc48808..e9eab5066ac 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SyncedStateTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/SyncedStateTest.java @@ -21,7 +21,6 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.shadows.ShadowLooper.shadowMainLooper; @@ -33,6 +32,7 @@ import android.content.Context; import androidx.preference.Preference; import androidx.test.core.app.ApplicationProvider; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -60,10 +60,15 @@ public class SyncedStateTest { @Before public void setUp() { ShadowAlertDialog.reset(); - mMockContext = spy(ApplicationProvider.getApplicationContext()); + mMockContext = ApplicationProvider.getApplicationContext(); mInstance = SyncedState.getInstance(); } + @After + public void tearDown() { + ShadowAlertDialog.reset(); + } + @Test public void testGetInstance() { assertThat(mInstance).isNotNull(); diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java index 0dff64d99de..331a30bec20 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java @@ -18,12 +18,15 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams.testshado import android.bluetooth.BluetoothLeBroadcastReceiveState; +import androidx.annotation.Nullable; + import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsHelper; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; import java.util.List; import java.util.Optional; @@ -31,20 +34,22 @@ import java.util.Optional; @Implements(value = AudioStreamsHelper.class, callThroughByDefault = false) public class ShadowAudioStreamsHelper { private static AudioStreamsHelper sMockHelper; - private static Optional sCachedBluetoothDevice; + @Nullable private static CachedBluetoothDevice sCachedBluetoothDevice; public static void setUseMock(AudioStreamsHelper mockAudioStreamsHelper) { sMockHelper = mockAudioStreamsHelper; } - /** Resets {@link CachedBluetoothDevice} */ - public static void resetCachedBluetoothDevice() { - sCachedBluetoothDevice = Optional.empty(); + /** Reset static fields */ + @Resetter + public static void reset() { + sMockHelper = null; + sCachedBluetoothDevice = null; } public static void setCachedBluetoothDeviceInSharingOrLeConnected( CachedBluetoothDevice cachedBluetoothDevice) { - sCachedBluetoothDevice = Optional.of(cachedBluetoothDevice); + sCachedBluetoothDevice = cachedBluetoothDevice; } @Implementation @@ -56,6 +61,6 @@ public class ShadowAudioStreamsHelper { @Implementation public static Optional getCachedBluetoothDeviceInSharingOrLeConnected( LocalBluetoothManager manager) { - return sCachedBluetoothDevice; + return Optional.ofNullable(sCachedBluetoothDevice); } } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowEntityHeaderController.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowEntityHeaderController.java index 951fb26c252..82519c5e8a4 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowEntityHeaderController.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowEntityHeaderController.java @@ -25,6 +25,7 @@ import com.android.settings.widget.EntityHeaderController; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; @Implements(value = EntityHeaderController.class, callThroughByDefault = false) public class ShadowEntityHeaderController { @@ -34,6 +35,12 @@ public class ShadowEntityHeaderController { sMockController = mockController; } + /** Reset static fields */ + @Resetter + public static void reset() { + sMockController = null; + } + /** Returns new instance of {@link EntityHeaderController} */ @Implementation public static EntityHeaderController newInstance( diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowLocalMediaManager.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowLocalMediaManager.java index 02f12c20350..4c679fb81f5 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowLocalMediaManager.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowLocalMediaManager.java @@ -21,6 +21,7 @@ import com.android.settingslib.media.MediaDevice; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.annotation.Resetter; import java.util.Collections; @@ -34,6 +35,13 @@ public class ShadowLocalMediaManager { sMockManager = mockLocalMediaManager; } + /** Reset static fields */ + @Resetter + public static void reset() { + sMockManager = null; + sDeviceCallback = null; + } + /** Triggers onDeviceListUpdate of {@link LocalMediaManager.DeviceCallback} */ public static void onDeviceListUpdate() { sDeviceCallback.onDeviceListUpdate(Collections.emptyList()); @@ -45,7 +53,7 @@ public class ShadowLocalMediaManager { sMockManager.startScan(); } - /** Registers {@link LocalMediaManager.DeviceCallback} */ + /** Registers {@link LocalMediaManager.DeviceCallback} */ @Implementation public void registerCallback(LocalMediaManager.DeviceCallback deviceCallback) { sMockManager.registerCallback(deviceCallback); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtilsTest.kt b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtilsTest.kt new file mode 100644 index 00000000000..02d3739a9c1 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/AppOptModeSharedPreferencesUtilsTest.kt @@ -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) + } + } +} diff --git a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java index ac86eb071f2..e488792d55b 100644 --- a/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/applications/specialaccess/notificationaccess/ApprovalPreferenceControllerTest.java @@ -189,8 +189,8 @@ public class ApprovalPreferenceControllerTest { @Test @EnableFlags(Flags.FLAG_MODES_API) - @Ignore("b/339550695") public void disable() { + when(mNm.isNotificationPolicyAccessGrantedForPackage(anyString())).thenReturn(false); mController.disable(mCn); verify(mFeatureFactory.metricsFeatureProvider).action( mContext,