Merge changes from topic "nlps-policy"

* changes:
  Implement package exemptions for Low Power Standby
  LowPowerStandby: Add APIs to get/set custom Policy
This commit is contained in:
Robert Horvath
2023-02-10 22:29:34 +00:00
committed by Android (Google) Code Review
11 changed files with 1337 additions and 83 deletions

View File

@@ -33284,9 +33284,12 @@ package android.os {
method public int getCurrentThermalStatus();
method public int getLocationPowerSaveMode();
method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
method public boolean isAllowedInLowPowerStandby(int);
method public boolean isAllowedInLowPowerStandby(@NonNull String);
method public boolean isBatteryDischargePredictionPersonalized();
method public boolean isDeviceIdleMode();
method public boolean isDeviceLightIdleMode();
method public boolean isExemptFromLowPowerStandby();
method public boolean isIgnoringBatteryOptimizations(String);
method public boolean isInteractive();
method public boolean isLowPowerStandbyEnabled();
@@ -33309,6 +33312,8 @@ package android.os {
field public static final int LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF = 1; // 0x1
field public static final int LOCATION_MODE_NO_CHANGE = 0; // 0x0
field public static final int LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF = 4; // 0x4
field public static final int LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION = 1; // 0x1
field public static final String LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN = "com.android.lowpowerstandby.WAKE_ON_LAN";
field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
field public static final int PARTIAL_WAKE_LOCK = 1; // 0x1
field public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32; // 0x20

View File

@@ -10572,6 +10572,7 @@ package android.os {
method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long);
method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend();
method @NonNull public android.os.BatterySaverPolicyConfig getFullPowerSavePolicy();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public android.os.PowerManager.LowPowerStandbyPolicy getLowPowerStandbyPolicy();
method public int getPowerSaveModeTrigger();
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplayAvailable();
method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressed();
@@ -10584,9 +10585,11 @@ package android.os {
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setFullPowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void setLowPowerStandbyActiveDuringMaintenance(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void setLowPowerStandbyEnabled(boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER}) public void setLowPowerStandbyPolicy(@Nullable android.os.PowerManager.LowPowerStandbyPolicy);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.USER_ACTIVITY}) public void userActivity(long, int, int);
field @RequiresPermission(android.Manifest.permission.MANAGE_LOW_POWER_STANDBY) public static final String ACTION_LOW_POWER_STANDBY_POLICY_CHANGED = "android.os.action.LOW_POWER_STANDBY_POLICY_CHANGED";
field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1
field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0
field public static final String REBOOT_USERSPACE = "userspace";
@@ -10601,6 +10604,14 @@ package android.os {
field public static final int USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1; // 0x1
}
public static final class PowerManager.LowPowerStandbyPolicy {
ctor public PowerManager.LowPowerStandbyPolicy(@NonNull String, @NonNull java.util.Set<java.lang.String>, int, @NonNull java.util.Set<java.lang.String>);
method @NonNull public java.util.Set<java.lang.String> getAllowedFeatures();
method public int getAllowedReasons();
method @NonNull public java.util.Set<java.lang.String> getExemptPackages();
method @NonNull public String getIdentifier();
}
@Deprecated public class PowerWhitelistManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>);

View File

@@ -70,11 +70,29 @@ interface IPowerManager
boolean isBatteryDischargePredictionPersonalized();
boolean isDeviceIdleMode();
boolean isLightDeviceIdleMode();
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER })")
boolean isLowPowerStandbySupported();
boolean isLowPowerStandbyEnabled();
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER })")
void setLowPowerStandbyEnabled(boolean enabled);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER })")
void setLowPowerStandbyActiveDuringMaintenance(boolean activeDuringMaintenance);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER })")
void forceLowPowerStandbyActive(boolean active);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER })")
void setLowPowerStandbyPolicy(in @nullable LowPowerStandbyPolicy policy);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf = { android.Manifest.permission.MANAGE_LOW_POWER_STANDBY, android.Manifest.permission.DEVICE_POWER })")
LowPowerStandbyPolicy getLowPowerStandbyPolicy();
boolean isExemptFromLowPowerStandby();
boolean isReasonAllowedInLowPowerStandby(int reason);
boolean isFeatureAllowedInLowPowerStandby(String feature);
parcelable LowPowerStandbyPolicy {
String identifier;
List<String> exemptPackages;
int allowedReasons;
List<String> allowedFeatures;
}
@UnsupportedAppUsage
void reboot(boolean confirm, String reason, boolean wait);

View File

