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:
committed by
Android (Google) Code Review
commit
7357be30dd
@@ -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
|
||||
|
||||
@@ -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>);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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. -->
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user