LowPowerStandby: Add APIs to get/set custom Policy

The Low Power Standby Policy declares:
- Packages (by package name) that should be exempt from
  Low Power Standby restrictions
- Reasons why an app may be dynamically exempt from Low Power Standby
  restrictions.
- Features apps are allowed to use during Low Power Standby.
  The features are Strings, so that vendors can define custom features
  for functionality that may not always be available or allowed in
  all Low Power Standby policies.

The existing exemption for active Voice Interaction Sessions is migrated
to use a predefined allow-reason, and enabled in the default policy.
When a policy without the VIS allow-reason is set, then apps having an
active VIS are no longer exempted from Low Power Standby restrictions.

Bug: 234002812
Test: atest LowPowerStandbyControllerTest LowPowerStandbyTest
Change-Id: I8747ae5b49613122e922aae7a917cbd2771ae091
This commit is contained in:
Robert Horvath
2022-11-25 15:00:58 +01:00
parent d2267f0739
commit 46ccaf46ff
11 changed files with 1128 additions and 82 deletions

View File

@@ -32831,9 +32831,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();
@@ -32856,6 +32859,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

@@ -10159,6 +10159,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();
@@ -10171,9 +10172,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";
@@ -10188,6 +10191,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

@@ -68,11 +68,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;
@@ -2343,6 +2347,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
@@ -2839,6 +2930,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,66 @@
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.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 +86,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 +108,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,7 +128,10 @@ 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();
@GuardedBy("mLock")
private boolean mEnableCustomPolicy;
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -159,6 +204,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 +223,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 +268,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 +289,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 +330,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();
@@ -396,6 +617,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 +654,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 +722,101 @@ 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();
return (policyAllowedReasonsChanged & allowedReasonsInUse) != 0;
}
void dump(PrintWriter pw) {
final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
@@ -496,6 +834,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 +849,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 +904,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,52 +938,138 @@ 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 int[] getAllowlistUidsLocked() {
final int[] uids = new int[mAllowlistUids.size()];
for (int i = 0; i < mAllowlistUids.size(); i++) {
uids[i] = mAllowlistUids.keyAt(i);
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;
}
return uids;
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 ArraySet<Integer> uids = new ArraySet<>(mUidAllowedReasons.size());
final int policyAllowedReasons = getPolicy().getAllowedReasons();
for (int i = 0; i < mUidAllowedReasons.size(); i++) {
Integer uid = mUidAllowedReasons.keyAt(i);
if ((mUidAllowedReasons.valueAt(i) & policyAllowedReasons) != 0) {
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;
}
@GuardedBy("mLock")
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 +1077,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

@@ -628,7 +628,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;
@@ -1020,7 +1020,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) {
@@ -3924,9 +3924,9 @@ public final class PowerManagerService extends SystemService
}
}
void setLowPowerStandbyAllowlistInternal(int[] appids) {
void setLowPowerStandbyAllowlistInternal(int[] uids) {
synchronized (mLock) {
mLowPowerStandbyAllowlist = appids;
mLowPowerStandbyAllowlist = uids;
if (mLowPowerStandbyActive) {
updateWakeLockDisabledStatesLocked();
}
@@ -4085,7 +4085,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;
@@ -6113,6 +6113,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;
@@ -37,10 +43,12 @@ import android.content.Intent;
import android.content.res.Resources;
import android.os.IPowerManager;
import android.os.PowerManager;
import android.os.PowerManager.LowPowerStandbyPolicy;
import android.os.PowerManagerInternal;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.util.ArraySet;
import androidx.test.InstrumentationRegistry;
@@ -48,6 +56,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 +67,8 @@ import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.File;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
/**
@@ -69,12 +80,18 @@ import java.util.concurrent.TimeUnit;
public class LowPowerStandbyControllerTest {
private static final int STANDBY_TIMEOUT = 5000;
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
@@ -82,7 +99,7 @@ public class LowPowerStandbyControllerTest {
@Mock
private PowerManagerInternal mPowerManagerInternalMock;
@Mock
private NetworkPolicyManagerInternal mNetworkPolicyManagerInternal;
private NetworkPolicyManagerInternal mNetworkPolicyManagerInternalMock;
@Before
public void setUp() throws Exception {
@@ -93,10 +110,11 @@ public class LowPowerStandbyControllerTest {
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(
@@ -117,8 +135,9 @@ public class LowPowerStandbyControllerTest {
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 +145,7 @@ public class LowPowerStandbyControllerTest {
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.removeServiceForTest(LowPowerStandbyControllerInternal.class);
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
mTestPolicyFile.delete();
}
@Test
@@ -135,7 +155,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 +168,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 +196,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 +210,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 +226,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 +251,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 +269,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 +287,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 +306,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 +319,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 +375,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 +385,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 +401,140 @@ 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));
}
private void setInteractive() throws Exception {
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
mContextSpy.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
@@ -414,6 +550,24 @@ 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 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;
}
}