@@ -35,6 +35,7 @@ import android.content.Context;
import android.service.dreams.Sandman;
import android.sysprop.InitProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -44,7 +45,10 @@ import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;
@@ -2380,6 +2384,93 @@ public final class PowerManager {
}
}
/**
* Sets the current Low Power Standby policy.
*
* When the policy changes {@link #ACTION_LOW_POWER_STANDBY_POLICY_CHANGED} is broadcast to
* registered receivers.
*
* @param policy The policy to set. If null, resets to the default policy.
* @see #getLowPowerStandbyPolicy
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
android.Manifest.permission.DEVICE_POWER
})
public void setLowPowerStandbyPolicy(@Nullable LowPowerStandbyPolicy policy) {
try {
mService.setLowPowerStandbyPolicy(LowPowerStandbyPolicy.toParcelable(policy));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get the current Low Power Standby policy.
*
* When the policy changes {@link #ACTION_LOW_POWER_STANDBY_POLICY_CHANGED} is broadcast to
* registered receivers.
*
* @see #setLowPowerStandbyPolicy
* @hide
*/
@SystemApi
@Nullable
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
android.Manifest.permission.DEVICE_POWER
})
public LowPowerStandbyPolicy getLowPowerStandbyPolicy() {
try {
return LowPowerStandbyPolicy.fromParcelable(mService.getLowPowerStandbyPolicy());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns true if the calling package is exempt from Low Power Standby restrictions or
* Low Power Standby is disabled (so Low Power Standby does not restrict apps),
* false otherwise.
*/
public boolean isExemptFromLowPowerStandby() {
try {
return mService.isExemptFromLowPowerStandby();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns true if Low Power Standby is disabled (so Low Power Standby does not restrict apps),
* or apps may be automatically exempt from Low Power Standby restrictions for the given reason.
*
* The system may exempt apps from Low Power Standby restrictions when using allowed features.
* For example, if {@link #LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION} is allowed,
* then apps with active voice interaction sessions are exempt from restrictions.
*/
public boolean isAllowedInLowPowerStandby(@LowPowerStandbyAllowedReason int reason) {
try {
return mService.isReasonAllowedInLowPowerStandby(reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns true if Low Power Standby is disabled (so Low Power Standby does not restrict apps),
* or apps are allowed to use a given feature during Low Power Standby.
*/
public boolean isAllowedInLowPowerStandby(@NonNull String feature) {
try {
return mService.isFeatureAllowedInLowPowerStandby(feature);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Return whether the given application package name is on the device's power allowlist.
* Apps can be placed on the allowlist through the settings UI invoked by
@@ -2876,6 +2967,176 @@ public final class PowerManager {
public static final String ACTION_LOW_POWER_STANDBY_ENABLED_CHANGED =
"android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED";
/**
* Intent that is broadcast when Low Power Standby is enabled or disabled.
* This broadcast is only sent to registered receivers.
*
* @see #getLowPowerStandbyPolicy
* @see #setLowPowerStandbyPolicy
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_LOW_POWER_STANDBY)
@SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LOW_POWER_STANDBY_POLICY_CHANGED =
"android.os.action.LOW_POWER_STANDBY_POLICY_CHANGED";
/**
* Signals that wake-on-lan/wake-on-wlan is allowed in Low Power Standby.
*
* @see #isAllowedInLowPowerStandby(String)
*/
public static final String LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN =
"com.android.lowpowerstandby.WAKE_ON_LAN";
/**
* @hide
*/
@IntDef(prefix = { "LOW_POWER_STANDBY_ALLOWED_REASON_" }, flag = true, value = {
LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface LowPowerStandbyAllowedReason {
}
/**
* Exempts active Voice Interaction Sessions in Low Power Standby.
*
* @see #isAllowedInLowPowerStandby(int)
*/
public static final int LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION = 1 << 0;
/** @hide */
public static String lowPowerStandbyAllowedReasonsToString(
@LowPowerStandbyAllowedReason int allowedReasons) {
ArrayList<String> allowedStrings = new ArrayList<>();
if ((allowedReasons & LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION) != 0) {
allowedStrings.add("ALLOWED_REASON_VOICE_INTERACTION");
allowedReasons &= ~LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION;
}
if (allowedReasons != 0) {
allowedStrings.add(String.valueOf(allowedReasons));
}
return String.join(",", allowedStrings);
}
/**
* Policy that defines the restrictions enforced by Low Power Standby.
*
* @hide
*/
@SystemApi
public static final class LowPowerStandbyPolicy {
/** Name of the policy, used for debugging & metrics */
@NonNull
private final String mIdentifier;
/** Packages that are exempt from Low Power Standby restrictions. */
@NonNull
private final Set<String> mExemptPackages;
/**
* Reasons that this policy allows apps to be automatically exempted
* from Low Power Standby restrictions for.
*/
@LowPowerStandbyAllowedReason
private final int mAllowedReasons;
/** Features that are allowed to be used in Low Power Standby. */
@NonNull
private final Set<String> mAllowedFeatures;
public LowPowerStandbyPolicy(@NonNull String identifier,
@NonNull Set<String> exemptPackages,
@LowPowerStandbyAllowedReason int allowedReasons,
@NonNull Set<String> allowedFeatures) {
Objects.requireNonNull(identifier);
Objects.requireNonNull(exemptPackages);
Objects.requireNonNull(allowedFeatures);
mIdentifier = identifier;
mExemptPackages = Collections.unmodifiableSet(exemptPackages);
mAllowedReasons = allowedReasons;
mAllowedFeatures = Collections.unmodifiableSet(allowedFeatures);
}
@NonNull
public String getIdentifier() {
return mIdentifier;
}
@NonNull
public Set<String> getExemptPackages() {
return mExemptPackages;
}
@LowPowerStandbyAllowedReason
public int getAllowedReasons() {
return mAllowedReasons;
}
@NonNull
public Set<String> getAllowedFeatures() {
return mAllowedFeatures;
}
@Override
public String toString() {
return "Policy{"
+ "mIdentifier='" + mIdentifier + '\''
+ ", mExemptPackages=" + String.join(",", mExemptPackages)
+ ", mAllowedReasons=" + lowPowerStandbyAllowedReasonsToString(mAllowedReasons)
+ ", mAllowedFeatures=" + String.join(",", mAllowedFeatures)
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof LowPowerStandbyPolicy)) return false;
LowPowerStandbyPolicy that = (LowPowerStandbyPolicy) o;
return mAllowedReasons == that.mAllowedReasons && Objects.equals(mIdentifier,
that.mIdentifier) && Objects.equals(mExemptPackages, that.mExemptPackages)
&& Objects.equals(mAllowedFeatures, that.mAllowedFeatures);
}
@Override
public int hashCode() {
return Objects.hash(mIdentifier, mExemptPackages, mAllowedReasons,
mAllowedFeatures);
}
/** @hide */
public static IPowerManager.LowPowerStandbyPolicy toParcelable(
LowPowerStandbyPolicy policy) {
if (policy == null) {
return null;
}
IPowerManager.LowPowerStandbyPolicy parcelablePolicy =
new IPowerManager.LowPowerStandbyPolicy();
parcelablePolicy.identifier = policy.mIdentifier;
parcelablePolicy.exemptPackages = new ArrayList<>(policy.mExemptPackages);
parcelablePolicy.allowedReasons = policy.mAllowedReasons;
parcelablePolicy.allowedFeatures = new ArrayList<>(policy.mAllowedFeatures);
return parcelablePolicy;
}
/** @hide */
public static LowPowerStandbyPolicy fromParcelable(
IPowerManager.LowPowerStandbyPolicy parcelablePolicy) {
if (parcelablePolicy == null) {
return null;
}
return new LowPowerStandbyPolicy(
parcelablePolicy.identifier,
new ArraySet<>(parcelablePolicy.exemptPackages),
parcelablePolicy.allowedReasons,
new ArraySet<>(parcelablePolicy.allowedFeatures));
}
}
/**
* Constant for PreIdleTimeout normal mode (default mode, not short nor extend timeout) .
* @hide

View File

@@ -469,5 +469,26 @@ message LowPowerStandbyControllerDumpProto {
// Set of app ids that are exempt form low power standby
repeated int32 allowlist = 10;
// Next tag: 11
// The active policy specifying exemptions
optional LowPowerStandbyPolicyProto policy = 11;
// Next tag: 12
}
message LowPowerStandbyPolicyProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
// Name of the policy
optional string identifier = 1;
// Packages that are exempt from Low Power Standby restrictions
repeated string exempt_packages = 2;
// Exemption reasons that this policy allows
optional int32 allowed_reasons = 3;
// Features that this policy allows to be used
repeated string allowed_features = 4;
// Next tag: 5
}

View File

@@ -108,6 +108,7 @@
<protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" />
<protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL" />
<protected-broadcast android:name="android.os.action.LOW_POWER_STANDBY_ENABLED_CHANGED" />
<protected-broadcast android:name="android.os.action.LOW_POWER_STANDBY_POLICY_CHANGED" />
<protected-broadcast android:name="android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED" />
<!-- @deprecated This is rarely used and will be phased out soon. -->

View File

@@ -16,36 +16,67 @@
package com.android.server.power;
import static android.os.PowerManager.lowPowerStandbyAllowedReasonsToString;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.LowPowerStandbyAllowedReason;
import android.os.PowerManager.LowPowerStandbyPolicy;
import android.os.PowerManagerInternal;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
import com.android.server.net.NetworkPolicyManagerInternal;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
/**
* Controls Low Power Standby state.
@@ -56,11 +87,13 @@ import java.util.Arrays;
* <ul>
* <li>Low Power Standby is enabled
* <li>The device is not interactive, and has been non-interactive for a given timeout
* <li>The device is not in a doze maintenance window
* <li>The device is not in a doze maintenance window (devices may be configured to also
* apply restrictions during doze maintenance windows, see {@link #setActiveDuringMaintenance})
* </ul>
*
* <p>When Low Power Standby is active, the following restrictions are applied to applications
* with procstate less important than {@link android.app.ActivityManager#PROCESS_STATE_BOUND_TOP}:
* with procstate less important than {@link android.app.ActivityManager#PROCESS_STATE_BOUND_TOP}
* unless they are exempted (see {@link LowPowerStandbyPolicy}):
* <ul>
* <li>Network access is blocked
* <li>Wakelocks are disabled
@@ -76,9 +109,19 @@ public class LowPowerStandbyController {
private static final int MSG_STANDBY_TIMEOUT = 0;
private static final int MSG_NOTIFY_ACTIVE_CHANGED = 1;
private static final int MSG_NOTIFY_ALLOWLIST_CHANGED = 2;
private static final int MSG_NOTIFY_POLICY_CHANGED = 3;
private static final String TAG_ROOT = "low-power-standby-policy";
private static final String TAG_IDENTIFIER = "identifier";
private static final String TAG_EXEMPT_PACKAGE = "exempt-package";
private static final String TAG_ALLOWED_REASONS = "allowed-reasons";
private static final String TAG_ALLOWED_FEATURES = "allowed-features";
private static final String ATTR_VALUE = "value";
private final Handler mHandler;
private final SettingsObserver mSettingsObserver;
private final DeviceConfigWrapper mDeviceConfig;
private final File mPolicyFile;
private final Object mLock = new Object();
private final Context mContext;
@@ -86,9 +129,12 @@ public class LowPowerStandbyController {
private final AlarmManager.OnAlarmListener mOnStandbyTimeoutExpired =
this::onStandbyTimeoutExpired;
private final LowPowerStandbyControllerInternal mLocalService = new LocalService();
private final SparseBooleanArray mAllowlistUids = new SparseBooleanArray();
private final SparseIntArray mUidAllowedReasons = new SparseIntArray();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@GuardedBy("mLock")
private boolean mEnableCustomPolicy;
private final BroadcastReceiver mIdleBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
@@ -105,6 +151,41 @@ public class LowPowerStandbyController {
}
};
private final BroadcastReceiver mPackageBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Slog.d(TAG, "Received package intent: action=" + intent.getAction() + ", data="
+ intent.getData());
}
final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
if (replacing) {
return;
}
final Uri intentUri = intent.getData();
final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart()
: null;
synchronized (mLock) {
final LowPowerStandbyPolicy policy = getPolicy();
if (policy.getExemptPackages().contains(packageName)) {
enqueueNotifyAllowlistChangedLocked();
}
}
}
};
private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Slog.d(TAG, "Received user intent: action=" + intent.getAction());
}
synchronized (mLock) {
enqueueNotifyAllowlistChangedLocked();
}
}
};
@GuardedBy("mLock")
private AlarmManager mAlarmManager;
@GuardedBy("mLock")
@@ -159,6 +240,18 @@ public class LowPowerStandbyController {
@GuardedBy("mLock")
private boolean mForceActive;
/** Current Low Power Standby policy. */
@GuardedBy("mLock")
@Nullable
private LowPowerStandbyPolicy mPolicy;
@VisibleForTesting
static final LowPowerStandbyPolicy DEFAULT_POLICY = new LowPowerStandbyPolicy(
"DEFAULT_POLICY",
Collections.emptySet(),
PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION,
Collections.emptySet());
/** Functional interface for providing time. */
@VisibleForTesting
interface Clock {
@@ -166,11 +259,21 @@ public class LowPowerStandbyController {
long elapsedRealtime();
}
public LowPowerStandbyController(Context context, Looper looper, Clock clock) {
public LowPowerStandbyController(Context context, Looper looper) {
this(context, looper, SystemClock::elapsedRealtime,
new DeviceConfigWrapper(),
new File(Environment.getDataSystemDirectory(), "low_power_standby_policy.xml"));
}
@VisibleForTesting
LowPowerStandbyController(Context context, Looper looper, Clock clock,
DeviceConfigWrapper deviceConfig, File policyFile) {
mContext = context;
mHandler = new LowPowerStandbyHandler(looper);
mClock = clock;
mSettingsObserver = new SettingsObserver(mHandler);
mDeviceConfig = deviceConfig;
mPolicyFile = policyFile;
}
/** Call when system services are ready */
@@ -201,6 +304,16 @@ public class LowPowerStandbyController {
mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
Settings.Global.LOW_POWER_STANDBY_ACTIVE_DURING_MAINTENANCE),
false, mSettingsObserver, UserHandle.USER_ALL);
mDeviceConfig.registerPropertyUpdateListener(mContext.getMainExecutor(),
properties -> onDeviceConfigFlagsChanged());
mEnableCustomPolicy = mDeviceConfig.enableCustomPolicy();
if (mEnableCustomPolicy) {
mPolicy = loadPolicy();
} else {
mPolicy = DEFAULT_POLICY;
}
initSettingsLocked();
updateSettingsLocked();
@@ -212,6 +325,17 @@ public class LowPowerStandbyController {
LocalServices.addService(LowPowerStandbyControllerInternal.class, mLocalService);
}
private void onDeviceConfigFlagsChanged() {
synchronized (mLock) {
boolean enableCustomPolicy = mDeviceConfig.enableCustomPolicy();
if (mEnableCustomPolicy != enableCustomPolicy) {
enqueueNotifyPolicyChangedLocked();
enqueueNotifyAllowlistChangedLocked();
mEnableCustomPolicy = enableCustomPolicy;
}
}
}
@GuardedBy("mLock")
private void initSettingsLocked() {
final ContentResolver resolver = mContext.getContentResolver();
@@ -242,6 +366,139 @@ public class LowPowerStandbyController {
updateActiveLocked();
}
@Nullable
private LowPowerStandbyPolicy loadPolicy() {
final AtomicFile file = getPolicyFile();
if (!file.exists()) {
return null;
}
if (DEBUG) {
Slog.d(TAG, "Loading policy from " + file.getBaseFile());
}
try (FileInputStream in = file.openRead()) {
String identifier = null;
Set<String> exemptPackages = new ArraySet<>();
int allowedReasons = 0;
Set<String> allowedFeatures = new ArraySet<>();
TypedXmlPullParser parser = Xml.resolvePullParser(in);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final int depth = parser.getDepth();
// Check the root tag
final String tag = parser.getName();
if (depth == 1) {
if (!TAG_ROOT.equals(tag)) {
Slog.e(TAG, "Invalid root tag: " + tag);
return null;
}
continue;
}
// Assume depth == 2
switch (tag) {
case TAG_IDENTIFIER:
identifier = parser.getAttributeValue(null, ATTR_VALUE);
break;
case TAG_EXEMPT_PACKAGE:
exemptPackages.add(parser.getAttributeValue(null, ATTR_VALUE));
break;
case TAG_ALLOWED_REASONS:
allowedReasons = parser.getAttributeInt(null, ATTR_VALUE);
break;
case TAG_ALLOWED_FEATURES:
allowedFeatures.add(parser.getAttributeValue(null, ATTR_VALUE));
break;
default:
Slog.e(TAG, "Invalid tag: " + tag);
break;
}
}
final LowPowerStandbyPolicy policy = new LowPowerStandbyPolicy(identifier,
exemptPackages, allowedReasons, allowedFeatures);
if (DEBUG) {
Slog.d(TAG, "Loaded policy: " + policy);
}
return policy;
} catch (FileNotFoundException e) {
// Use the default
return null;
} catch (IOException | NullPointerException | IllegalArgumentException
| XmlPullParserException e) {
Slog.e(TAG, "Failed to read policy file " + file.getBaseFile(), e);
return null;
}
}
static void writeTagValue(TypedXmlSerializer out, String tag, String value) throws IOException {
if (TextUtils.isEmpty(value)) return;
out.startTag(null, tag);
out.attribute(null, ATTR_VALUE, value);
out.endTag(null, tag);
}
static void writeTagValue(TypedXmlSerializer out, String tag, int value) throws IOException {
out.startTag(null, tag);
out.attributeInt(null, ATTR_VALUE, value);
out.endTag(null, tag);
}
private void savePolicy(@Nullable LowPowerStandbyPolicy policy) {
final AtomicFile file = getPolicyFile();
if (DEBUG) {
Slog.d(TAG, "Saving policy to " + file.getBaseFile());
}
if (policy == null) {
file.delete();
return;
}
FileOutputStream outs = null;
try {
file.getBaseFile().mkdirs();
outs = file.startWrite();
// Write to XML
TypedXmlSerializer out = Xml.resolveSerializer(outs);
out.startDocument(null, true);
out.startTag(null, TAG_ROOT);
// Body.
writeTagValue(out, TAG_IDENTIFIER, policy.getIdentifier());
for (String exemptPackage : policy.getExemptPackages()) {
writeTagValue(out, TAG_EXEMPT_PACKAGE, exemptPackage);
}
writeTagValue(out, TAG_ALLOWED_REASONS, policy.getAllowedReasons());
for (String allowedFeature : policy.getAllowedFeatures()) {
writeTagValue(out, TAG_ALLOWED_FEATURES, allowedFeature);
}
// Epilogue.
out.endTag(null, TAG_ROOT);
out.endDocument();
// Close.
file.finishWrite(outs);
} catch (IOException e) {
Slog.e(TAG, "Failed to write policy to file " + file.getBaseFile(), e);
file.failWrite(outs);
}
}
private void enqueueSavePolicy(@Nullable LowPowerStandbyPolicy policy) {
mHandler.post(() -> savePolicy(policy));
}
private AtomicFile getPolicyFile() {
return new AtomicFile(mPolicyFile);
}
@GuardedBy("mLock")
private void updateActiveLocked() {
final long now = mClock.elapsedRealtime();
@@ -378,11 +635,25 @@ public class LowPowerStandbyController {
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
mContext.registerReceiver(mIdleBroadcastReceiver, intentFilter);
IntentFilter packageFilter = new IntentFilter();
packageFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(mPackageBroadcastReceiver, packageFilter);
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_ADDED);
userFilter.addAction(Intent.ACTION_USER_REMOVED);
mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
}
private void unregisterBroadcastReceiver() {
mContext.unregisterReceiver(mBroadcastReceiver);
mContext.unregisterReceiver(mIdleBroadcastReceiver);
mContext.unregisterReceiver(mPackageBroadcastReceiver);
mContext.unregisterReceiver(mUserReceiver);
}
@GuardedBy("mLock")
@@ -396,6 +667,25 @@ public class LowPowerStandbyController {
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
@GuardedBy("mLock")
private void enqueueNotifyPolicyChangedLocked() {
final long now = mClock.elapsedRealtime();
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_POLICY_CHANGED, getPolicy());
mHandler.sendMessageAtTime(msg, now);
}
private void notifyPolicyChanged(LowPowerStandbyPolicy policy) {
if (DEBUG) {
Slog.d(TAG, "notifyPolicyChanged, policy=" + policy);
}
final Intent intent = new Intent(
PowerManager.ACTION_LOW_POWER_STANDBY_POLICY_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
Manifest.permission.MANAGE_LOW_POWER_STANDBY);
}
private void onStandbyTimeoutExpired() {
if (DEBUG) {
Slog.d(TAG, "onStandbyTimeoutExpired");
@@ -414,6 +704,9 @@ public class LowPowerStandbyController {
/** Notify other system components about the updated Low Power Standby active state */
private void notifyActiveChanged(boolean active) {
if (DEBUG) {
Slog.d(TAG, "notifyActiveChanged, active=" + active);
}
final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
final NetworkPolicyManagerInternal npmi = LocalServices.getService(
NetworkPolicyManagerInternal.class);
@@ -479,6 +772,104 @@ public class LowPowerStandbyController {
}
}
void setPolicy(@Nullable LowPowerStandbyPolicy policy) {
synchronized (mLock) {
if (!mSupportedConfig) {
Slog.w(TAG, "Low Power Standby policy cannot be changed "
+ "because it is not supported on this device");
return;
}
if (!mEnableCustomPolicy) {
Slog.d(TAG, "Custom policies are not enabled.");
return;
}
if (DEBUG) {
Slog.d(TAG, "setPolicy: policy=" + policy);
}
if (Objects.equals(mPolicy, policy)) {
return;
}
boolean allowlistChanged = policyChangeAffectsAllowlistLocked(mPolicy, policy);
mPolicy = policy;
enqueueSavePolicy(mPolicy);
if (allowlistChanged) {
enqueueNotifyAllowlistChangedLocked();
}
enqueueNotifyPolicyChangedLocked();
}
}
@NonNull
LowPowerStandbyPolicy getPolicy() {
synchronized (mLock) {
if (!mSupportedConfig) {
return null;
} else if (mEnableCustomPolicy) {
return policyOrDefault(mPolicy);
} else {
return DEFAULT_POLICY;
}
}
}
@NonNull
private LowPowerStandbyPolicy policyOrDefault(@Nullable LowPowerStandbyPolicy policy) {
if (policy == null) {
return DEFAULT_POLICY;
}
return policy;
}
boolean isPackageExempt(int uid) {
synchronized (mLock) {
if (!isEnabled()) {
return true;
}
return getExemptPackageAppIdsLocked().contains(UserHandle.getAppId(uid));
}
}
boolean isAllowed(@LowPowerStandbyAllowedReason int reason) {
synchronized (mLock) {
if (!isEnabled()) {
return true;
}
return (getPolicy().getAllowedReasons() & reason) != 0;
}
}
boolean isAllowed(String feature) {
synchronized (mLock) {
if (!mSupportedConfig) {
return true;
}
return !isEnabled() || getPolicy().getAllowedFeatures().contains(feature);
}
}
private boolean policyChangeAffectsAllowlistLocked(
@Nullable LowPowerStandbyPolicy oldPolicy, @Nullable LowPowerStandbyPolicy newPolicy) {
final LowPowerStandbyPolicy policyA = policyOrDefault(oldPolicy);
final LowPowerStandbyPolicy policyB = policyOrDefault(newPolicy);
int allowedReasonsInUse = 0;
for (int i = 0; i < mUidAllowedReasons.size(); i++) {
allowedReasonsInUse |= mUidAllowedReasons.valueAt(i);
}
int policyAllowedReasonsChanged = policyA.getAllowedReasons() ^ policyB.getAllowedReasons();
boolean exemptPackagesChanged = !policyA.getExemptPackages().equals(
policyB.getExemptPackages());
return (policyAllowedReasonsChanged & allowedReasonsInUse) != 0 || exemptPackagesChanged;
}
void dump(PrintWriter pw) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
@@ -496,6 +887,8 @@ public class LowPowerStandbyController {
ipw.println(mEnabledByDefaultConfig);
ipw.print("mStandbyTimeoutConfig=");
ipw.println(mStandbyTimeoutConfig);
ipw.print("mEnableCustomPolicy=");
ipw.println(mEnableCustomPolicy);
if (mIsActive || mIsEnabled) {
ipw.print("mIsInteractive=");
@@ -509,8 +902,35 @@ public class LowPowerStandbyController {
}
final int[] allowlistUids = getAllowlistUidsLocked();
ipw.print("mAllowlistUids=");
ipw.print("Allowed UIDs=");
ipw.println(Arrays.toString(allowlistUids));
ipw.println();
final LowPowerStandbyPolicy policy = getPolicy();
ipw.println("mPolicy:");
ipw.increaseIndent();
ipw.print("mIdentifier=");
ipw.println(policy.getIdentifier());
ipw.print("mExemptPackages=");
ipw.println(String.join(",", policy.getExemptPackages()));
ipw.print("mAllowedReasons=");
ipw.println(lowPowerStandbyAllowedReasonsToString(policy.getAllowedReasons()));
ipw.print("mAllowedFeatures=");
ipw.println(String.join(",", policy.getAllowedFeatures()));
ipw.decreaseIndent();
ipw.println();
ipw.println("UID allowed reasons:");
ipw.increaseIndent();
for (int i = 0; i < mUidAllowedReasons.size(); i++) {
if (mUidAllowedReasons.valueAt(i) > 0) {
ipw.print(mUidAllowedReasons.keyAt(i));
ipw.print(": ");
ipw.println(
lowPowerStandbyAllowedReasonsToString(mUidAllowedReasons.valueAt(i)));
}
}
ipw.decreaseIndent();
}
ipw.decreaseIndent();
}
@@ -537,6 +957,17 @@ public class LowPowerStandbyController {
proto.write(LowPowerStandbyControllerDumpProto.ALLOWLIST, appId);
}
long policyToken = proto.start(LowPowerStandbyControllerDumpProto.POLICY);
final LowPowerStandbyPolicy policy = getPolicy();
proto.write(LowPowerStandbyPolicyProto.IDENTIFIER, policy.getIdentifier());
for (String exemptPackage : policy.getExemptPackages()) {
proto.write(LowPowerStandbyPolicyProto.EXEMPT_PACKAGES, exemptPackage);
}
proto.write(LowPowerStandbyPolicyProto.ALLOWED_REASONS, policy.getAllowedReasons());
for (String feature : policy.getAllowedFeatures()) {
proto.write(LowPowerStandbyPolicyProto.ALLOWED_FEATURES, feature);
}
proto.end(policyToken);
proto.end(token);
}
}
@@ -560,39 +991,133 @@ public class LowPowerStandbyController {
final int[] allowlistUids = (int[]) msg.obj;
notifyAllowlistChanged(allowlistUids);
break;
}
}
}
private void addToAllowlistInternal(int uid) {
if (DEBUG) {
Slog.i(TAG, "Adding to allowlist: " + uid);
}
synchronized (mLock) {
if (mSupportedConfig && !mAllowlistUids.get(uid)) {
mAllowlistUids.append(uid, true);
enqueueNotifyAllowlistChangedLocked();
}
}
}
private void removeFromAllowlistInternal(int uid) {
if (DEBUG) {
Slog.i(TAG, "Removing from allowlist: " + uid);
}
synchronized (mLock) {
if (mSupportedConfig && mAllowlistUids.get(uid)) {
mAllowlistUids.delete(uid);
enqueueNotifyAllowlistChangedLocked();
case MSG_NOTIFY_POLICY_CHANGED:
notifyPolicyChanged((LowPowerStandbyPolicy) msg.obj);
break;
}
}
}
@GuardedBy("mLock")
private boolean hasAllowedReasonLocked(int uid,
@LowPowerStandbyAllowedReason int allowedReason) {
int allowedReasons = mUidAllowedReasons.get(uid);
return (allowedReasons & allowedReason) != 0;
}
@GuardedBy("mLock")
private boolean addAllowedReasonLocked(int uid,
@LowPowerStandbyAllowedReason int allowedReason) {
int allowedReasons = mUidAllowedReasons.get(uid);
final int newAllowReasons = allowedReasons | allowedReason;
mUidAllowedReasons.put(uid, newAllowReasons);
return allowedReasons != newAllowReasons;
}
@GuardedBy("mLock")
private boolean removeAllowedReasonLocked(int uid,
@LowPowerStandbyAllowedReason int allowedReason) {
int allowedReasons = mUidAllowedReasons.get(uid);
if (allowedReasons == 0) {
return false;
}
final int newAllowedReasons = allowedReasons & ~allowedReason;
if (newAllowedReasons == 0) {
mUidAllowedReasons.removeAt(mUidAllowedReasons.indexOfKey(uid));
} else {
mUidAllowedReasons.put(uid, newAllowedReasons);
}
return allowedReasons != newAllowedReasons;
}
private void addToAllowlistInternal(int uid, @LowPowerStandbyAllowedReason int allowedReason) {
if (DEBUG) {
Slog.i(TAG,
"Adding to allowlist: uid=" + uid + ", allowedReason=" + allowedReason);
}
synchronized (mLock) {
if (allowedReason != 0 && !hasAllowedReasonLocked(uid, allowedReason)) {
addAllowedReasonLocked(uid, allowedReason);
if ((getPolicy().getAllowedReasons() & allowedReason) != 0) {
enqueueNotifyAllowlistChangedLocked();
}
}
}
}
private void removeFromAllowlistInternal(int uid,
@LowPowerStandbyAllowedReason int allowedReason) {
if (DEBUG) {
Slog.i(TAG, "Removing from allowlist: uid=" + uid + ", allowedReason=" + allowedReason);
}
synchronized (mLock) {
if (allowedReason != 0 && hasAllowedReasonLocked(uid, allowedReason)) {
removeAllowedReasonLocked(uid, allowedReason);
if ((getPolicy().getAllowedReasons() & allowedReason) != 0) {
enqueueNotifyAllowlistChangedLocked();
}
}
}
}
@GuardedBy("mLock")
@NonNull
private List<Integer> getExemptPackageAppIdsLocked() {
final PackageManager packageManager = mContext.getPackageManager();
final LowPowerStandbyPolicy policy = getPolicy();
final List<Integer> appIds = new ArrayList<>();
for (String packageName : policy.getExemptPackages()) {
try {
int packageUid = packageManager.getPackageUid(packageName,
PackageManager.PackageInfoFlags.of(0));
int appId = UserHandle.getAppId(packageUid);
appIds.add(appId);
} catch (PackageManager.NameNotFoundException e) {
if (DEBUG) {
Slog.d(TAG, "Package UID cannot be resolved: packageName=" + packageName);
}
}
}
return appIds;
}
@GuardedBy("mLock")
private int[] getAllowlistUidsLocked() {
final int[] uids = new int[mAllowlistUids.size()];
for (int i = 0; i < mAllowlistUids.size(); i++) {
uids[i] = mAllowlistUids.keyAt(i);
final UserManager userManager = mContext.getSystemService(UserManager.class);
final List<UserHandle> userHandles = userManager.getUserHandles(true);
final ArraySet<Integer> uids = new ArraySet<>(mUidAllowedReasons.size());
final LowPowerStandbyPolicy policy = getPolicy();
final int policyAllowedReasons = policy.getAllowedReasons();
for (int i = 0; i < mUidAllowedReasons.size(); i++) {
Integer uid = mUidAllowedReasons.keyAt(i);
if ((mUidAllowedReasons.valueAt(i) & policyAllowedReasons) != 0) {
uids.add(uid);
}
}
for (int appId : getExemptPackageAppIdsLocked()) {
for (int uid : uidsForAppId(appId, userHandles)) {
uids.add(uid);
}
}
int[] allowlistUids = new int[uids.size()];
for (int i = 0; i < uids.size(); i++) {
allowlistUids[i] = uids.valueAt(i);
}
Arrays.sort(allowlistUids);
return allowlistUids;
}
private int[] uidsForAppId(int appUid, List<UserHandle> userHandles) {
final int appId = UserHandle.getAppId(appUid);
final int[] uids = new int[userHandles.size()];
for (int i = 0; i < userHandles.size(); i++) {
uids[i] = userHandles.get(i).getUid(appId);
}
return uids;
}
@@ -601,11 +1126,21 @@ public class LowPowerStandbyController {
private void enqueueNotifyAllowlistChangedLocked() {
final long now = mClock.elapsedRealtime();
final int[] allowlistUids = getAllowlistUidsLocked();
if (DEBUG) {
Slog.d(TAG, "enqueueNotifyAllowlistChangedLocked: allowlistUids=" + Arrays.toString(
allowlistUids));
}
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ALLOWLIST_CHANGED, allowlistUids);
mHandler.sendMessageAtTime(msg, now);
}
private void notifyAllowlistChanged(int[] allowlistUids) {
if (DEBUG) {
Slog.d(TAG, "notifyAllowlistChanged: " + Arrays.toString(allowlistUids));
}
final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
final NetworkPolicyManagerInternal npmi = LocalServices.getService(
NetworkPolicyManagerInternal.class);
@@ -613,15 +1148,42 @@ public class LowPowerStandbyController {
npmi.setLowPowerStandbyAllowlist(allowlistUids);
}
/**
* Class that is used to read device config for low power standby configuration.
*/
@VisibleForTesting
public static class DeviceConfigWrapper {
public static final String NAMESPACE = "low_power_standby";
public static final String FEATURE_FLAG_ENABLE_POLICY = "enable_policy";
/**
* Returns true if custom policies are enabled.
* Otherwise, returns false, and the default policy will be used.
*/
public boolean enableCustomPolicy() {
return DeviceConfig.getBoolean(NAMESPACE, FEATURE_FLAG_ENABLE_POLICY, false);
}
/**
* Registers a DeviceConfig update listener.
*/
public void registerPropertyUpdateListener(
@NonNull Executor executor,
@NonNull DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener) {
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, executor,
onPropertiesChangedListener);
}
}
private final class LocalService extends LowPowerStandbyControllerInternal {
@Override
public void addToAllowlist(int uid) {
addToAllowlistInternal(uid);
public void addToAllowlist(int uid, @LowPowerStandbyAllowedReason int allowedReason) {
addToAllowlistInternal(uid, allowedReason);
}
@Override
public void removeFromAllowlist(int uid) {
removeFromAllowlistInternal(uid);
public void removeFromAllowlist(int uid, @LowPowerStandbyAllowedReason int allowedReason) {
removeFromAllowlistInternal(uid, allowedReason);
}
}

View File

@@ -16,6 +16,8 @@
package com.android.server.power;
import android.os.PowerManager.LowPowerStandbyAllowedReason;
/**
* @hide Only for use within the system server.
*/
@@ -25,13 +27,16 @@ public abstract class LowPowerStandbyControllerInternal {
* exempting it from Low Power Standby restrictions.
*
* @param uid UID to add to allowlist.
* @param allowedReason reason for this package to be allowed
*/
public abstract void addToAllowlist(int uid);
public abstract void addToAllowlist(int uid, @LowPowerStandbyAllowedReason int allowedReason);
/**
* Removes an application from the Low Power Standby allowlist.
*
* @param uid UID to remove from allowlist.
* @param allowedReason reason for this package to have been allowed
*/
public abstract void removeFromAllowlist(int uid);
public abstract void removeFromAllowlist(int uid,
@LowPowerStandbyAllowedReason int allowedReason);
}

View File

@@ -635,7 +635,7 @@ public final class PowerManagerService extends SystemService
// Set of app ids that are temporarily allowed to acquire wakelocks due to high-pri message
int[] mDeviceIdleTempWhitelist = new int[0];
// Set of app ids that are allowed to acquire wakelocks while low power standby is active
// Set of uids that are allowed to acquire wakelocks while low power standby is active
int[] mLowPowerStandbyAllowlist = new int[0];
private boolean mLowPowerStandbyActive;
@@ -1027,7 +1027,7 @@ public final class PowerManagerService extends SystemService
}
LowPowerStandbyController createLowPowerStandbyController(Context context, Looper looper) {
return new LowPowerStandbyController(context, looper, SystemClock::elapsedRealtime);
return new LowPowerStandbyController(context, looper);
}
AppOpsManager createAppOpsManager(Context context) {
@@ -3935,9 +3935,9 @@ public final class PowerManagerService extends SystemService
}
}
void setLowPowerStandbyAllowlistInternal(int[] appids) {
void setLowPowerStandbyAllowlistInternal(int[] uids) {
synchronized (mLock) {
mLowPowerStandbyAllowlist = appids;
mLowPowerStandbyAllowlist = uids;
if (mLowPowerStandbyActive) {
updateWakeLockDisabledStatesLocked();
}
@@ -4095,7 +4095,7 @@ public final class PowerManagerService extends SystemService
}
if (mLowPowerStandbyActive) {
final UidState state = wakeLock.mUidState;
if (Arrays.binarySearch(mLowPowerStandbyAllowlist, appid) < 0
if (Arrays.binarySearch(mLowPowerStandbyAllowlist, wakeLock.mOwnerUid) < 0
&& state.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT
&& state.mProcState > ActivityManager.PROCESS_STATE_BOUND_TOP) {
disabled = true;
@@ -6150,6 +6150,82 @@ public final class PowerManagerService extends SystemService
}
}
@Override // Binder call
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
android.Manifest.permission.DEVICE_POWER
})
public void setLowPowerStandbyPolicy(@Nullable IPowerManager.LowPowerStandbyPolicy policy) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
"setLowPowerStandbyPolicy");
}
final long ident = Binder.clearCallingIdentity();
try {
mLowPowerStandbyController.setPolicy(
PowerManager.LowPowerStandbyPolicy.fromParcelable(policy));
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override // Binder call
@RequiresPermission(anyOf = {
android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
android.Manifest.permission.DEVICE_POWER
})
public IPowerManager.LowPowerStandbyPolicy getLowPowerStandbyPolicy() {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
!= PackageManager.PERMISSION_GRANTED) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_LOW_POWER_STANDBY,
"getLowPowerStandbyPolicy");
}
final long ident = Binder.clearCallingIdentity();
try {
return PowerManager.LowPowerStandbyPolicy.toParcelable(
mLowPowerStandbyController.getPolicy());
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override // Binder call
public boolean isExemptFromLowPowerStandby() {
final int callingUid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
return mLowPowerStandbyController.isPackageExempt(callingUid);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override // Binder call
public boolean isReasonAllowedInLowPowerStandby(
@PowerManager.LowPowerStandbyAllowedReason int reason) {
final long ident = Binder.clearCallingIdentity();
try {
return mLowPowerStandbyController.isAllowed(reason);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override // Binder call
public boolean isFeatureAllowedInLowPowerStandby(String feature) {
final long ident = Binder.clearCallingIdentity();
try {
return mLowPowerStandbyController.isAllowed(feature);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Gets the reason for the last time the phone had to reboot.
*

View File

@@ -16,8 +16,14 @@
package com.android.server.power;
import static android.os.PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION;
import static android.os.PowerManager.LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -27,20 +33,29 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.IPowerManager;
import android.os.PowerManager;
import android.os.PowerManager.LowPowerStandbyPolicy;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
@@ -48,6 +63,7 @@ import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.power.LowPowerStandbyController.DeviceConfigWrapper;
import com.android.server.testutils.OffsettableClock;
import org.junit.After;
@@ -58,6 +74,9 @@ import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@@ -68,35 +87,53 @@ import java.util.concurrent.TimeUnit;
*/
public class LowPowerStandbyControllerTest {
private static final int STANDBY_TIMEOUT = 5000;
private static final String TEST_PKG1 = "PKG1";
private static final String TEST_PKG2 = "PKG2";
private static final int TEST_PKG1_APP_ID = 123;
private static final int TEST_PKG2_APP_ID = 456;
private static final int USER_ID_1 = 0;
private static final int USER_ID_2 = 10;
private static final LowPowerStandbyPolicy EMPTY_POLICY = new LowPowerStandbyPolicy(
"Test policy", Collections.emptySet(), 0, Collections.emptySet());
private LowPowerStandbyController mController;
private BroadcastInterceptingContext mContextSpy;
private Resources mResourcesSpy;
private OffsettableClock mClock;
private TestLooper mTestLooper;
private File mTestPolicyFile;
@Mock
private DeviceConfigWrapper mDeviceConfigWrapperMock;
@Mock
private AlarmManager mAlarmManagerMock;
@Mock
private PackageManager mPackageManagerMock;
@Mock
private UserManager mUserManagerMock;
@Mock
private IPowerManager mIPowerManagerMock;
@Mock
private PowerManagerInternal mPowerManagerInternalMock;
@Mock
private NetworkPolicyManagerInternal mNetworkPolicyManagerInternal;
private NetworkPolicyManagerInternal mNetworkPolicyManagerInternalMock;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContextSpy = spy(new BroadcastInterceptingContext(InstrumentationRegistry.getContext()));
when(mContextSpy.getPackageManager()).thenReturn(mPackageManagerMock);
when(mContextSpy.getSystemService(AlarmManager.class)).thenReturn(mAlarmManagerMock);
when(mContextSpy.getSystemService(UserManager.class)).thenReturn(mUserManagerMock);
PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null, null);
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
addLocalServiceMock(NetworkPolicyManagerInternal.class, mNetworkPolicyManagerInternal);
addLocalServiceMock(NetworkPolicyManagerInternal.class, mNetworkPolicyManagerInternalMock);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
when(mDeviceConfigWrapperMock.enableCustomPolicy()).thenReturn(true);
mResourcesSpy = spy(mContextSpy.getResources());
when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
when(mResourcesSpy.getBoolean(
@@ -114,11 +151,17 @@ public class LowPowerStandbyControllerTest {
cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContextSpy.getContentResolver()).thenReturn(cr);
when(mUserManagerMock.getUserHandles(true)).thenReturn(List.of(
UserHandle.of(USER_ID_1), UserHandle.of(USER_ID_2)));
when(mPackageManagerMock.getPackageUid(eq(TEST_PKG1), any())).thenReturn(TEST_PKG1_APP_ID);
when(mPackageManagerMock.getPackageUid(eq(TEST_PKG2), any())).thenReturn(TEST_PKG2_APP_ID);
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
mTestPolicyFile = new File(mContextSpy.getCacheDir(), "lps_policy.xml");
mController = new LowPowerStandbyController(mContextSpy, mTestLooper.getLooper(),
() -> mClock.now());
() -> mClock.now(), mDeviceConfigWrapperMock, mTestPolicyFile);
}
@After
@@ -126,6 +169,7 @@ public class LowPowerStandbyControllerTest {
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.removeServiceForTest(LowPowerStandbyControllerInternal.class);
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
mTestPolicyFile.delete();
}
@Test
@@ -135,7 +179,7 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isFalse();
verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
verify(mNetworkPolicyManagerInternal, never()).setLowPowerStandbyActive(anyBoolean());
verify(mNetworkPolicyManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
}
@Test
@@ -148,7 +192,7 @@ public class LowPowerStandbyControllerTest {
awaitStandbyTimeoutAlarm();
assertThat(mController.isActive()).isTrue();
verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(true);
verify(mNetworkPolicyManagerInternal, times(1)).setLowPowerStandbyActive(true);
verify(mNetworkPolicyManagerInternalMock, times(1)).setLowPowerStandbyActive(true);
}
private void awaitStandbyTimeoutAlarm() {
@@ -176,7 +220,7 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isFalse();
verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
verify(mNetworkPolicyManagerInternal, never()).setLowPowerStandbyActive(anyBoolean());
verify(mNetworkPolicyManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
}
@Test
@@ -190,7 +234,7 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isTrue();
verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(true);
verify(mNetworkPolicyManagerInternal, times(1)).setLowPowerStandbyActive(true);
verify(mNetworkPolicyManagerInternalMock, times(1)).setLowPowerStandbyActive(true);
}
@Test
@@ -206,7 +250,7 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isFalse();
verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
verify(mNetworkPolicyManagerInternal, never()).setLowPowerStandbyActive(anyBoolean());
verify(mNetworkPolicyManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
}
private void verifyStandbyAlarmCancelled() {
@@ -231,7 +275,7 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isFalse();
verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(false);
verify(mNetworkPolicyManagerInternal, times(1)).setLowPowerStandbyActive(false);
verify(mNetworkPolicyManagerInternalMock, times(1)).setLowPowerStandbyActive(false);
}
@Test
@@ -249,7 +293,7 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isFalse();
verify(mPowerManagerInternalMock, times(1)).setLowPowerStandbyActive(false);
verify(mNetworkPolicyManagerInternal, times(1)).setLowPowerStandbyActive(false);
verify(mNetworkPolicyManagerInternalMock, times(1)).setLowPowerStandbyActive(false);
}
@Test
@@ -267,7 +311,7 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isTrue();
verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(false);
verify(mNetworkPolicyManagerInternal, never()).setLowPowerStandbyActive(false);
verify(mNetworkPolicyManagerInternalMock, never()).setLowPowerStandbyActive(false);
}
@Test
@@ -286,7 +330,7 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isTrue();
verify(mPowerManagerInternalMock, times(2)).setLowPowerStandbyActive(true);
verify(mNetworkPolicyManagerInternal, times(2)).setLowPowerStandbyActive(true);
verify(mNetworkPolicyManagerInternalMock, times(2)).setLowPowerStandbyActive(true);
}
@Test
@@ -299,7 +343,7 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isFalse();
verify(mAlarmManagerMock, never()).setExact(anyInt(), anyLong(), anyString(), any(), any());
verify(mPowerManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
verify(mNetworkPolicyManagerInternal, never()).setLowPowerStandbyActive(anyBoolean());
verify(mNetworkPolicyManagerInternalMock, never()).setLowPowerStandbyActive(anyBoolean());
}
@Test
@@ -355,24 +399,6 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isSupported()).isFalse();
}
@Test
public void testAllowlistChange_servicesAreNotified() throws Exception {
setLowPowerStandbySupportedConfig(true);
mController.systemReady();
LowPowerStandbyControllerInternal service = LocalServices.getService(
LowPowerStandbyControllerInternal.class);
service.addToAllowlist(10);
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {10});
verify(mNetworkPolicyManagerInternal).setLowPowerStandbyAllowlist(new int[] {10});
service.removeFromAllowlist(10);
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {});
verify(mNetworkPolicyManagerInternal).setLowPowerStandbyAllowlist(new int[] {});
}
@Test
public void testForceActive() throws Exception {
setLowPowerStandbySupportedConfig(false);
@@ -383,14 +409,14 @@ public class LowPowerStandbyControllerTest {
assertThat(mController.isActive()).isTrue();
verify(mPowerManagerInternalMock).setLowPowerStandbyActive(true);
verify(mNetworkPolicyManagerInternal).setLowPowerStandbyActive(true);
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyActive(true);
mController.forceActive(false);
mTestLooper.dispatchAll();
assertThat(mController.isActive()).isFalse();
verify(mPowerManagerInternalMock).setLowPowerStandbyActive(false);
verify(mNetworkPolicyManagerInternal).setLowPowerStandbyActive(false);
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyActive(false);
}
private void setLowPowerStandbySupportedConfig(boolean supported) {
@@ -399,6 +425,244 @@ public class LowPowerStandbyControllerTest {
.thenReturn(supported);
}
@Test
public void testSetPolicy() throws Exception {
mController.systemReady();
mController.setPolicy(EMPTY_POLICY);
assertThat(mController.getPolicy()).isEqualTo(EMPTY_POLICY);
}
@Test
public void testSetDefaultPolicy() throws Exception {
mController.systemReady();
mController.setPolicy(EMPTY_POLICY);
mController.setPolicy(null);
assertThat(mController.getPolicy()).isNotNull();
assertThat(mController.getPolicy()).isEqualTo(LowPowerStandbyController.DEFAULT_POLICY);
}
@Test
public void testAddToAllowlist_ReasonIsAllowed_servicesAreNotified() throws Exception {
mController.systemReady();
mController.setPolicy(
policyWithAllowedReasons(LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION));
LowPowerStandbyControllerInternal service = LocalServices.getService(
LowPowerStandbyControllerInternal.class);
service.addToAllowlist(10, LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{10});
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{10});
service.removeFromAllowlist(10, LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{});
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{});
}
@Test
public void testRemoveFromAllowlist_ReasonIsAllowed_servicesAreNotified() throws Exception {
mController.systemReady();
mController.setPolicy(
policyWithAllowedReasons(LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION));
LowPowerStandbyControllerInternal service = LocalServices.getService(
LowPowerStandbyControllerInternal.class);
service.addToAllowlist(10, LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
mTestLooper.dispatchAll();
service.removeFromAllowlist(10, LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{});
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{});
}
@Test
public void testSetAllowReasons_ActiveExemptionsNoLongerAllowed_servicesAreNotified() {
mController.systemReady();
mController.setEnabled(true);
mController.setPolicy(
policyWithAllowedReasons(LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION));
LowPowerStandbyControllerInternal service = LocalServices.getService(
LowPowerStandbyControllerInternal.class);
service.addToAllowlist(10, LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
mTestLooper.dispatchAll();
mController.setPolicy(EMPTY_POLICY);
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{});
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{});
}
@Test
public void testSetAllowReasons_ReasonBecomesAllowed_servicesAreNotified() throws Exception {
mController.systemReady();
mController.setEnabled(true);
mController.setPolicy(EMPTY_POLICY);
LowPowerStandbyControllerInternal service = LocalServices.getService(
LowPowerStandbyControllerInternal.class);
service.addToAllowlist(10, LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock, never()).setLowPowerStandbyAllowlist(any());
verify(mNetworkPolicyManagerInternalMock, never()).setLowPowerStandbyAllowlist(any());
mController.setPolicy(
policyWithAllowedReasons(LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION));
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{10});
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{10});
}
@Test
public void testSetAllowReasons_NoActiveExemptions_servicesAreNotNotified() throws Exception {
mController.systemReady();
mController.setEnabled(true);
mController.setPolicy(
policyWithAllowedReasons(LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION));
mController.setPolicy(EMPTY_POLICY);
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock, never()).setLowPowerStandbyAllowlist(any());
verify(mNetworkPolicyManagerInternalMock, never()).setLowPowerStandbyAllowlist(any());
}
@Test
public void testSetAllowedFeatures_isAllowedIfDisabled() throws Exception {
mController.systemReady();
mController.setEnabled(false);
mTestLooper.dispatchAll();
assertTrue(mController.isAllowed(LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN));
}
@Test
public void testSetAllowedFeatures_isAllowedWhenEnabled() throws Exception {
mController.systemReady();
mController.setEnabled(true);
mController.setPolicy(policyWithAllowedFeatures(LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN));
mTestLooper.dispatchAll();
assertTrue(mController.isAllowed(LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN));
}
@Test
public void testSetAllowedFeatures_isNotAllowed() throws Exception {
mController.systemReady();
mController.setEnabled(true);
mTestLooper.dispatchAll();
assertFalse(mController.isAllowed(LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN));
}
@Test
public void testSetExemptPackages_uidPerUserIsExempt() throws Exception {
mController.systemReady();
mController.setEnabled(true);
mController.setPolicy(policyWithExemptPackages(TEST_PKG1, TEST_PKG2));
mTestLooper.dispatchAll();
int[] expectedUidAllowlist = {
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
UserHandle.getUid(USER_ID_1, TEST_PKG2_APP_ID),
UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID),
UserHandle.getUid(USER_ID_2, TEST_PKG2_APP_ID)
};
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(expectedUidAllowlist);
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(expectedUidAllowlist);
}
@Test
public void testExemptPackageIsRemoved_servicesAreNotified() throws Exception {
mController.systemReady();
mController.setEnabled(true);
mController.setPolicy(policyWithExemptPackages(TEST_PKG1));
mTestLooper.dispatchAll();
int[] expectedUidAllowlist = {
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID),
};
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(expectedUidAllowlist);
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(expectedUidAllowlist);
verifyNoMoreInteractions(mPowerManagerInternalMock, mNetworkPolicyManagerInternalMock);
reset(mPackageManagerMock);
when(mPackageManagerMock.getPackageUid(eq(TEST_PKG1), any()))
.thenThrow(PackageManager.NameNotFoundException.class);
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
intent.setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, TEST_PKG1, null));
intent.putExtra(Intent.EXTRA_REPLACING, false);
mContextSpy.sendBroadcast(intent);
mTestLooper.dispatchAll();
verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[0]);
verify(mNetworkPolicyManagerInternalMock).setLowPowerStandbyAllowlist(new int[0]);
}
@Test
public void testUsersChanged_packagesExemptForNewUser() throws Exception {
mController.systemReady();
mController.setEnabled(true);
mController.setPolicy(policyWithExemptPackages(TEST_PKG1));
mTestLooper.dispatchAll();
InOrder inOrder = inOrder(mPowerManagerInternalMock);
inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID),
});
inOrder.verifyNoMoreInteractions();
when(mUserManagerMock.getUserHandles(true)).thenReturn(List.of(UserHandle.of(USER_ID_1)));
Intent intent = new Intent(Intent.ACTION_USER_REMOVED);
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(USER_ID_2));
mContextSpy.sendBroadcast(intent);
mTestLooper.dispatchAll();
inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID)
});
inOrder.verifyNoMoreInteractions();
when(mUserManagerMock.getUserHandles(true)).thenReturn(
List.of(UserHandle.of(USER_ID_1), UserHandle.of(USER_ID_2)));
intent = new Intent(Intent.ACTION_USER_ADDED);
intent.putExtra(Intent.EXTRA_USER, UserHandle.of(USER_ID_2));
mContextSpy.sendBroadcast(intent);
mTestLooper.dispatchAll();
inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{
UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID)
});
inOrder.verifyNoMoreInteractions();
}
@Test
public void testIsExempt_exemptIfDisabled() throws Exception {
mController.systemReady();
mController.setEnabled(false);
mTestLooper.dispatchAll();
assertTrue(mController.isPackageExempt(TEST_PKG1_APP_ID));
}
@Test
public void testIsExempt_notExemptIfEnabled() throws Exception {
mController.systemReady();
mController.setEnabled(true);
mTestLooper.dispatchAll();
assertFalse(mController.isPackageExempt(TEST_PKG1_APP_ID));
}
private void setInteractive() throws Exception {
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mContextSpy.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
@@ -414,6 +678,33 @@ public class LowPowerStandbyControllerTest {
mContextSpy.sendBroadcast(new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
}
private LowPowerStandbyPolicy policyWithAllowedReasons(int allowedReasons) {
return new LowPowerStandbyPolicy(
"Test policy",
Collections.emptySet(),
allowedReasons,
Collections.emptySet()
);
}
private LowPowerStandbyPolicy policyWithAllowedFeatures(String... allowedFeatures) {
return new LowPowerStandbyPolicy(
"Test policy",
Collections.emptySet(),
0,
new ArraySet<>(allowedFeatures)
);
}
private LowPowerStandbyPolicy policyWithExemptPackages(String... exemptPackages) {
return new LowPowerStandbyPolicy(
"Test policy",
new ArraySet<>(exemptPackages),
0,
Collections.emptySet()
);
}
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();

View File

@@ -51,6 +51,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -346,7 +347,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
mFgHandler.post(mSetPowerBoostRunnable);
if (mLowPowerStandbyControllerInternal != null) {
mLowPowerStandbyControllerInternal.addToAllowlist(mCallingUid);
mLowPowerStandbyControllerInternal.addToAllowlist(mCallingUid,
PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
mLowPowerStandbyAllowlisted = true;
mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable);
mFgHandler.postDelayed(mRemoveFromLowPowerStandbyAllowlistRunnable,
@@ -861,7 +863,8 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
synchronized (mLock) {
if (mLowPowerStandbyAllowlisted) {
mFgHandler.removeCallbacks(mRemoveFromLowPowerStandbyAllowlistRunnable);
mLowPowerStandbyControllerInternal.removeFromAllowlist(mCallingUid);
mLowPowerStandbyControllerInternal.removeFromAllowlist(mCallingUid,
PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION);
mLowPowerStandbyAllowlisted = false;
}
}