Exclude the bg battery usage for apps with access to fine location

If an app has the permission ACCESS_FINE_LOCATION granted, its
background battery usage during that wil be excluded from its overall
background battery usage in AppBatteryTracker.

Also turning OFF the exemption for apps with ACCESS_BACKGROUND_LOCATION
only, as this doesn't mean it has access to locations.

Bug: 200326767
Bug: 203105544
Test: atest FrameworksMockingServicesTests:BackgroundRestrictionTest
Change-Id: Ibbd6dbb555c34c1839be6d21c62de00012832de6
This commit is contained in:
Jing Ji
2022-02-07 20:19:11 -08:00
parent 8ddcda8bec
commit 4838c7cac9
11 changed files with 711 additions and 84 deletions

View File

@@ -5764,4 +5764,15 @@
show the prompt to user, False - we'll not show it.
-->
<bool name="config_bg_prompt_fgs_with_noti_to_bg_restricted">false</bool>
<!-- 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.
-->
<integer name="config_bg_current_drain_exempted_types">9</integer>
<!-- 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.
-->
<bool name="config_bg_current_drain_high_threshold_by_bg_location">false</bool>
</resources>

View File

@@ -4744,4 +4744,6 @@
<java-symbol type="integer" name="config_bg_current_drain_media_playback_min_duration" />
<java-symbol type="integer" name="config_bg_current_drain_location_min_duration" />
<java-symbol type="bool" name="config_bg_prompt_fgs_with_noti_to_bg_restricted" />
<java-symbol type="integer" name="config_bg_current_drain_exempted_types" />
<java-symbol type="bool" name="config_bg_current_drain_high_threshold_by_bg_location" />
</resources>

View File

@@ -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<AppBatteryExemptionPolicy, UidBatteryStates>
implements BaseAppStateEvents.Factory<UidBatteryStates>, EventListener {
implements BaseAppStateEvents.Factory<UidBatteryStates>, 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<UidStateEventWithBattery> {
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<ImmutableBatteryUsage, ImmutableBatteryUsage> getBatteryUsageSince(long since,
long now, int eventType) {
return getBatteryUsageSince(since, now, mEvents[eventType]);
}
private Pair<ImmutableBatteryUsage, ImmutableBatteryUsage> getBatteryUsageSince(long since,
long now, LinkedList<UidStateEventWithBattery> 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<ImmutableBatteryUsage, ImmutableBatteryUsage> getBatteryUsageSince(long since,
long now) {
long now, int types) {
LinkedList<UidStateEventWithBattery> 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);
}

View File

@@ -325,7 +325,8 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
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<AppBatteryPolicy>
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<AppBatteryPolicy>
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<AppBatteryPolicy>
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<AppBatteryPolicy>
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<AppBatteryPolicy>
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<AppBatteryPolicy>
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<AppBatteryPolicy>
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<AppBatteryPolicy>
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<AppBatteryPolicy>
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<AppBatteryPolicy>
updateCurrentDrainMediaPlaybackMinDuration();
updateCurrentDrainLocationMinDuration();
updateCurrentDrainEventDurationBasedThresholdEnabled();
updateCurrentDrainExemptedTypes();
}
@Override
@@ -1507,6 +1564,9 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
}
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<AppBatteryPolicy>
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:");

View File

@@ -543,7 +543,7 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
}
if (isActive(i)) {
mEvents[i].add(new BaseTimeEvent(now));
notifyListenersOnEventIfNecessary(false, now,
notifyListenersOnStateChangeIfNecessary(false, now,
indexToForegroundServiceType(i));
}
}
@@ -569,13 +569,13 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
}
if (!isActive(i)) {
mEvents[i].add(new BaseTimeEvent(now));
notifyListenersOnEventIfNecessary(true, now, serviceType);
notifyListenersOnStateChangeIfNecessary(true, now, serviceType);
}
} else {
// Stop this type.
if (mEvents[i] != null && isActive(i)) {
mEvents[i].add(new BaseTimeEvent(now));
notifyListenersOnEventIfNecessary(false, now, serviceType);
notifyListenersOnStateChangeIfNecessary(false, now, serviceType);
}
}
changes &= ~serviceType;
@@ -584,20 +584,20 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
mForegroundServiceTypes = serviceTypes;
}
private void notifyListenersOnEventIfNecessary(boolean start, long now,
private void notifyListenersOnStateChangeIfNecessary(boolean start, long now,
@ForegroundServiceType int serviceType) {
int eventType;
int stateType;
switch (serviceType) {
case FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK:
eventType = BaseAppStateDurationsTracker.EVENT_TYPE_FGS_MEDIA_PLAYBACK;
stateType = BaseAppStateDurationsTracker.STATE_TYPE_FGS_MEDIA_PLAYBACK;
break;
case FOREGROUND_SERVICE_TYPE_LOCATION:
eventType = BaseAppStateDurationsTracker.EVENT_TYPE_FGS_LOCATION;
stateType = BaseAppStateDurationsTracker.STATE_TYPE_FGS_LOCATION;
break;
default:
return;
}
mTracker.notifyListenersOnEvent(mUid, mPackageName, start, now, eventType);
mTracker.notifyListenersOnStateChange(mUid, mPackageName, start, now, stateType);
}
void setIsLongRunning(boolean isLongRunning) {

View File

@@ -19,8 +19,8 @@ package com.android.server.am;
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.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
import static com.android.server.am.BaseAppStateDurationsTracker.EVENT_TYPE_MEDIA_SESSION;
import static com.android.server.am.BaseAppStateTracker.ONE_DAY;
import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_MEDIA_SESSION;
import android.annotation.NonNull;
import android.content.Context;
@@ -104,8 +104,8 @@ final class AppMediaSessionTracker
}
if (!pkg.isActive()) {
pkg.addEvent(true, now);
notifyListenersOnEvent(pkg.mUid, pkg.mPackageName, true, now,
EVENT_TYPE_MEDIA_SESSION);
notifyListenersOnStateChange(pkg.mUid, pkg.mPackageName, true, now,
STATE_TYPE_MEDIA_SESSION);
}
// Mark it as active, so we could filter out inactive ones below.
mTmpMediaControllers.put(packageName, uid, Boolean.TRUE);
@@ -127,8 +127,8 @@ final class AppMediaSessionTracker
&& mTmpMediaControllers.get(pkg.mPackageName, pkg.mUid) == null) {
// This package has removed its controller, issue a stop event.
pkg.addEvent(false, now);
notifyListenersOnEvent(pkg.mUid, pkg.mPackageName, false, now,
EVENT_TYPE_MEDIA_SESSION);
notifyListenersOnStateChange(pkg.mUid, pkg.mPackageName, false, now,
STATE_TYPE_MEDIA_SESSION);
}
}
}
@@ -146,8 +146,8 @@ final class AppMediaSessionTracker
final SimplePackageDurations pkg = val.valueAt(j);
if (pkg.isActive()) {
pkg.addEvent(false, now);
notifyListenersOnEvent(pkg.mUid, pkg.mPackageName, false, now,
EVENT_TYPE_MEDIA_SESSION);
notifyListenersOnStateChange(pkg.mUid, pkg.mPackageName, false, now,
STATE_TYPE_MEDIA_SESSION);
}
}
}

