Merge "Add support to exempt the battery usage with visible FGS notifications" into tm-dev

This commit is contained in:
Jing Ji
2022-03-19 07:11:33 +00:00
committed by Android (Google) Code Review
5 changed files with 257 additions and 62 deletions

View File

@@ -5796,7 +5796,7 @@
<!-- The types of state where we'll exempt its battery usage during that state. <!-- The types of state where we'll exempt its battery usage during that state.
The state here must be one or a combination of STATE_TYPE_* in BaseAppStateTracker. The state here must be one or a combination of STATE_TYPE_* in BaseAppStateTracker.
--> -->
<integer name="config_bg_current_drain_exempted_types">9</integer> <integer name="config_bg_current_drain_exempted_types">25</integer>
<!-- The behavior when an app has the permission ACCESS_BACKGROUND_LOCATION granted, <!-- The behavior when an app has the permission ACCESS_BACKGROUND_LOCATION granted,
whether or not the system will use a higher threshold towards its background battery usage whether or not the system will use a higher threshold towards its background battery usage

View File

@@ -32,18 +32,22 @@ import static com.android.server.am.BaseAppStateTracker.ONE_HOUR;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.ActivityManagerInternal.ForegroundServiceStateListener; import android.app.ActivityManagerInternal.ForegroundServiceStateListener;
import android.app.IProcessObserver; import android.app.IProcessObserver;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.pm.ServiceInfo.ForegroundServiceType; import android.content.pm.ServiceInfo.ForegroundServiceType;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerExemptionManager.ReasonCode;
import android.os.RemoteException;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TimeUtils; import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
@@ -53,7 +57,6 @@ import com.android.server.am.AppFGSTracker.PackageDurations;
import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy; import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy;
import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent; import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
import com.android.server.am.BaseAppStateTracker.Injector; import com.android.server.am.BaseAppStateTracker.Injector;
import com.android.server.notification.NotificationManagerInternal;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
@@ -72,11 +75,14 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
private final MyHandler mHandler; private final MyHandler mHandler;
@GuardedBy("mLock") @GuardedBy("mLock")
private final UidProcessMap<ArraySet<Integer>> mFGSNotificationIDs = new UidProcessMap<>(); private final UidProcessMap<SparseBooleanArray> mFGSNotificationIDs = new UidProcessMap<>();
// Unlocked since it's only accessed in single thread. // Unlocked since it's only accessed in single thread.
private final ArrayMap<PackageDurations, Long> mTmpPkgDurations = new ArrayMap<>(); private final ArrayMap<PackageDurations, Long> mTmpPkgDurations = new ArrayMap<>();
@VisibleForTesting
final NotificationListener mNotificationListener = new NotificationListener();
final IProcessObserver.Stub mProcessObserver = new IProcessObserver.Stub() { final IProcessObserver.Stub mProcessObserver = new IProcessObserver.Stub() {
@Override @Override
public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) { public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
@@ -116,6 +122,8 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
static final int MSG_FOREGROUND_SERVICES_CHANGED = 2; static final int MSG_FOREGROUND_SERVICES_CHANGED = 2;
static final int MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED = 3; static final int MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED = 3;
static final int MSG_CHECK_LONG_RUNNING_FGS = 4; static final int MSG_CHECK_LONG_RUNNING_FGS = 4;
static final int MSG_NOTIFICATION_POSTED = 5;
static final int MSG_NOTIFICATION_REMOVED = 6;
private final AppFGSTracker mTracker; private final AppFGSTracker mTracker;
@@ -146,6 +154,12 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
case MSG_CHECK_LONG_RUNNING_FGS: case MSG_CHECK_LONG_RUNNING_FGS:
mTracker.checkLongRunningFgs(); mTracker.checkLongRunningFgs();
break; break;
case MSG_NOTIFICATION_POSTED:
mTracker.handleNotificationPosted((String) msg.obj, msg.arg1, msg.arg2);
break;
case MSG_NOTIFICATION_REMOVED:
mTracker.handleNotificationRemoved((String) msg.obj, msg.arg1, msg.arg2);
break;
} }
} }
} }
@@ -223,19 +237,36 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
private void handleForegroundServiceNotificationUpdated(String packageName, int uid, private void handleForegroundServiceNotificationUpdated(String packageName, int uid,
int notificationId) { int notificationId) {
synchronized (mLock) { synchronized (mLock) {
SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, packageName);
if (notificationId > 0) { if (notificationId > 0) {
ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
if (notificationIDs == null) { if (notificationIDs == null) {
notificationIDs = new ArraySet<>(); notificationIDs = new SparseBooleanArray();
mFGSNotificationIDs.put(uid, packageName, notificationIDs); mFGSNotificationIDs.put(uid, packageName, notificationIDs);
} }
notificationIDs.add(notificationId); notificationIDs.put(notificationId, false);
} else if (notificationId < 0) { } else if (notificationId < 0) {
final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
if (notificationIDs != null) { if (notificationIDs != null) {
notificationIDs.remove(-notificationId); final int indexOfKey = notificationIDs.indexOfKey(-notificationId);
if (notificationIDs.isEmpty()) { if (indexOfKey >= 0) {
mFGSNotificationIDs.remove(uid, packageName); final boolean wasVisible = notificationIDs.valueAt(indexOfKey);
notificationIDs.removeAt(indexOfKey);
if (notificationIDs.size() == 0) {
mFGSNotificationIDs.remove(uid, packageName);
}
// Walk through the list of FGS notification IDs and see if there are any
// visible ones.
for (int i = notificationIDs.size() - 1; i >= 0; i--) {
if (notificationIDs.valueAt(i)) {
// Still visible, nothing to do.
return;
}
}
if (wasVisible) {
// That was the last visible notification, notify the listeners.
notifyListenersOnStateChange(uid, packageName, false,
SystemClock.elapsedRealtime(),
STATE_TYPE_FGS_WITH_NOTIFICATION);
}
} }
} }
} }
@@ -244,20 +275,74 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
@GuardedBy("mLock") @GuardedBy("mLock")
private boolean hasForegroundServiceNotificationsLocked(String packageName, int uid) { private boolean hasForegroundServiceNotificationsLocked(String packageName, int uid) {
final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName); final SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, packageName);
if (notificationIDs == null || notificationIDs.isEmpty()) { if (notificationIDs == null || notificationIDs.size() == 0) {
return false; return false;
} }
final NotificationManagerInternal nm = mInjector.getNotificationManagerInternal();
final int userId = UserHandle.getUserId(uid);
for (int i = notificationIDs.size() - 1; i >= 0; i--) { for (int i = notificationIDs.size() - 1; i >= 0; i--) {
if (nm.isNotificationShown(packageName, null, notificationIDs.valueAt(i), userId)) { if (notificationIDs.valueAt(i)) {
return true; return true;
} }
} }
return false; return false;
} }
private void handleNotificationPosted(String pkgName, int uid, int notificationId) {
synchronized (mLock) {
final SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, pkgName);
final int indexOfKey;
if (notificationIDs == null
|| (indexOfKey = notificationIDs.indexOfKey(notificationId)) < 0) {
return;
}
if (notificationIDs.valueAt(indexOfKey)) {
// It's already visible.
return;
}
boolean anyVisible = false;
// Walk through the list of FGS notification IDs and see if there are any visible ones.
for (int i = notificationIDs.size() - 1; i >= 0; i--) {
if (notificationIDs.valueAt(i)) {
anyVisible = true;
break;
}
}
notificationIDs.setValueAt(indexOfKey, true);
if (!anyVisible) {
// We didn't have any visible FGS notifications but now we have one,
// let the listeners know.
notifyListenersOnStateChange(uid, pkgName, true, SystemClock.elapsedRealtime(),
STATE_TYPE_FGS_WITH_NOTIFICATION);
}
}
}
private void handleNotificationRemoved(String pkgName, int uid, int notificationId) {
synchronized (mLock) {
final SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, pkgName);
final int indexOfKey;
if (notificationIDs == null
|| (indexOfKey = notificationIDs.indexOfKey(notificationId)) < 0) {
return;
}
if (!notificationIDs.valueAt(indexOfKey)) {
// It's already invisible.
return;
}
notificationIDs.setValueAt(indexOfKey, false);
// Walk through the list of FGS notification IDs and see if there are any visible ones.
for (int i = notificationIDs.size() - 1; i >= 0; i--) {
if (notificationIDs.valueAt(i)) {
// Still visible, nothing to do.
return;
}
}
// Nothing is visible now, let the listeners know.
notifyListenersOnStateChange(uid, pkgName, false, SystemClock.elapsedRealtime(),
STATE_TYPE_FGS_WITH_NOTIFICATION);
}
}
@GuardedBy("mLock") @GuardedBy("mLock")
private void scheduleDurationCheckLocked(long now) { private void scheduleDurationCheckLocked(long now) {
// Look for the active FGS with longest running time till now. // Look for the active FGS with longest running time till now.
@@ -374,7 +459,19 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
synchronized (mLock) { synchronized (mLock) {
scheduleDurationCheckLocked(SystemClock.elapsedRealtime()); scheduleDurationCheckLocked(SystemClock.elapsedRealtime());
} }
try {
mNotificationListener.registerAsSystemService(mContext,
new ComponentName(mContext, NotificationListener.class),
UserHandle.USER_ALL);
} catch (RemoteException e) {
// Intra-process call, should never happen.
}
} else { } else {
try {
mNotificationListener.unregisterAsSystemService();
} catch (RemoteException e) {
// Intra-process call, should never happen.
}
mHandler.removeMessages(MyHandler.MSG_CHECK_LONG_RUNNING_FGS); mHandler.removeMessages(MyHandler.MSG_CHECK_LONG_RUNNING_FGS);
synchronized (mLock) { synchronized (mLock) {
mPkgEvents.clear(); mPkgEvents.clear();
@@ -436,9 +533,9 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
boolean hasForegroundServiceNotifications(int uid) { boolean hasForegroundServiceNotifications(int uid) {
synchronized (mLock) { synchronized (mLock) {
final SparseArray<ArrayMap<String, ArraySet<Integer>>> map = final SparseArray<ArrayMap<String, SparseBooleanArray>> map =
mFGSNotificationIDs.getMap(); mFGSNotificationIDs.getMap();
final ArrayMap<String, ArraySet<Integer>> pkgs = map.get(uid); final ArrayMap<String, SparseBooleanArray> pkgs = map.get(uid);
if (pkgs != null) { if (pkgs != null) {
for (int i = pkgs.size() - 1; i >= 0; i--) { for (int i = pkgs.size() - 1; i >= 0; i--) {
if (hasForegroundServiceNotificationsLocked(pkgs.keyAt(i), uid)) { if (hasForegroundServiceNotificationsLocked(pkgs.keyAt(i), uid)) {
@@ -463,7 +560,7 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
pw.println("APPS WITH ACTIVE FOREGROUND SERVICES:"); pw.println("APPS WITH ACTIVE FOREGROUND SERVICES:");
prefix = " " + prefix; prefix = " " + prefix;
synchronized (mLock) { synchronized (mLock) {
final SparseArray<ArrayMap<String, ArraySet<Integer>>> map = final SparseArray<ArrayMap<String, SparseBooleanArray>> map =
mFGSNotificationIDs.getMap(); mFGSNotificationIDs.getMap();
if (map.size() == 0) { if (map.size() == 0) {
pw.print(prefix); pw.print(prefix);
@@ -472,7 +569,7 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
for (int i = 0, size = map.size(); i < size; i++) { for (int i = 0, size = map.size(); i < size; i++) {
final int uid = map.keyAt(i); final int uid = map.keyAt(i);
final String uidString = UserHandle.formatUid(uid); final String uidString = UserHandle.formatUid(uid);
final ArrayMap<String, ArraySet<Integer>> pkgs = map.valueAt(i); final ArrayMap<String, SparseBooleanArray> pkgs = map.valueAt(i);
for (int j = 0, numOfPkgs = pkgs.size(); j < numOfPkgs; j++) { for (int j = 0, numOfPkgs = pkgs.size(); j < numOfPkgs; j++) {
final String pkgName = pkgs.keyAt(j); final String pkgName = pkgs.keyAt(j);
pw.print(prefix); pw.print(prefix);
@@ -622,6 +719,28 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
} }
} }
@VisibleForTesting
class NotificationListener extends NotificationListenerService {
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap map) {
if (DEBUG_BACKGROUND_FGS_TRACKER) {
Slog.i(TAG, "Notification posted: " + sbn);
}
mHandler.obtainMessage(MyHandler.MSG_NOTIFICATION_POSTED,
sbn.getUid(), sbn.getId(), sbn.getPackageName()).sendToTarget();
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
int reason) {
if (DEBUG_BACKGROUND_FGS_TRACKER) {
Slog.i(TAG, "Notification removed: " + sbn);
}
mHandler.obtainMessage(MyHandler.MSG_NOTIFICATION_REMOVED,
sbn.getUid(), sbn.getId(), sbn.getPackageName()).sendToTarget();
}
}
static final class AppFGSPolicy extends BaseAppStateEventsPolicy<AppFGSTracker> { static final class AppFGSPolicy extends BaseAppStateEventsPolicy<AppFGSTracker> {
/** /**
* Whether or not we should enable the monitoring on abusive FGS. * Whether or not we should enable the monitoring on abusive FGS.

View File

@@ -64,13 +64,15 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> {
static final int STATE_TYPE_MEDIA_SESSION = 1; static final int STATE_TYPE_MEDIA_SESSION = 1;
static final int STATE_TYPE_FGS_MEDIA_PLAYBACK = 1 << 1; static final int STATE_TYPE_FGS_MEDIA_PLAYBACK = 1 << 1;
static final int STATE_TYPE_FGS_LOCATION = 1 << 2; static final int STATE_TYPE_FGS_LOCATION = 1 << 2;
static final int STATE_TYPE_PERMISSION = 1 << 3; static final int STATE_TYPE_FGS_WITH_NOTIFICATION = 1 << 3;
static final int STATE_TYPE_NUM = 4; static final int STATE_TYPE_PERMISSION = 1 << 4;
static final int STATE_TYPE_NUM = 5;
static final int STATE_TYPE_INDEX_MEDIA_SESSION = 0; static final int STATE_TYPE_INDEX_MEDIA_SESSION = 0;
static final int STATE_TYPE_INDEX_FGS_MEDIA_PLAYBACK = 1; static final int STATE_TYPE_INDEX_FGS_MEDIA_PLAYBACK = 1;
static final int STATE_TYPE_INDEX_FGS_LOCATION = 2; static final int STATE_TYPE_INDEX_FGS_LOCATION = 2;
static final int STATE_TYPE_INDEX_PERMISSION = 3; static final int STATE_TYPE_INDEX_FGS_WITH_NOTIFICATION = 3;
static final int STATE_TYPE_INDEX_PERMISSION = 4;
protected final AppRestrictionController mAppRestrictionController; protected final AppRestrictionController mAppRestrictionController;
protected final Injector<T> mInjector; protected final Injector<T> mInjector;
@@ -129,6 +131,9 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> {
case STATE_TYPE_FGS_LOCATION: case STATE_TYPE_FGS_LOCATION:
sb.append("FGS_LOCATION"); sb.append("FGS_LOCATION");
break; break;
case STATE_TYPE_FGS_WITH_NOTIFICATION:
sb.append("FGS_NOTIFICATION");
break;
case STATE_TYPE_PERMISSION: case STATE_TYPE_PERMISSION:
sb.append("PERMISSION"); sb.append("PERMISSION");
break; break;

View File

@@ -31,6 +31,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission <uses-permission
android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/> android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<!-- needed by MasterClearReceiverTest to display a system dialog --> <!-- needed by MasterClearReceiverTest to display a system dialog -->
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/> <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>

View File

@@ -59,6 +59,7 @@ import static com.android.server.am.AppPermissionTracker.AppPermissionPolicy;
import static com.android.server.am.AppRestrictionController.STOCK_PM_FLAGS; import static com.android.server.am.AppRestrictionController.STOCK_PM_FLAGS;
import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_FGS_LOCATION; import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_FGS_LOCATION;
import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_FGS_MEDIA_PLAYBACK; import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_FGS_MEDIA_PLAYBACK;
import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_FGS_WITH_NOTIFICATION;
import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_MEDIA_SESSION; import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_MEDIA_SESSION;
import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION; import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION;
@@ -113,6 +114,7 @@ import android.os.UidBatteryConsumer;
import android.os.UserHandle; import android.os.UserHandle;
import android.permission.PermissionManager; import android.permission.PermissionManager;
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
import android.service.notification.StatusBarNotification;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
@@ -566,6 +568,7 @@ public final class BackgroundRestrictionTest {
DeviceConfigSession<Float> bgCurrentDrainBgRestrictedThreshold = null; DeviceConfigSession<Float> bgCurrentDrainBgRestrictedThreshold = null;
DeviceConfigSession<Boolean> bgPromptFgsWithNotiToBgRestricted = null; DeviceConfigSession<Boolean> bgPromptFgsWithNotiToBgRestricted = null;
DeviceConfigSession<Long> bgNotificationMinInterval = null; DeviceConfigSession<Long> bgNotificationMinInterval = null;
DeviceConfigSession<Integer> bgBatteryExemptionTypes = null;
mBgRestrictionController.addAppBackgroundRestrictionListener(listener); mBgRestrictionController.addAppBackgroundRestrictionListener(listener);
@@ -627,16 +630,25 @@ public final class BackgroundRestrictionTest {
ConstantsObserver.DEFAULT_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL_MS); ConstantsObserver.DEFAULT_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL_MS);
bgNotificationMinInterval.set(windowMs); bgNotificationMinInterval.set(windowMs);
bgBatteryExemptionTypes = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES,
DeviceConfig::getInt,
mContext.getResources().getInteger(
R.integer.config_bg_current_drain_exempted_types));
bgBatteryExemptionTypes.set(0);
mCurrentTimeMillis = 10_000L; mCurrentTimeMillis = 10_000L;
doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp(); doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp(); doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(anyObject()); doReturn(statsList).when(mBatteryStatsInternal).getBatteryUsageStats(anyObject());
doReturn(true).when(mNotificationManagerInternal).isNotificationShown(
testPkgName, null, notificationId, testUser);
mAppFGSTracker.onForegroundServiceStateChanged(testPkgName, testUid, mAppFGSTracker.onForegroundServiceStateChanged(testPkgName, testUid,
testPid, true); testPid, true);
mAppFGSTracker.onForegroundServiceNotificationUpdated( mAppFGSTracker.onForegroundServiceNotificationUpdated(
testPkgName, testUid, notificationId); testPkgName, testUid, notificationId);
mAppFGSTracker.mNotificationListener.onNotificationPosted(new StatusBarNotification(
testPkgName, null, notificationId, null, testUid, testPid,
new Notification(), UserHandle.of(testUser), null, mCurrentTimeMillis), null);
runTestBgCurrentDrainMonitorOnce(listener, stats, uids, runTestBgCurrentDrainMonitorOnce(listener, stats, uids,
new double[]{restrictBucketThresholdMah - 1, 0}, new double[]{restrictBucketThresholdMah - 1, 0},
@@ -915,6 +927,7 @@ public final class BackgroundRestrictionTest {
closeIfNotNull(bgCurrentDrainBgRestrictedThreshold); closeIfNotNull(bgCurrentDrainBgRestrictedThreshold);
closeIfNotNull(bgPromptFgsWithNotiToBgRestricted); closeIfNotNull(bgPromptFgsWithNotiToBgRestricted);
closeIfNotNull(bgNotificationMinInterval); closeIfNotNull(bgNotificationMinInterval);
closeIfNotNull(bgBatteryExemptionTypes);
} }
} }
@@ -1250,14 +1263,15 @@ public final class BackgroundRestrictionTest {
List<Pair<List<MediaController>, Long>> mediaControllers, List<Long> topStateChanges, List<Pair<List<MediaController>, Long>> mediaControllers, List<Long> topStateChanges,
VerificationMode mode) throws Exception { VerificationMode mode) throws Exception {
runExemptionTestOnce( runExemptionTestOnce(
packageName, uid, pid, serviceType, sleepMs, true, perm, mediaControllers, packageName, uid, pid, serviceType, sleepMs, true, false, perm, mediaControllers,
topStateChanges, true, true, topStateChanges, true, true,
() -> checkNotificationShown(new String[] {packageName}, mode, false) () -> checkNotificationShown(new String[] {packageName}, mode, false)
); );
} }
private void runExemptionTestOnce(String packageName, int uid, int pid, private void runExemptionTestOnce(String packageName, int uid, int pid,
int serviceType, long sleepMs, boolean stopAfterSleep, String perm, int serviceType, long sleepMs, boolean stopAfterSleep,
boolean withNotification, String perm,
List<Pair<List<MediaController>, Long>> mediaControllers, List<Pair<List<MediaController>, Long>> mediaControllers,
List<Long> topStateChanges, boolean resetFGSTracker, boolean resetController, List<Long> topStateChanges, boolean resetFGSTracker, boolean resetController,
RunnableWithException r) throws Exception { RunnableWithException r) throws Exception {
@@ -1302,7 +1316,20 @@ public final class BackgroundRestrictionTest {
FOREGROUND_SERVICE_TYPE_NONE); FOREGROUND_SERVICE_TYPE_NONE);
} }
} }
if (withNotification) {
final int notificationId = 1000;
mAppFGSTracker.onForegroundServiceNotificationUpdated(
packageName, uid, notificationId);
final StatusBarNotification noti = new StatusBarNotification(
packageName, null, notificationId, null, uid, pid,
new Notification(), UserHandle.of(UserHandle.getUserId(uid)),
null, mCurrentTimeMillis);
mAppFGSTracker.mNotificationListener.onNotificationPosted(noti, null);
Thread.sleep(sleepMs);
if (stopAfterSleep) {
mAppFGSTracker.mNotificationListener.onNotificationRemoved(noti, null, 0);
}
}
if (perm != null) { if (perm != null) {
doReturn(PERMISSION_GRANTED) doReturn(PERMISSION_GRANTED)
.when(mPermissionManagerServiceInternal) .when(mPermissionManagerServiceInternal)
@@ -1513,7 +1540,8 @@ public final class BackgroundRestrictionTest {
mContext.getResources().getInteger( mContext.getResources().getInteger(
R.integer.config_bg_current_drain_exempted_types)); R.integer.config_bg_current_drain_exempted_types));
bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK
| STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION); | STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION
| STATE_TYPE_FGS_WITH_NOTIFICATION);
bgPermissionMonitorEnabled = new DeviceConfigSession<>( bgPermissionMonitorEnabled = new DeviceConfigSession<>(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1546,7 +1574,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service which starts/stops immediately, we should // Run with a media playback service which starts/stops immediately, we should
// goto the restricted bucket. // goto the restricted bucket.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, 0, true, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, 0, true, false,
null, null, null, listener, stats, uids, null, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0}, new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1556,7 +1584,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time. We should be back to normal. // Run with a media playback service with extended time. We should be back to normal.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
null, null, null, listener, stats, uids, false, null, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0}, new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_ADAPTIVE_BUCKET, timeout, false, true, RESTRICTION_LEVEL_ADAPTIVE_BUCKET, timeout, false,
@@ -1585,7 +1613,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time, with higher current drain. // Run with a media playback service with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
null, null, null, listener, stats, uids, false, null, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0}, new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true, true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1594,7 +1622,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time, with even higher current drain. // Run with a media playback service with extended time, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
null, null, null, listener, stats, uids, false, null, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 1, 0}, new double[]{restrictBucketHighThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false, false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
@@ -1607,8 +1635,8 @@ public final class BackgroundRestrictionTest {
// Run with a media session with extended time, with higher current drain. // Run with a media session with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, null, FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
List.of(Pair.create(createMediaControllers(new String[] {testPkgName1}, null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)), new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
null, listener, stats, uids, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0}, new double[]{restrictBucketHighThresholdMah - 1, 0},
@@ -1618,8 +1646,8 @@ public final class BackgroundRestrictionTest {
// Run with a media session with extended time, with even higher current drain. // Run with a media session with extended time, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, null, FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
List.of(Pair.create(createMediaControllers(new String[] {testPkgName1}, null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)), new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
null, listener, stats, uids, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 1, 0}, new double[]{restrictBucketHighThresholdMah + 1, 0},
@@ -1635,8 +1663,8 @@ public final class BackgroundRestrictionTest {
// Run with a media session with extended time, with moderate current drain, // Run with a media session with extended time, with moderate current drain,
// but it ran on the top when the location service is active. // but it ran on the top when the location service is active.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, null, FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
List.of(Pair.create(createMediaControllers(new String[] {testPkgName1}, null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)), new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
List.of(0L, timeout * 2), listener, stats, uids, List.of(0L, timeout * 2), listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0}, new double[]{restrictBucketThresholdMah + 1, 0},
@@ -1651,7 +1679,7 @@ public final class BackgroundRestrictionTest {
// Run with a location service with extended time, with higher current drain. // Run with a location service with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
null, null, null, listener, stats, uids, null, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0}, new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1660,7 +1688,7 @@ public final class BackgroundRestrictionTest {
// Run with a location service with extended time, with even higher current drain. // Run with a location service with extended time, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
null, null, null, listener, stats, uids, null, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 1, 0}, new double[]{restrictBucketHighThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1675,7 +1703,7 @@ public final class BackgroundRestrictionTest {
// Run with a location service with extended time, with moderate current drain, // Run with a location service with extended time, with moderate current drain,
// but it ran on the top when the location service is active. // but it ran on the top when the location service is active.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
null, null, List.of(0L, timeout * 2), listener, stats, uids, null, null, List.of(0L, timeout * 2), listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0}, new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1692,7 +1720,7 @@ public final class BackgroundRestrictionTest {
// Run with bg location permission, with moderate current drain. // Run with bg location permission, with moderate current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids, ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah - 1, 0}, new double[]{restrictBucketThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1701,7 +1729,7 @@ public final class BackgroundRestrictionTest {
// Run with bg location permission, with a bit higher current drain. // Run with bg location permission, with a bit higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids, ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0}, new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1718,7 +1746,7 @@ public final class BackgroundRestrictionTest {
// Run with bg location permission, with higher current drain. // Run with bg location permission, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
ACCESS_BACKGROUND_LOCATION , null, null, listener, stats, uids, ACCESS_BACKGROUND_LOCATION , null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0}, new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1727,7 +1755,7 @@ public final class BackgroundRestrictionTest {
// Run with bg location permission, with even higher current drain. // Run with bg location permission, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
ACCESS_BACKGROUND_LOCATION , null, null, listener, stats, uids, ACCESS_BACKGROUND_LOCATION , null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 1, 0}, new double[]{restrictBucketHighThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1749,7 +1777,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service which starts/stops immediately, we should // Run with a media playback service which starts/stops immediately, we should
// goto the restricted bucket. // goto the restricted bucket.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, 0, true, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, 0, true, false,
null, null, null, listener, stats, uids, null, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0}, new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1759,7 +1787,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time. We should be back to normal. // Run with a media playback service with extended time. We should be back to normal.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
null, null, null, listener, stats, uids, false, null, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0}, new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_ADAPTIVE_BUCKET, timeout, false, true, RESTRICTION_LEVEL_ADAPTIVE_BUCKET, timeout, false,
@@ -1791,7 +1819,7 @@ public final class BackgroundRestrictionTest {
// Run with a media playback service with extended time, with higher current drain. // Run with a media playback service with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
null, null, null, listener, stats, uids, false, null, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0}, new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true, true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
@@ -1802,7 +1830,7 @@ public final class BackgroundRestrictionTest {
// playback. // playback.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK, bgMediaPlaybackMinDuration * 2, false,
null, null, null, listener, stats, uids, false, null, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 100, 0}, new double[]{restrictBucketHighThresholdMah + 100, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false, true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
@@ -1817,7 +1845,7 @@ public final class BackgroundRestrictionTest {
// Run with coarse location permission, with high current drain. // Run with coarse location permission, with high current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
ACCESS_COARSE_LOCATION, null, null, listener, stats, uids, ACCESS_COARSE_LOCATION, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0}, new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1831,7 +1859,7 @@ public final class BackgroundRestrictionTest {
// Run with fine location permission, with high current drain. // Run with fine location permission, with high current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false, FOREGROUND_SERVICE_TYPE_NONE, 0, false, false,
ACCESS_FINE_LOCATION, null, null, listener, stats, uids, ACCESS_FINE_LOCATION, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0}, new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1845,8 +1873,8 @@ public final class BackgroundRestrictionTest {
// Run with a media session with extended time, with higher current drain. // Run with a media session with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, null, FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
List.of(Pair.create(createMediaControllers(new String[] {testPkgName1}, null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)), new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
null, listener, stats, uids, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0}, new double[]{restrictBucketHighThresholdMah - 1, 0},
@@ -1858,8 +1886,8 @@ public final class BackgroundRestrictionTest {
// it still should stay in the current restriction level as we exempt the media // it still should stay in the current restriction level as we exempt the media
// session. // session.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, null, FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false, false,
List.of(Pair.create(createMediaControllers(new String[] {testPkgName1}, null, List.of(Pair.create(createMediaControllers(new String[] {testPkgName1},
new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)), new int[] {testUid1}), bgMediaPlaybackMinDuration * 2)),
null, listener, stats, uids, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 100, 0}, new double[]{restrictBucketHighThresholdMah + 100, 0},
@@ -1867,9 +1895,51 @@ public final class BackgroundRestrictionTest {
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true, true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
null, windowMs, initialBg, initialFgs, initialFg, initialCached); null, windowMs, initialBg, initialFgs, initialFg, initialCached);
// Set the policy to exempt fgs with notifications.
bgBatteryExemptionTypes.set(STATE_TYPE_FGS_WITH_NOTIFICATION);
// Start over.
resetBgRestrictionController();
setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
mAppBatteryPolicy.reset();
// Run with a FGS with notification posted/removed immediately, we should
// goto the restricted bucket.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, true, true,
null, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros,
false, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
null, windowMs, null, null, null);
// Run with a service with notification for extended time. We should be back to normal.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, bgMediaPlaybackMinDuration * 2, false,
true, null, null, null, listener, stats, uids,
new double[]{restrictBucketThresholdMah + 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros,
true, RESTRICTION_LEVEL_ADAPTIVE_BUCKET, timeout, false,
() -> {
// A user interaction will bring it back to normal.
mIdleStateListener.onUserInteractionStarted(testPkgName1,
UserHandle.getUserId(testUid1));
waitForIdleHandler(mBgRestrictionController.getBackgroundHandler());
// It should have been back to normal.
listener.verify(timeout, testUid1, testPkgName1,
RESTRICTION_LEVEL_ADAPTIVE_BUCKET);
verify(mInjector.getAppStandbyInternal(), times(1)).maybeUnrestrictApp(
eq(testPkgName1),
eq(UserHandle.getUserId(testUid1)),
eq(REASON_MAIN_FORCED_BY_SYSTEM),
eq(REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE),
eq(REASON_MAIN_USAGE),
eq(REASON_SUB_USAGE_USER_INTERACTION));
}, windowMs, null, null, null);
// Set the policy to exempt all. // Set the policy to exempt all.
bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK
| STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION); | STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION
| STATE_TYPE_FGS_WITH_NOTIFICATION);
// Start over. // Start over.
resetBgRestrictionController(); resetBgRestrictionController();
@@ -1878,7 +1948,7 @@ public final class BackgroundRestrictionTest {
// Run with a location service with extended time, with higher current drain. // Run with a location service with extended time, with higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
null, null, null, listener, stats, uids, null, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah - 1, 0}, new double[]{restrictBucketHighThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1888,7 +1958,7 @@ public final class BackgroundRestrictionTest {
// Run with a location service with extended time, with even higher current drain. // Run with a location service with extended time, with even higher current drain.
// it still should stay in the current restriction level as we exempt the location. // it still should stay in the current restriction level as we exempt the location.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1, runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, FOREGROUND_SERVICE_TYPE_LOCATION, bgMediaPlaybackMinDuration * 2, false, false,
null, null, null, listener, stats, uids, null, null, null, listener, stats, uids,
new double[]{restrictBucketHighThresholdMah + 100, 0}, new double[]{restrictBucketHighThresholdMah + 100, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, zeros,
@@ -1913,8 +1983,8 @@ public final class BackgroundRestrictionTest {
} }
private void runTestBgCurrentDrainExemptionOnce(String packageName, int uid, int pid, private void runTestBgCurrentDrainExemptionOnce(String packageName, int uid, int pid,
int serviceType, long sleepMs, boolean stopAfterSleep, String perm, int serviceType, long sleepMs, boolean stopAfterSleep, boolean withNotification,
List<Pair<List<MediaController>, Long>> mediaControllers, String perm, List<Pair<List<MediaController>, Long>> mediaControllers,
List<Long> topStateChanges, TestAppRestrictionLevelListener listener, List<Long> topStateChanges, TestAppRestrictionLevelListener listener,
BatteryUsageStats stats, int[] uids, double[] bg, double[] fgs, double[] fg, BatteryUsageStats stats, int[] uids, double[] bg, double[] fgs, double[] fg,
double[] cached, boolean expectingTimeout, int expectingLevel, long timeout, double[] cached, boolean expectingTimeout, int expectingLevel, long timeout,
@@ -1941,7 +2011,7 @@ public final class BackgroundRestrictionTest {
} }
waitForIdleHandler(mBgRestrictionController.getBackgroundHandler()); waitForIdleHandler(mBgRestrictionController.getBackgroundHandler());
runExemptionTestOnce( runExemptionTestOnce(
packageName, uid, pid, serviceType, sleepMs, stopAfterSleep, packageName, uid, pid, serviceType, sleepMs, stopAfterSleep, withNotification,
perm, mediaControllers, topStateChanges, resetFGSTracker, false, perm, mediaControllers, topStateChanges, resetFGSTracker, false,
() -> { () -> {
clearInvocations(mInjector.getAppStandbyInternal()); clearInvocations(mInjector.getAppStandbyInternal());