diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b2c016f0dcbc9..b7e349cc4813f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5764,4 +5764,15 @@
show the prompt to user, False - we'll not show it.
-->
false
+
+
+ 9
+
+
+ false
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 538a5db46df6a..8cac7bf556ae4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4744,4 +4744,6 @@
+
+
diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
index 3c780aaf25586..d1cf0049d1468 100644
--- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
@@ -20,7 +20,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.AppBatteryTracker.BATTERY_USAGE_NONE;
import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
-import static com.android.server.am.BaseAppStateDurationsTracker.EVENT_NUM;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_NUM;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,9 +36,9 @@ import com.android.server.am.AppBatteryExemptionTracker.UidBatteryStates;
import com.android.server.am.AppBatteryTracker.AppBatteryPolicy;
import com.android.server.am.AppBatteryTracker.BatteryUsage;
import com.android.server.am.AppBatteryTracker.ImmutableBatteryUsage;
-import com.android.server.am.BaseAppStateDurationsTracker.EventListener;
import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
import com.android.server.am.BaseAppStateTracker.Injector;
+import com.android.server.am.BaseAppStateTracker.StateListener;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
@@ -57,13 +57,13 @@ import java.util.LinkedList;
*/
final class AppBatteryExemptionTracker
extends BaseAppStateDurationsTracker
- implements BaseAppStateEvents.Factory, EventListener {
+ implements BaseAppStateEvents.Factory, StateListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "AppBatteryExemptionTracker" : TAG_AM;
private static final boolean DEBUG_BACKGROUND_BATTERY_EXEMPTION_TRACKER = false;
// As it's a UID-based tracker, anywhere which requires a package name, use this default name.
- private static final String DEFAULT_NAME = "";
+ static final String DEFAULT_NAME = "";
AppBatteryExemptionTracker(Context context, AppRestrictionController controller) {
this(context, controller, null, null);
@@ -80,9 +80,7 @@ final class AppBatteryExemptionTracker
void onSystemReady() {
super.onSystemReady();
mAppRestrictionController.forEachTracker(tracker -> {
- if (tracker instanceof BaseAppStateDurationsTracker) {
- ((BaseAppStateDurationsTracker) tracker).registerEventListener(this);
- }
+ tracker.registerStateListener(this);
});
}
@@ -97,19 +95,20 @@ final class AppBatteryExemptionTracker
}
@Override
- public void onNewEvent(int uid, String packageName, boolean start, long now, int eventType) {
+ public void onStateChange(int uid, String packageName, boolean start, long now, int stateType) {
if (!mInjector.getPolicy().isEnabled()) {
return;
}
final ImmutableBatteryUsage batteryUsage = mAppRestrictionController
.getUidBatteryUsage(uid);
+ final int stateTypeIndex = stateTypeToIndex(stateType);
synchronized (mLock) {
UidBatteryStates pkg = mPkgEvents.get(uid, DEFAULT_NAME);
if (pkg == null) {
pkg = createAppStateEvents(uid, DEFAULT_NAME);
mPkgEvents.put(uid, DEFAULT_NAME, pkg);
}
- pkg.addEvent(start, now, batteryUsage, eventType);
+ pkg.addEvent(start, now, batteryUsage, stateTypeIndex);
}
}
@@ -125,7 +124,8 @@ final class AppBatteryExemptionTracker
* @return The to-be-exempted battery usage of the given UID in the given duration; it could
* be considered as "exempted" due to various use cases, i.e. media playback.
*/
- ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now) {
+ ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now,
+ int types) {
if (!mInjector.getPolicy().isEnabled()) {
return BATTERY_USAGE_NONE;
}
@@ -135,7 +135,7 @@ final class AppBatteryExemptionTracker
if (pkg == null) {
return BATTERY_USAGE_NONE;
}
- result = pkg.getBatteryUsageSince(since, now);
+ result = pkg.getBatteryUsageSince(since, now, types);
}
if (!result.second.isEmpty()) {
// We have an open event (just start, no stop), get the battery usage till now.
@@ -149,7 +149,7 @@ final class AppBatteryExemptionTracker
static final class UidBatteryStates extends BaseAppStateDurations {
UidBatteryStates(int uid, @NonNull String tag,
@NonNull MaxTrackingDurationConfig maxTrackingDurationConfig) {
- super(uid, DEFAULT_NAME, EVENT_NUM, tag, maxTrackingDurationConfig);
+ super(uid, DEFAULT_NAME, STATE_TYPE_NUM, tag, maxTrackingDurationConfig);
}
UidBatteryStates(@NonNull UidBatteryStates other) {
@@ -160,7 +160,7 @@ final class AppBatteryExemptionTracker
* @param start {@code true} if it's a start event.
* @param now The timestamp when this event occurred.
* @param batteryUsage The background current drain since the system boots.
- * @param eventType One of EVENT_TYPE_* defined in the class BaseAppStateDurationsTracker.
+ * @param eventType One of STATE_TYPE_INDEX_* defined in the class BaseAppStateTracker.
*/
void addEvent(boolean start, long now, ImmutableBatteryUsage batteryUsage, int eventType) {
if (start) {
@@ -184,17 +184,6 @@ final class AppBatteryExemptionTracker
return mEvents[eventType] != null ? mEvents[eventType].peekLast() : null;
}
- /**
- * @return The pair of bg battery usage of given duration; the first value in the pair
- * is the aggregated battery usage of all event pairs in this duration; while
- * the second value is the battery usage since the system boots, if there is
- * an open event(just start, no stop) at the end of the duration.
- */
- Pair getBatteryUsageSince(long since,
- long now, int eventType) {
- return getBatteryUsageSince(since, now, mEvents[eventType]);
- }
-
private Pair getBatteryUsageSince(long since,
long now, LinkedList events) {
if (events == null || events.size() == 0) {
@@ -217,13 +206,18 @@ final class AppBatteryExemptionTracker
}
/**
- * @return The aggregated battery usage amongst all the event types we're tracking.
+ * @return The pair of bg battery usage of given duration; the first value in the pair
+ * is the aggregated battery usage of selected events in this duration; while
+ * the second value is the battery usage since the system boots, if there is
+ * an open event(just start, no stop) at the end of the duration.
*/
Pair getBatteryUsageSince(long since,
- long now) {
+ long now, int types) {
LinkedList result = new LinkedList<>();
for (int i = 0; i < mEvents.length; i++) {
- result = add(result, mEvents[i]);
+ if ((types & stateIndexToType(i)) != 0) {
+ result = add(result, mEvents[i]);
+ }
}
return getBatteryUsageSince(since, now, result);
}
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 37ad5f9bb57a4..655e309820918 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -325,7 +325,8 @@ final class AppBatteryTracker extends BaseAppStateTracker
final int uid = uidConsumers.keyAt(i);
final ImmutableBatteryUsage actualUsage = uidConsumers.valueAt(i);
final ImmutableBatteryUsage exemptedUsage = mAppRestrictionController
- .getUidBatteryExemptedUsageSince(uid, since, now);
+ .getUidBatteryExemptedUsageSince(uid, since, now,
+ bgPolicy.mBgCurrentDrainExemptedTypes);
// It's possible the exemptedUsage could be larger than actualUsage,
// as the former one is an approximate value.
final BatteryUsage bgUsage = actualUsage.mutate()
@@ -656,7 +657,8 @@ final class AppBatteryTracker extends BaseAppStateTracker
final BatteryUsage bgUsage = uidConsumers.valueAt(i)
.calcPercentage(uid, bgPolicy);
final BatteryUsage exemptedUsage = mAppRestrictionController
- .getUidBatteryExemptedUsageSince(uid, since, now)
+ .getUidBatteryExemptedUsageSince(uid, since, now,
+ bgPolicy.mBgCurrentDrainExemptedTypes)
.calcPercentage(uid, bgPolicy);
final BatteryUsage reportedUsage = new BatteryUsage(bgUsage)
.subtract(exemptedUsage)
@@ -1026,6 +1028,21 @@ final class AppBatteryTracker extends BaseAppStateTracker
static final String KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS =
DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_power_components";
+ /**
+ * The types of state where we'll exempt its battery usage when it's in that state.
+ * The state here must be one or a combination of STATE_TYPE_* in BaseAppStateTracker.
+ */
+ static final String KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_exempted_types";
+
+ /**
+ * 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 because of it.
+ */
+ static final String KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_high_threshold_by_bg_location";
+
/**
* Default value to the {@link #INDEX_REGULAR_CURRENT_DRAIN_THRESHOLD} of
* the {@link #mBgCurrentDrainRestrictedBucketThreshold}.
@@ -1088,6 +1105,16 @@ final class AppBatteryTracker extends BaseAppStateTracker
final int mDefaultBgCurrentDrainPowerComponent;
+ /**
+ * Default value to {@link #mBgCurrentDrainExmptedTypes}.
+ **/
+ final int mDefaultBgCurrentDrainExemptedTypes;
+
+ /**
+ * Default value to {@link #mBgCurrentDrainHighThresholdByBgLocation}.
+ */
+ final boolean mDefaultBgCurrentDrainHighThresholdByBgLocation;
+
/**
* The index to {@link #mBgCurrentDrainRestrictedBucketThreshold}
* and {@link #mBgCurrentDrainBgRestrictedThreshold}.
@@ -1145,6 +1172,16 @@ final class AppBatteryTracker extends BaseAppStateTracker
volatile Dimensions[] mBatteryDimensions;
+ /**
+ * @see #KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES.
+ */
+ volatile int mBgCurrentDrainExemptedTypes;
+
+ /**
+ * @see #KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION.
+ */
+ volatile boolean mBgCurrentDrainHighThresholdByBgLocation;
+
/**
* The capacity of the battery when fully charged in mAh.
*/
@@ -1201,6 +1238,10 @@ final class AppBatteryTracker extends BaseAppStateTracker
R.integer.config_bg_current_drain_types_to_bg_restricted);
mDefaultBgCurrentDrainPowerComponent = resources.getInteger(
R.integer.config_bg_current_drain_power_components);
+ mDefaultBgCurrentDrainExemptedTypes = resources.getInteger(
+ R.integer.config_bg_current_drain_exempted_types);
+ mDefaultBgCurrentDrainHighThresholdByBgLocation = resources.getBoolean(
+ R.bool.config_bg_current_drain_high_threshold_by_bg_location);
mBgCurrentDrainRestrictedBucketThreshold[0] =
mDefaultBgCurrentDrainRestrictedBucket;
mBgCurrentDrainRestrictedBucketThreshold[1] =
@@ -1230,6 +1271,7 @@ final class AppBatteryTracker extends BaseAppStateTracker
switch (name) {
case KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET:
case KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED:
+ case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION:
case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET:
case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED:
case KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET:
@@ -1249,6 +1291,9 @@ final class AppBatteryTracker extends BaseAppStateTracker
case KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED:
updateCurrentDrainEventDurationBasedThresholdEnabled();
break;
+ case KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES:
+ updateCurrentDrainExemptedTypes();
+ break;
default:
super.onPropertiesChanged(name);
break;
@@ -1305,6 +1350,10 @@ final class AppBatteryTracker extends BaseAppStateTracker
mBatteryDimensions[i] = new Dimensions(mBgCurrentDrainPowerComponents, i);
}
}
+ mBgCurrentDrainHighThresholdByBgLocation =
+ DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION,
+ mDefaultBgCurrentDrainHighThresholdByBgLocation);
}
private void updateCurrentDrainWindow() {
@@ -1335,6 +1384,13 @@ final class AppBatteryTracker extends BaseAppStateTracker
mDefaultBgCurrentDrainEventDurationBasedThresholdEnabled);
}
+ private void updateCurrentDrainExemptedTypes() {
+ mBgCurrentDrainExemptedTypes = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES,
+ mDefaultBgCurrentDrainExemptedTypes);
+ }
+
@Override
public void onSystemReady() {
mBatteryFullChargeMah =
@@ -1345,6 +1401,7 @@ final class AppBatteryTracker extends BaseAppStateTracker
updateCurrentDrainMediaPlaybackMinDuration();
updateCurrentDrainLocationMinDuration();
updateCurrentDrainEventDurationBasedThresholdEnabled();
+ updateCurrentDrainExemptedTypes();
}
@Override
@@ -1507,6 +1564,9 @@ final class AppBatteryTracker extends BaseAppStateTracker
}
private boolean hasLocation(int uid, long now, long window) {
+ if (!mBgCurrentDrainHighThresholdByBgLocation) {
+ return false;
+ }
final AppRestrictionController controller = mTracker.mAppRestrictionController;
if (mInjector.getPermissionManagerServiceInternal().checkUidPermission(
uid, ACCESS_BACKGROUND_LOCATION) == PERMISSION_GRANTED) {
@@ -1620,6 +1680,14 @@ final class AppBatteryTracker extends BaseAppStateTracker
pw.print(KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS);
pw.print('=');
pw.println(mBgCurrentDrainPowerComponents);
+ pw.print(prefix);
+ pw.print(KEY_BG_CURRENT_DRAIN_EXEMPTED_TYPES);
+ pw.print('=');
+ pw.println(BaseAppStateTracker.stateTypesToString(mBgCurrentDrainExemptedTypes));
+ pw.print(prefix);
+ pw.print(KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION);
+ pw.print('=');
+ pw.println(mBgCurrentDrainHighThresholdByBgLocation);
pw.print(prefix);
pw.println("Excessive current drain detected:");
diff --git a/services/core/java/com/android/server/am/AppFGSTracker.java b/services/core/java/com/android/server/am/AppFGSTracker.java
index de554fe3f1149..b92b856828dd3 100644
--- a/services/core/java/com/android/server/am/AppFGSTracker.java
+++ b/services/core/java/com/android/server/am/AppFGSTracker.java
@@ -543,7 +543,7 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker
+ implements OnPermissionsChangedListener {
+ static final String TAG = TAG_WITH_CLASS_NAME ? "AppPermissionTracker" : TAG_AM;
+
+ static final boolean DEBUG_PERMISSION_TRACKER = false;
+
+ private final MyHandler mHandler;
+
+ @GuardedBy("mLock")
+ private SparseArray> mUidGrantedPermissionsInMonitor = new SparseArray<>();
+
+ AppPermissionTracker(Context context, AppRestrictionController controller) {
+ this(context, controller, null, null);
+ }
+
+ AppPermissionTracker(Context context, AppRestrictionController controller,
+ Constructor extends Injector> injector, Object outerContext) {
+ super(context, controller, injector, outerContext);
+ mHandler = new MyHandler(this);
+ mInjector.setPolicy(new AppPermissionPolicy(mInjector, this));
+ }
+
+ @Override
+ public void onPermissionsChanged(int uid) {
+ mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget();
+ }
+
+ private void handlePermissionsInit() {
+ final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
+ final PackageManagerInternal pmi = mInjector.getPackageManagerInternal();
+ final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
+ final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+ for (int userId : allUsers) {
+ final List apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID);
+ if (apps == null) {
+ continue;
+ }
+ synchronized (mLock) {
+ final SparseArray> uidPerms = mUidGrantedPermissionsInMonitor;
+ final long now = SystemClock.elapsedRealtime();
+ for (int i = 0, size = apps.size(); i < size; i++) {
+ final ApplicationInfo ai = apps.get(i);
+ for (String permission : permissions) {
+ if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) {
+ continue;
+ }
+ ArraySet grantedPermissions = uidPerms.get(ai.uid);
+ if (grantedPermissions == null) {
+ grantedPermissions = new ArraySet();
+ uidPerms.put(ai.uid, grantedPermissions);
+ }
+ grantedPermissions.add(permission);
+ notifyListenersOnStateChange(ai.uid, DEFAULT_NAME, true, now,
+ STATE_TYPE_PERMISSION);
+ }
+ }
+ }
+ }
+ }
+
+ private void handlePermissionsDestroy() {
+ synchronized (mLock) {
+ final SparseArray> uidPerms = mUidGrantedPermissionsInMonitor;
+ final long now = SystemClock.elapsedRealtime();
+ for (int i = 0, size = uidPerms.size(); i < size; i++) {
+ final int uid = uidPerms.keyAt(i);
+ final ArraySet grantedPermissions = uidPerms.valueAt(i);
+ for (int j = 0, numOfPerms = grantedPermissions.size(); j < numOfPerms; j++) {
+ notifyListenersOnStateChange(uid, DEFAULT_NAME, false, now,
+ STATE_TYPE_PERMISSION);
+ }
+ }
+ uidPerms.clear();
+ }
+ }
+
+ private void handlePermissionsChanged(int uid) {
+ final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+ if (permissions != null && permissions.length > 0) {
+ synchronized (mLock) {
+ handlePermissionsChangedLocked(uid);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void handlePermissionsChangedLocked(int uid) {
+ final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal();
+ final int index = mUidGrantedPermissionsInMonitor.indexOfKey(uid);
+ ArraySet grantedPermissions = index >= 0
+ ? mUidGrantedPermissionsInMonitor.valueAt(index) : null;
+ final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+ final long now = SystemClock.elapsedRealtime();
+ for (String permission: permissions) {
+ boolean granted = pm.checkUidPermission(uid, permission) == PERMISSION_GRANTED;
+ if (DEBUG_PERMISSION_TRACKER) {
+ Slog.i(TAG, UserHandle.formatUid(uid) + " " + permission + "=" + granted);
+ }
+ boolean changed = false;
+ if (granted) {
+ if (grantedPermissions == null) {
+ grantedPermissions = new ArraySet<>();
+ mUidGrantedPermissionsInMonitor.put(uid, grantedPermissions);
+ }
+ changed = grantedPermissions.add(permission);
+ } else if (grantedPermissions != null) {
+ changed = grantedPermissions.remove(permission);
+ if (grantedPermissions.isEmpty()) {
+ mUidGrantedPermissionsInMonitor.removeAt(index);
+ }
+ }
+ if (changed) {
+ notifyListenersOnStateChange(uid, DEFAULT_NAME, granted, now,
+ STATE_TYPE_PERMISSION);
+ }
+ }
+ }
+
+ private static class MyHandler extends Handler {
+ static final int MSG_PERMISSIONS_INIT = 0;
+ static final int MSG_PERMISSIONS_DESTROY = 1;
+ static final int MSG_PERMISSIONS_CHANGED = 2;
+
+ private @NonNull AppPermissionTracker mTracker;
+
+ MyHandler(@NonNull AppPermissionTracker tracker) {
+ super(tracker.mBgHandler.getLooper());
+ mTracker = tracker;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_PERMISSIONS_INIT:
+ mTracker.handlePermissionsInit();
+ break;
+ case MSG_PERMISSIONS_DESTROY:
+ mTracker.handlePermissionsDestroy();
+ break;
+ case MSG_PERMISSIONS_CHANGED:
+ mTracker.handlePermissionsChanged(msg.arg1);
+ break;
+ }
+ }
+ }
+
+ private void onPermissionTrackerEnabled(boolean enabled) {
+ final PermissionManager pm = mInjector.getPermissionManager();
+ if (enabled) {
+ pm.addOnPermissionsChangeListener(this);
+ mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_INIT).sendToTarget();
+ } else {
+ pm.removeOnPermissionsChangeListener(this);
+ mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_DESTROY).sendToTarget();
+ }
+ }
+
+ @Override
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("APP PERMISSIONS TRACKER:");
+ final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor();
+ final String prefixMore = " " + prefix;
+ final String prefixMoreMore = " " + prefixMore;
+ for (String permission : permissions) {
+ pw.print(prefixMore);
+ pw.print(permission);
+ pw.println(':');
+ synchronized (mLock) {
+ final SparseArray> uidPerms = mUidGrantedPermissionsInMonitor;
+ pw.print(prefixMoreMore);
+ pw.print('[');
+ boolean needDelimiter = false;
+ for (int i = 0, size = uidPerms.size(); i < size; i++) {
+ if (uidPerms.valueAt(i).contains(permission)) {
+ if (needDelimiter) {
+ pw.print(',');
+ }
+ needDelimiter = true;
+ pw.print(UserHandle.formatUid(uidPerms.keyAt(i)));
+ }
+ }
+ pw.println(']');
+ }
+ }
+ super.dump(pw, prefix);
+ }
+
+ static final class AppPermissionPolicy extends BaseAppStatePolicy {
+ /**
+ * Whether or not we should enable the monitoring on app permissions.
+ */
+ static final String KEY_BG_PERMISSION_MONITOR_ENABLED =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "permission_monitor_enabled";
+
+ /**
+ * The names of the permissions we're monitoring its changes.
+ */
+ static final String KEY_BG_PERMISSIONS_IN_MONITOR =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "permission_in_monitor";
+
+ /**
+ * Default value to {@link #mTrackerEnabled}.
+ */
+ static final boolean DEFAULT_BG_PERMISSION_MONITOR_ENABLED = true;
+
+ /**
+ * Default value to {@link #mBgPermissionsInMonitor}.
+ */
+ static final String[] DEFAULT_BG_PERMISSIONS_IN_MONITOR = new String[] {
+ ACCESS_FINE_LOCATION,
+ };
+
+ /**
+ * @see #KEY_BG_PERMISSIONS_IN_MONITOR.
+ */
+ volatile String[] mBgPermissionsInMonitor = DEFAULT_BG_PERMISSIONS_IN_MONITOR;
+
+ AppPermissionPolicy(@NonNull Injector injector, @NonNull AppPermissionTracker tracker) {
+ super(injector, tracker, KEY_BG_PERMISSION_MONITOR_ENABLED,
+ DEFAULT_BG_PERMISSION_MONITOR_ENABLED);
+ }
+
+ @Override
+ public void onSystemReady() {
+ super.onSystemReady();
+ updateBgPermissionsInMonitor();
+ }
+
+ @Override
+ public void onPropertiesChanged(String name) {
+ switch (name) {
+ case KEY_BG_PERMISSIONS_IN_MONITOR:
+ updateBgPermissionsInMonitor();
+ break;
+ default:
+ super.onPropertiesChanged(name);
+ break;
+ }
+ }
+
+ String[] getBgPermissionsInMonitor() {
+ return mBgPermissionsInMonitor;
+ }
+
+ private void updateBgPermissionsInMonitor() {
+ final String config = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_PERMISSIONS_IN_MONITOR,
+ null);
+ mBgPermissionsInMonitor = config != null
+ ? config.split(",") : DEFAULT_BG_PERMISSIONS_IN_MONITOR;
+ }
+
+ @Override
+ public void onTrackerEnabled(boolean enabled) {
+ mTracker.onPermissionTrackerEnabled(enabled);
+ }
+
+ @Override
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.println("APP PERMISSION TRACKER POLICY SETTINGS:");
+ prefix = " " + prefix;
+ super.dump(pw, prefix);
+ pw.print(prefix);
+ pw.print(KEY_BG_PERMISSIONS_IN_MONITOR);
+ pw.print('=');
+ pw.println(Arrays.toString(mBgPermissionsInMonitor));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 6b571935a9b4d..376709c42ddec 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -1141,9 +1141,10 @@ public final class AppRestrictionController {
* @return The to-be-exempted battery usage of the given UID in the given duration; it could
* be considered as "exempted" due to various use cases, i.e. media playback.
*/
- ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now) {
+ ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now,
+ int types) {
return mInjector.getAppBatteryExemptionTracker()
- .getUidBatteryExemptedUsageSince(uid, since, now);
+ .getUidBatteryExemptedUsageSince(uid, since, now, types);
}
/**
@@ -1944,6 +1945,7 @@ public final class AppRestrictionController {
private AppBatteryExemptionTracker mAppBatteryExemptionTracker;
private AppFGSTracker mAppFGSTracker;
private AppMediaSessionTracker mAppMediaSessionTracker;
+ private AppPermissionTracker mAppPermissionTracker;
private TelephonyManager mTelephonyManager;
Injector(Context context) {
@@ -1960,10 +1962,12 @@ public final class AppRestrictionController {
mAppBatteryExemptionTracker = new AppBatteryExemptionTracker(mContext, controller);
mAppFGSTracker = new AppFGSTracker(mContext, controller);
mAppMediaSessionTracker = new AppMediaSessionTracker(mContext, controller);
+ mAppPermissionTracker = new AppPermissionTracker(mContext, controller);
controller.mAppStateTrackers.add(mAppBatteryTracker);
controller.mAppStateTrackers.add(mAppBatteryExemptionTracker);
controller.mAppStateTrackers.add(mAppFGSTracker);
controller.mAppStateTrackers.add(mAppMediaSessionTracker);
+ controller.mAppStateTrackers.add(mAppPermissionTracker);
controller.mAppStateTrackers.add(new AppBroadcastEventsTracker(mContext, controller));
controller.mAppStateTrackers.add(new AppBindServiceEventsTracker(mContext, controller));
}
@@ -2071,6 +2075,10 @@ public final class AppRestrictionController {
return mAppBatteryExemptionTracker;
}
+ AppPermissionTracker getAppPermissionTracker() {
+ return mAppPermissionTracker;
+ }
+
String getPackageName(int pid) {
final ActivityManagerService am = getActivityManagerService();
final ProcessRecord app;
diff --git a/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java b/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java
index cc89e847023dc..80a219d446682 100644
--- a/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateDurationsTracker.java
@@ -19,7 +19,6 @@ package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import android.annotation.NonNull;
import android.content.Context;
import android.os.SystemClock;
import android.util.SparseArray;
@@ -32,7 +31,6 @@ import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
-import java.util.ArrayList;
import java.util.LinkedList;
/**
@@ -43,20 +41,9 @@ abstract class BaseAppStateDurationsTracker
extends BaseAppStateEventsTracker {
static final boolean DEBUG_BASE_APP_STATE_DURATION_TRACKER = false;
- static final int EVENT_TYPE_MEDIA_SESSION = 0;
- static final int EVENT_TYPE_FGS_MEDIA_PLAYBACK = 1;
- static final int EVENT_TYPE_FGS_LOCATION = 2;
- static final int EVENT_NUM = 3;
-
- final ArrayList mEventListeners = new ArrayList<>();
-
@GuardedBy("mLock")
final SparseArray mUidStateDurations = new SparseArray<>();
- interface EventListener {
- void onNewEvent(int uid, String packageName, boolean start, long now, int eventType);
- }
-
BaseAppStateDurationsTracker(Context context, AppRestrictionController controller,
Constructor extends Injector> injector, Object outerContext) {
super(context, controller, injector, outerContext);
@@ -104,21 +91,6 @@ abstract class BaseAppStateDurationsTracker
mUidStateDurations.remove(uid);
}
- void registerEventListener(@NonNull EventListener listener) {
- synchronized (mLock) {
- mEventListeners.add(listener);
- }
- }
-
- void notifyListenersOnEvent(int uid, String packageName,
- boolean start, long now, int eventType) {
- synchronized (mLock) {
- for (int i = 0, size = mEventListeners.size(); i < size; i++) {
- mEventListeners.get(i).onNewEvent(uid, packageName, start, now, eventType);
- }
- }
- }
-
long getTotalDurations(String packageName, int uid, long now, int index, boolean bgOnly) {
synchronized (mLock) {
final U durations = mPkgEvents.get(uid, packageName);
diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java
index 60d9167586a1f..482d69751d700 100644
--- a/services/core/java/com/android/server/am/BaseAppStateTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java
@@ -28,10 +28,12 @@ import android.app.AppOpsManager;
import android.app.role.RoleManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.media.session.MediaSessionManager;
import android.os.BatteryManagerInternal;
import android.os.BatteryStatsInternal;
import android.os.Handler;
+import android.permission.PermissionManager;
import android.util.Slog;
import com.android.server.DeviceIdleInternal;
@@ -42,6 +44,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
+import java.util.ArrayList;
/**
* Base class to track certain state of the app, could be used to determine the restriction level.
@@ -55,11 +58,27 @@ public abstract class BaseAppStateTracker {
static final long ONE_HOUR = 60 * ONE_MINUTE;
static final long ONE_DAY = 24 * ONE_HOUR;
+ static final int STATE_TYPE_MEDIA_SESSION = 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_PERMISSION = 1 << 3;
+ static final int STATE_TYPE_NUM = 4;
+
+ 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_LOCATION = 2;
+ static final int STATE_TYPE_INDEX_PERMISSION = 3;
+
protected final AppRestrictionController mAppRestrictionController;
protected final Injector mInjector;
protected final Context mContext;
protected final Handler mBgHandler;
protected final Object mLock;
+ protected final ArrayList mStateListeners = new ArrayList<>();
+
+ interface StateListener {
+ void onStateChange(int uid, String packageName, boolean start, long now, int stateType);
+ }
BaseAppStateTracker(Context context, AppRestrictionController controller,
@Nullable Constructor extends Injector> injector, Object outerContext) {
@@ -80,6 +99,60 @@ public abstract class BaseAppStateTracker {
}
}
+ static int stateTypeToIndex(int stateType) {
+ return Integer.numberOfTrailingZeros(stateType);
+ }
+
+ static int stateIndexToType(int stateTypeIndex) {
+ return 1 << stateTypeIndex;
+ }
+
+ static String stateTypesToString(int stateTypes) {
+ final StringBuilder sb = new StringBuilder("[");
+ boolean needDelimiter = false;
+ for (int stateType = Integer.highestOneBit(stateTypes); stateType != 0;
+ stateType = Integer.highestOneBit(stateTypes)) {
+ if (needDelimiter) {
+ sb.append('|');
+ }
+ needDelimiter = true;
+ switch (stateType) {
+ case STATE_TYPE_MEDIA_SESSION:
+ sb.append("MEDIA_SESSION");
+ break;
+ case STATE_TYPE_FGS_MEDIA_PLAYBACK:
+ sb.append("FGS_MEDIA_PLAYBACK");
+ break;
+ case STATE_TYPE_FGS_LOCATION:
+ sb.append("FGS_LOCATION");
+ break;
+ case STATE_TYPE_PERMISSION:
+ sb.append("PERMISSION");
+ break;
+ default:
+ return "[UNKNOWN(" + Integer.toHexString(stateTypes) + ")]";
+ }
+ stateTypes &= ~stateType;
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ void registerStateListener(@NonNull StateListener listener) {
+ synchronized (mLock) {
+ mStateListeners.add(listener);
+ }
+ }
+
+ void notifyListenersOnStateChange(int uid, String packageName,
+ boolean start, long now, int stateType) {
+ synchronized (mLock) {
+ for (int i = 0, size = mStateListeners.size(); i < size; i++) {
+ mStateListeners.get(i).onStateChange(uid, packageName, start, now, stateType);
+ }
+ }
+ }
+
/**
* Return the policy holder of this tracker.
*/
@@ -180,6 +253,8 @@ public abstract class BaseAppStateTracker {
DeviceIdleInternal mDeviceIdleInternal;
UserManagerInternal mUserManagerInternal;
PackageManager mPackageManager;
+ PackageManagerInternal mPackageManagerInternal;
+ PermissionManager mPermissionManager;
PermissionManagerServiceInternal mPermissionManagerServiceInternal;
AppOpsManager mAppOpsManager;
MediaSessionManager mMediaSessionManager;
@@ -196,12 +271,14 @@ public abstract class BaseAppStateTracker {
mBatteryStatsInternal = LocalServices.getService(BatteryStatsInternal.class);
mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mPermissionManagerServiceInternal = LocalServices.getService(
PermissionManagerServiceInternal.class);
final Context context = mAppStatePolicy.mTracker.mContext;
mPackageManager = context.getPackageManager();
mAppOpsManager = context.getSystemService(AppOpsManager.class);
mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
+ mPermissionManager = context.getSystemService(PermissionManager.class);
mRoleManager = context.getSystemService(RoleManager.class);
mNotificationManagerInternal = LocalServices.getService(
NotificationManagerInternal.class);
@@ -244,6 +321,14 @@ public abstract class BaseAppStateTracker {
return mPackageManager;
}
+ PackageManagerInternal getPackageManagerInternal() {
+ return mPackageManagerInternal;
+ }
+
+ PermissionManager getPermissionManager() {
+ return mPermissionManager;
+ }
+
PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
return mPermissionManagerServiceInternal;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index e401d0ea09fd7..936940fcde6e8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -17,6 +17,8 @@
package com.android.server.am;
import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
@@ -52,7 +54,12 @@ import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND;
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE;
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATT_DIMENS;
+import static com.android.server.am.AppPermissionTracker.AppPermissionPolicy;
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_MEDIA_PLAYBACK;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_MEDIA_SESSION;
+import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -103,6 +110,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
+import android.permission.PermissionManager;
import android.provider.DeviceConfig;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -152,6 +160,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
/**
* Tests for {@link AppRestrictionController}.
@@ -217,6 +226,7 @@ public final class BackgroundRestrictionTest {
@Mock private PackageManagerInternal mPackageManagerInternal;
@Mock private NotificationManager mNotificationManager;
@Mock private NotificationManagerInternal mNotificationManagerInternal;
+ @Mock private PermissionManager mPermissionManager;
@Mock private PermissionManagerServiceInternal mPermissionManagerServiceInternal;
@Mock private MediaSessionManager mMediaSessionManager;
@Mock private RoleManager mRoleManager;
@@ -252,6 +262,7 @@ public final class BackgroundRestrictionTest {
private AppBindServiceEventsTracker mAppBindServiceEventsTracker;
private AppFGSTracker mAppFGSTracker;
private AppMediaSessionTracker mAppMediaSessionTracker;
+ private AppPermissionTracker mAppPermissionTracker;
@Before
public void setUp() throws Exception {
@@ -291,12 +302,16 @@ public final class BackgroundRestrictionTest {
doReturn(AppOpsManager.MODE_IGNORED)
.when(mAppOpsManager)
.checkOpNoThrow(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, uid, packageName);
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, ACCESS_BACKGROUND_LOCATION);
- doReturn(PERMISSION_DENIED)
- .when(mPermissionManagerServiceInternal)
- .checkPermission(packageName, ACCESS_BACKGROUND_LOCATION, userId);
+ final String[] permissions = new String[] {ACCESS_BACKGROUND_LOCATION,
+ ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION};
+ for (String permission : permissions) {
+ doReturn(PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkUidPermission(uid, permission);
+ doReturn(PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkPermission(packageName, permission, userId);
+ }
}
doReturn(appStandbyInfoList).when(mAppStandbyInternal).getAppStandbyBuckets(userId);
}
@@ -1278,7 +1293,8 @@ public final class BackgroundRestrictionTest {
.checkPermission(packageName, perm, UserHandle.getUserId(uid));
doReturn(PERMISSION_GRANTED)
.when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, ACCESS_BACKGROUND_LOCATION);
+ .checkUidPermission(uid, perm);
+ mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
}
if (mediaControllers != null) {
@@ -1303,7 +1319,8 @@ public final class BackgroundRestrictionTest {
.checkPermission(packageName, perm, UserHandle.getUserId(uid));
doReturn(PERMISSION_DENIED)
.when(mPermissionManagerServiceInternal)
- .checkUidPermission(uid, ACCESS_BACKGROUND_LOCATION);
+ .checkUidPermission(uid, perm);
+ mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
}
if (topStateThread != null) {
topStateThread.join();
@@ -1371,6 +1388,10 @@ public final class BackgroundRestrictionTest {
DeviceConfigSession bgLocationMinDurationThreshold = null;
DeviceConfigSession bgCurrentDrainEventDurationBasedThresholdEnabled = null;
DeviceConfigSession bgBatteryExemptionEnabled = null;
+ DeviceConfigSession bgBatteryExemptionTypes = null;
+ DeviceConfigSession bgPermissionMonitorEnabled = null;
+ DeviceConfigSession bgPermissionsInMonitor = null;
+ DeviceConfigSession bgCurrentDrainHighThresholdByBgLocation = null;
mBgRestrictionController.addAppBackgroundRestrictionListener(listener);
@@ -1469,6 +1490,38 @@ public final class BackgroundRestrictionTest {
AppBatteryExemptionPolicy.DEFAULT_BG_BATTERY_EXEMPTION_ENABLED);
bgBatteryExemptionEnabled.set(false);
+ 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(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK
+ | STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION);
+
+ bgPermissionMonitorEnabled = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ AppPermissionPolicy.KEY_BG_PERMISSION_MONITOR_ENABLED,
+ DeviceConfig::getBoolean,
+ AppPermissionPolicy.DEFAULT_BG_PERMISSION_MONITOR_ENABLED);
+ bgPermissionMonitorEnabled.set(true);
+
+ bgPermissionsInMonitor = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ AppPermissionPolicy.KEY_BG_PERMISSION_MONITOR_ENABLED,
+ DeviceConfig::getString,
+ Arrays.stream(AppPermissionPolicy.DEFAULT_BG_PERMISSIONS_IN_MONITOR)
+ .collect(Collectors.joining(",")));
+ bgPermissionsInMonitor.set(ACCESS_FINE_LOCATION);
+
+ bgCurrentDrainHighThresholdByBgLocation = new DeviceConfigSession<>(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_BY_BG_LOCATION,
+ DeviceConfig::getBoolean,
+ mContext.getResources().getBoolean(
+ R.bool.config_bg_current_drain_high_threshold_by_bg_location));
+ bgCurrentDrainHighThresholdByBgLocation.set(true);
+
mCurrentTimeMillis = 10_000L;
doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
@@ -1618,15 +1671,44 @@ public final class BackgroundRestrictionTest {
setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
mAppBatteryPolicy.reset();
- // Run with bg location permission, with higher current drain.
+ // Turn off the higher threshold for bg location access.
+ bgCurrentDrainHighThresholdByBgLocation.set(false);
+
+ // Run with bg location permission, with moderate current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false,
ACCESS_BACKGROUND_LOCATION, null, null, listener, stats, uids,
- new double[]{restrictBucketHighThresholdMah - 1, 0},
+ new double[]{restrictBucketThresholdMah - 1, 0},
new double[]{0, restrictBucketThresholdMah - 1}, zeros,
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
null, windowMs, null, null, null);
+ // Run with bg location permission, with a bit higher current drain.
+ runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+ ACCESS_BACKGROUND_LOCATION, 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);
+
+ // Start over.
+ resetBgRestrictionController();
+ setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
+ mAppBatteryPolicy.reset();
+
+ // Turn on the higher threshold for bg location access.
+ bgCurrentDrainHighThresholdByBgLocation.set(true);
+
+ // Run with bg location permission, with higher current drain.
+ runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+ ACCESS_BACKGROUND_LOCATION , null, null, listener, stats, uids,
+ new double[]{restrictBucketHighThresholdMah - 1, 0},
+ new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+ true , RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
+ null, windowMs, null, null, null);
+
// Run with bg location permission, with even higher current drain.
runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
FOREGROUND_SERVICE_TYPE_NONE, 0, false,
@@ -1709,6 +1791,36 @@ public final class BackgroundRestrictionTest {
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, false,
null, windowMs, initialBg, initialFgs, initialFg);
+ // Set the policy to exempt media session and permission.
+ bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_PERMISSION);
+ // Start over.
+ resetBgRestrictionController();
+ setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
+ mAppBatteryPolicy.reset();
+
+ // Run with coarse location permission, with high current drain.
+ runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+ ACCESS_COARSE_LOCATION, 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, initialBg, initialFgs, initialFg);
+
+ // Start over.
+ resetBgRestrictionController();
+ setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
+ mAppBatteryPolicy.reset();
+
+ // Run with fine location permission, with high current drain.
+ runTestBgCurrentDrainExemptionOnce(testPkgName1, testUid1, testPid1,
+ FOREGROUND_SERVICE_TYPE_NONE, 0, false,
+ ACCESS_FINE_LOCATION, null, null, listener, stats, uids,
+ new double[]{restrictBucketThresholdMah + 1, 0},
+ new double[]{0, restrictBucketThresholdMah - 1}, zeros,
+ true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
+ null, windowMs, initialBg, initialFgs, initialFg);
+
// Start over.
resetBgRestrictionController();
setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
@@ -1738,6 +1850,10 @@ public final class BackgroundRestrictionTest {
true, RESTRICTION_LEVEL_RESTRICTED_BUCKET, timeout, true,
null, windowMs, initialBg, initialFgs, initialFg);
+ // Set the policy to exempt all.
+ bgBatteryExemptionTypes.set(STATE_TYPE_MEDIA_SESSION | STATE_TYPE_FGS_MEDIA_PLAYBACK
+ | STATE_TYPE_FGS_LOCATION | STATE_TYPE_PERMISSION);
+
// Start over.
resetBgRestrictionController();
setUidBatteryConsumptions(stats, uids, zeros, zeros, zeros);
@@ -1772,6 +1888,10 @@ public final class BackgroundRestrictionTest {
closeIfNotNull(bgLocationMinDurationThreshold);
closeIfNotNull(bgCurrentDrainEventDurationBasedThresholdEnabled);
closeIfNotNull(bgBatteryExemptionEnabled);
+ closeIfNotNull(bgBatteryExemptionTypes);
+ closeIfNotNull(bgPermissionMonitorEnabled);
+ closeIfNotNull(bgPermissionsInMonitor);
+ closeIfNotNull(bgCurrentDrainHighThresholdByBgLocation);
}
}
@@ -1792,6 +1912,15 @@ public final class BackgroundRestrictionTest {
mAppBatteryExemptionTracker.reset();
mAppBatteryPolicy.reset();
}
+ if (perm != null) {
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManagerServiceInternal)
+ .checkPermission(packageName, perm, UserHandle.getUserId(uid));
+ doReturn(PERMISSION_GRANTED)
+ .when(mPermissionManagerServiceInternal)
+ .checkUidPermission(uid, perm);
+ mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
+ }
runExemptionTestOnce(
packageName, uid, pid, serviceType, sleepMs, stopAfterSleep,
perm, mediaControllers, topStateChanges, resetFGSTracker, false,
@@ -1841,6 +1970,15 @@ public final class BackgroundRestrictionTest {
);
}
);
+ if (perm != null) {
+ doReturn(PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkPermission(packageName, perm, UserHandle.getUserId(uid));
+ doReturn(PERMISSION_DENIED)
+ .when(mPermissionManagerServiceInternal)
+ .checkUidPermission(uid, perm);
+ mInjector.getAppPermissionTracker().onPermissionsChanged(uid);
+ }
}
@Test
@@ -2406,6 +2544,11 @@ public final class BackgroundRestrictionTest {
BackgroundRestrictionTest.class),
BackgroundRestrictionTest.this);
controller.addAppStateTracker(mAppBindServiceEventsTracker);
+ mAppPermissionTracker = new AppPermissionTracker(mContext, controller,
+ TestAppPermissionTrackerInjector.class.getDeclaredConstructor(
+ BackgroundRestrictionTest.class),
+ BackgroundRestrictionTest.this);
+ controller.addAppStateTracker(mAppPermissionTracker);
} catch (NoSuchMethodException e) {
// Won't happen.
}
@@ -2500,6 +2643,11 @@ public final class BackgroundRestrictionTest {
AppBatteryExemptionTracker getAppBatteryExemptionTracker() {
return mAppBatteryExemptionTracker;
}
+
+ @Override
+ AppPermissionTracker getAppPermissionTracker() {
+ return mAppPermissionTracker;
+ }
}
private class TestBaseTrackerInjector
@@ -2564,6 +2712,14 @@ public final class BackgroundRestrictionTest {
return BackgroundRestrictionTest.this.mNotificationManagerInternal;
}
+ PackageManagerInternal getPackageManagerInternal() {
+ return BackgroundRestrictionTest.this.mPackageManagerInternal;
+ }
+
+ PermissionManager getPermissionManager() {
+ return BackgroundRestrictionTest.this.mPermissionManager;
+ }
+
@Override
long getServiceStartForegroundTimeout() {
return 1_000; // ms
@@ -2594,6 +2750,10 @@ public final class BackgroundRestrictionTest {
extends TestBaseTrackerInjector {
}
+ private class TestAppPermissionTrackerInjector
+ extends TestBaseTrackerInjector {
+ }
+
private class TestAppBroadcastEventsTrackerInjector
extends TestBaseTrackerInjector {
@Override