View File

@@ -0,0 +1,327 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.am;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID;
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.AppBatteryExemptionTracker.DEFAULT_NAME;
import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION;
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.OnPermissionsChangedListener;
import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.server.am.AppPermissionTracker.AppPermissionPolicy;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
/**
* The tracker for monitoring selected permission state of apps.
*/
final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy>
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<ArraySet<String>> mUidGrantedPermissionsInMonitor = new SparseArray<>();
AppPermissionTracker(Context context, AppRestrictionController controller) {
this(context, controller, null, null);
}
AppPermissionTracker(Context context, AppRestrictionController controller,
Constructor<? extends Injector<AppPermissionPolicy>> 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<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID);
if (apps == null) {
continue;
}
synchronized (mLock) {
final SparseArray<ArraySet<String>> 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<String> grantedPermissions = uidPerms.get(ai.uid);
if (grantedPermissions == null) {
grantedPermissions = new ArraySet<String>();
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<ArraySet<String>> 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<String> 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<String> 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<ArraySet<String>> 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<AppPermissionTracker> {
/**
* 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));
}
}
}

View File

@@ -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;

View File

@@ -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<T, U> {
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<EventListener> mEventListeners = new ArrayList<>();
@GuardedBy("mLock")
final SparseArray<UidStateDurations> 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<T>> 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);

View File

@@ -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<T extends BaseAppStatePolicy> {
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<T> mInjector;
protected final Context mContext;
protected final Handler mBgHandler;
protected final Object mLock;
protected final ArrayList<StateListener> 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<T>> injector, Object outerContext) {
@@ -80,6 +99,60 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> {
}
}
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<T extends BaseAppStatePolicy> {
DeviceIdleInternal mDeviceIdleInternal;
UserManagerInternal mUserManagerInternal;
PackageManager mPackageManager;
PackageManagerInternal mPackageManagerInternal;
PermissionManager mPermissionManager;
PermissionManagerServiceInternal mPermissionManagerServiceInternal;
AppOpsManager mAppOpsManager;
MediaSessionManager mMediaSessionManager;
@@ -196,12 +271,14 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> {
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<T extends BaseAppStatePolicy> {
return mPackageManager;
}
PackageManagerInternal getPackageManagerInternal() {
return mPackageManagerInternal;
}
PermissionManager getPermissionManager() {
return mPermissionManager;
}
PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
return mPermissionManagerServiceInternal;
}

View File

@@ -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<Long> bgLocationMinDurationThreshold = null;
DeviceConfigSession<Boolean> bgCurrentDrainEventDurationBasedThresholdEnabled = null;
DeviceConfigSession<Boolean> bgBatteryExemptionEnabled = null;
DeviceConfigSession<Integer> bgBatteryExemptionTypes = null;
DeviceConfigSession<Boolean> bgPermissionMonitorEnabled = null;
DeviceConfigSession<String> bgPermissionsInMonitor = null;
DeviceConfigSession<Boolean> 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<T extends BaseAppStatePolicy>
@@ -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<AppMediaSessionPolicy> {
}
private class TestAppPermissionTrackerInjector
extends TestBaseTrackerInjector<AppPermissionPolicy> {
}
private class TestAppBroadcastEventsTrackerInjector
extends TestBaseTrackerInjector<AppBroadcastEventsPolicy> {
@Override