Update Anomaly detection framework

This cl make it could have multiple actions for each anomaly type.

This includes:
1. Add field "targetSdkVersion", "bgRestrictionEnabled" in Anomaly
2. Add new action which includes "force stop" and "background check"
3. Add "sdkVersion" and "bgRestrictionEnabled" method in BatteryUtils
4. Update BackgroundCheckAction to check sdkVersion first
5. Update serveral tests and add new test

Bug: 63037765
Test: RunSettingsRoboTests
Change-Id: Iea7a131ee57d501f7737ae4a3ba672d948d05cd8
This commit is contained in:
jackqdyulei
2017-06-29 14:20:46 -07:00
parent 8218511fb7
commit bfc2b11b93
20 changed files with 457 additions and 75 deletions

View File

@@ -15,10 +15,13 @@
*/ */
package com.android.settings.fuelgauge; package com.android.settings.fuelgauge;
import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.BatteryStats; import android.os.BatteryStats;
import android.os.Bundle; import android.os.Bundle;
import android.os.Build;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserManager; import android.os.UserManager;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
@@ -47,6 +50,7 @@ import java.util.List;
*/ */
public class BatteryUtils { public class BatteryUtils {
public static final int UID_NULL = -1; public static final int UID_NULL = -1;
public static final int SDK_NULL = -1;
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({StatusType.FOREGROUND, @IntDef({StatusType.FOREGROUND,
@@ -66,6 +70,7 @@ public class BatteryUtils {
private static BatteryUtils sInstance; private static BatteryUtils sInstance;
private PackageManager mPackageManager; private PackageManager mPackageManager;
private AppOpsManager mAppOpsManager;
@VisibleForTesting @VisibleForTesting
PowerUsageFeatureProvider mPowerUsageFeatureProvider; PowerUsageFeatureProvider mPowerUsageFeatureProvider;
@@ -79,6 +84,7 @@ public class BatteryUtils {
@VisibleForTesting @VisibleForTesting
BatteryUtils(Context context) { BatteryUtils(Context context) {
mPackageManager = context.getPackageManager(); mPackageManager = context.getPackageManager();
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mPowerUsageFeatureProvider = FeatureFactory.getFactory( mPowerUsageFeatureProvider = FeatureFactory.getFactory(
context).getPowerUsageFeatureProvider(context); context).getPowerUsageFeatureProvider(context);
} }
@@ -263,6 +269,37 @@ public class BatteryUtils {
return ArrayUtils.isEmpty(packageNames) ? null : packageNames[0]; return ArrayUtils.isEmpty(packageNames) ? null : packageNames[0];
} }
/**
* Find the targetSdkVersion for package with name {@code packageName}
*
* @return the targetSdkVersion, or {@link #SDK_NULL} if {@code packageName} doesn't exist
*/
public int getTargetSdkVersion(final String packageName) {
try {
ApplicationInfo info = mPackageManager.getApplicationInfo(packageName,
PackageManager.GET_META_DATA);
return info.targetSdkVersion;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Cannot find package: " + packageName, e);
}
return SDK_NULL;
}
/**
* Check whether background restriction is enabled
*/
public boolean isBackgroundRestrictionEnabled(final int targetSdkVersion, final int uid,
final String packageName) {
if (targetSdkVersion >= Build.VERSION_CODES.O) {
return true;
}
final int mode = mAppOpsManager
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName);
return mode == AppOpsManager.MODE_IGNORED || mode == AppOpsManager.MODE_ERRORED;
}
/** /**
* Sort the {@code usageList} based on {@link BatterySipper#totalPowerMah} * Sort the {@code usageList} based on {@link BatterySipper#totalPowerMah}
*/ */
@@ -337,7 +374,7 @@ public class BatteryUtils {
} }
private boolean isDataCorrupted() { private boolean isDataCorrupted() {
return mPackageManager == null; return mPackageManager == null || mAppOpsManager == null;
} }
@VisibleForTesting @VisibleForTesting

View File

@@ -46,16 +46,18 @@ public class Anomaly implements Parcelable {
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({AnomalyActionType.FORCE_STOP, @IntDef({AnomalyActionType.FORCE_STOP,
AnomalyActionType.BACKGROUND_CHECK, AnomalyActionType.BACKGROUND_CHECK,
AnomalyActionType.LOCATION_CHECK}) AnomalyActionType.LOCATION_CHECK,
AnomalyActionType.STOP_AND_BACKGROUND_CHECK})
public @interface AnomalyActionType { public @interface AnomalyActionType {
int FORCE_STOP = 0; int FORCE_STOP = 0;
int BACKGROUND_CHECK = 1; int BACKGROUND_CHECK = 1;
int LOCATION_CHECK = 2; int LOCATION_CHECK = 2;
int STOP_AND_BACKGROUND_CHECK = 3;
} }
@AnomalyType @AnomalyType
public static final int[] ANOMALY_TYPE_LIST = public static final int[] ANOMALY_TYPE_LIST = {
{AnomalyType.WAKE_LOCK, AnomalyType.WAKE_LOCK,
AnomalyType.WAKEUP_ALARM, AnomalyType.WAKEUP_ALARM,
AnomalyType.BLUETOOTH_SCAN}; AnomalyType.BLUETOOTH_SCAN};
@@ -64,7 +66,14 @@ public class Anomaly implements Parcelable {
*/ */
public final int type; public final int type;
public final int uid; public final int uid;
public final int targetSdkVersion;
public final long wakelockTimeMs; public final long wakelockTimeMs;
/**
* {@code true} if background restriction is enabled
*
* @see android.app.AppOpsManager.OP_RUN_IN_BACKGROUND
*/
public final boolean backgroundRestrictionEnabled;
/** /**
* Display name of this anomaly, usually it is the app name * Display name of this anomaly, usually it is the app name
*/ */
@@ -77,6 +86,8 @@ public class Anomaly implements Parcelable {
displayName = builder.mDisplayName; displayName = builder.mDisplayName;
packageName = builder.mPackageName; packageName = builder.mPackageName;
wakelockTimeMs = builder.mWakeLockTimeMs; wakelockTimeMs = builder.mWakeLockTimeMs;
targetSdkVersion = builder.mTargetSdkVersion;
backgroundRestrictionEnabled = builder.mBgRestrictionEnabled;
} }
private Anomaly(Parcel in) { private Anomaly(Parcel in) {
@@ -85,6 +96,8 @@ public class Anomaly implements Parcelable {
displayName = in.readCharSequence(); displayName = in.readCharSequence();
packageName = in.readString(); packageName = in.readString();
wakelockTimeMs = in.readLong(); wakelockTimeMs = in.readLong();
targetSdkVersion = in.readInt();
backgroundRestrictionEnabled = in.readBoolean();
} }
@Override @Override
@@ -99,6 +112,8 @@ public class Anomaly implements Parcelable {
dest.writeCharSequence(displayName); dest.writeCharSequence(displayName);
dest.writeString(packageName); dest.writeString(packageName);
dest.writeLong(wakelockTimeMs); dest.writeLong(wakelockTimeMs);
dest.writeInt(targetSdkVersion);
dest.writeBoolean(backgroundRestrictionEnabled);
} }
@Override @Override
@@ -115,12 +130,15 @@ public class Anomaly implements Parcelable {
&& uid == other.uid && uid == other.uid
&& wakelockTimeMs == other.wakelockTimeMs && wakelockTimeMs == other.wakelockTimeMs
&& TextUtils.equals(displayName, other.displayName) && TextUtils.equals(displayName, other.displayName)
&& TextUtils.equals(packageName, other.packageName); && TextUtils.equals(packageName, other.packageName)
&& targetSdkVersion == other.targetSdkVersion
&& backgroundRestrictionEnabled == other.backgroundRestrictionEnabled;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(type, uid, displayName, packageName, wakelockTimeMs); return Objects.hash(type, uid, displayName, packageName, wakelockTimeMs, targetSdkVersion,
backgroundRestrictionEnabled);
} }
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@@ -137,9 +155,11 @@ public class Anomaly implements Parcelable {
@AnomalyType @AnomalyType
private int mType; private int mType;
private int mUid; private int mUid;
private int mTargetSdkVersion;
private CharSequence mDisplayName; private CharSequence mDisplayName;
private String mPackageName; private String mPackageName;
private long mWakeLockTimeMs; private long mWakeLockTimeMs;
private boolean mBgRestrictionEnabled;
public Builder setType(@AnomalyType int type) { public Builder setType(@AnomalyType int type) {
mType = type; mType = type;
@@ -166,6 +186,16 @@ public class Anomaly implements Parcelable {
return this; return this;
} }
public Builder setTargetSdkVersion(int targetSdkVersion) {
mTargetSdkVersion = targetSdkVersion;
return this;
}
public Builder setBackgroundRestrictionEnabled(boolean bgRestrictionEnabled) {
mBgRestrictionEnabled = bgRestrictionEnabled;
return this;
}
public Anomaly build() { public Anomaly build() {
return new Anomaly(this); return new Anomaly(this);
} }

View File

@@ -90,7 +90,7 @@ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements
return; return;
} }
final AnomalyAction anomalyAction = mAnomalyUtils.getAnomalyAction(mAnomaly.type); final AnomalyAction anomalyAction = mAnomalyUtils.getAnomalyAction(mAnomaly);
final int metricsKey = getArguments().getInt(ARG_METRICS_KEY); final int metricsKey = getArguments().getInt(ARG_METRICS_KEY);
anomalyAction.handlePositiveAction(mAnomaly, metricsKey); anomalyAction.handlePositiveAction(mAnomaly, metricsKey);
@@ -103,16 +103,18 @@ public class AnomalyDialogFragment extends InstrumentedDialogFragment implements
mAnomaly = bundle.getParcelable(ARG_ANOMALY); mAnomaly = bundle.getParcelable(ARG_ANOMALY);
final Context context = getContext(); final Context context = getContext();
final AnomalyAction anomalyAction = mAnomalyUtils.getAnomalyAction(mAnomaly.type); final AnomalyAction anomalyAction = mAnomalyUtils.getAnomalyAction(mAnomaly);
switch (anomalyAction.getActionType()) { switch (anomalyAction.getActionType()) {
case Anomaly.AnomalyActionType.FORCE_STOP: case Anomaly.AnomalyActionType.FORCE_STOP:
return new AlertDialog.Builder(context) return new AlertDialog.Builder(context)
.setTitle(R.string.dialog_stop_title) .setTitle(R.string.dialog_stop_title)
.setMessage(getString(R.string.dialog_stop_message, mAnomaly.displayName)) .setMessage(getString(mAnomaly.type == Anomaly.AnomalyType.WAKE_LOCK
? R.string.dialog_stop_message
: R.string.dialog_stop_message_wakeup_alarm, mAnomaly.displayName))
.setPositiveButton(R.string.dialog_stop_ok, this) .setPositiveButton(R.string.dialog_stop_ok, this)
.setNegativeButton(R.string.dlg_cancel, null) .setNegativeButton(R.string.dlg_cancel, null)
.create(); .create();
case Anomaly.AnomalyActionType.BACKGROUND_CHECK: case Anomaly.AnomalyActionType.STOP_AND_BACKGROUND_CHECK:
return new AlertDialog.Builder(context) return new AlertDialog.Builder(context)
.setTitle(R.string.dialog_background_check_title) .setTitle(R.string.dialog_background_check_title)
.setMessage(getString(R.string.dialog_background_check_message, .setMessage(getString(R.string.dialog_background_check_message,

View File

@@ -17,12 +17,13 @@
package com.android.settings.fuelgauge.anomaly; package com.android.settings.fuelgauge.anomaly;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.support.annotation.VisibleForTesting; import android.support.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction; import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import com.android.settings.fuelgauge.anomaly.action.BackgroundCheckAction;
import com.android.settings.fuelgauge.anomaly.action.ForceStopAction; import com.android.settings.fuelgauge.anomaly.action.ForceStopAction;
import com.android.settings.fuelgauge.anomaly.action.LocationCheckAction; import com.android.settings.fuelgauge.anomaly.action.LocationCheckAction;
import com.android.settings.fuelgauge.anomaly.action.StopAndBackgroundCheckAction;
import com.android.settings.fuelgauge.anomaly.checker.AnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.AnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
@@ -49,16 +50,22 @@ public class AnomalyUtils {
/** /**
* Return the corresponding {@link AnomalyAction} according to * Return the corresponding {@link AnomalyAction} according to
* {@link com.android.settings.fuelgauge.anomaly.Anomaly.AnomalyType} * {@link com.android.settings.fuelgauge.anomaly.Anomaly}
* *
* @return corresponding {@link AnomalyAction}, or null if cannot find it. * @return corresponding {@link AnomalyAction}, or null if cannot find it.
*/ */
public AnomalyAction getAnomalyAction(@Anomaly.AnomalyType int anomalyType) { public AnomalyAction getAnomalyAction(Anomaly anomaly) {
switch (anomalyType) { switch (anomaly.type) {
case Anomaly.AnomalyType.WAKE_LOCK: case Anomaly.AnomalyType.WAKE_LOCK:
return new ForceStopAction(mContext); return new ForceStopAction(mContext);
case Anomaly.AnomalyType.WAKEUP_ALARM: case Anomaly.AnomalyType.WAKEUP_ALARM:
return new BackgroundCheckAction(mContext); if (anomaly.targetSdkVersion >= Build.VERSION_CODES.O
|| (anomaly.targetSdkVersion < Build.VERSION_CODES.O
&& anomaly.backgroundRestrictionEnabled)) {
return new ForceStopAction(mContext);
} else {
return new StopAndBackgroundCheckAction(mContext);
}
case Anomaly.AnomalyType.BLUETOOTH_SCAN: case Anomaly.AnomalyType.BLUETOOTH_SCAN:
return new LocationCheckAction(mContext); return new LocationCheckAction(mContext);
default: default:

View File

@@ -18,8 +18,11 @@ package com.android.settings.fuelgauge.anomaly.action;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.support.annotation.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.anomaly.Anomaly;
/** /**
@@ -28,6 +31,8 @@ import com.android.settings.fuelgauge.anomaly.Anomaly;
public class BackgroundCheckAction extends AnomalyAction { public class BackgroundCheckAction extends AnomalyAction {
private AppOpsManager mAppOpsManager; private AppOpsManager mAppOpsManager;
@VisibleForTesting
BatteryUtils mBatteryUtils;
public BackgroundCheckAction(Context context) { public BackgroundCheckAction(Context context) {
super(context); super(context);
@@ -38,17 +43,17 @@ public class BackgroundCheckAction extends AnomalyAction {
@Override @Override
public void handlePositiveAction(Anomaly anomaly, int contextMetricsKey) { public void handlePositiveAction(Anomaly anomaly, int contextMetricsKey) {
super.handlePositiveAction(anomaly, contextMetricsKey); super.handlePositiveAction(anomaly, contextMetricsKey);
if (anomaly.targetSdkVersion < Build.VERSION_CODES.O) {
mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, anomaly.uid, anomaly.packageName, mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, anomaly.uid,
anomaly.packageName,
AppOpsManager.MODE_IGNORED); AppOpsManager.MODE_IGNORED);
} }
}
@Override @Override
public boolean isActionActive(Anomaly anomaly) { public boolean isActionActive(Anomaly anomaly) {
final int mode = mAppOpsManager return !mBatteryUtils.isBackgroundRestrictionEnabled(anomaly.targetSdkVersion, anomaly.uid,
.checkOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND, anomaly.uid,
anomaly.packageName); anomaly.packageName);
return mode != AppOpsManager.MODE_IGNORED && mode != AppOpsManager.MODE_ERRORED;
} }
@Override @Override

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.anomaly.action;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.anomaly.Anomaly;
/**
* Force stop and background check action for anomaly app, this action will
* 1. Force stop the app
* 2. Turn on background check
*/
public class StopAndBackgroundCheckAction extends AnomalyAction {
@VisibleForTesting
ForceStopAction mForceStopAction;
@VisibleForTesting
BackgroundCheckAction mBackgroundCheckAction;
public StopAndBackgroundCheckAction(Context context) {
this(context, new ForceStopAction(context), new BackgroundCheckAction(context));
}
@VisibleForTesting
StopAndBackgroundCheckAction(Context context, ForceStopAction forceStopAction,
BackgroundCheckAction backgroundCheckAction) {
super(context);
mForceStopAction = forceStopAction;
mBackgroundCheckAction = backgroundCheckAction;
}
@Override
public void handlePositiveAction(Anomaly anomaly, int metricsKey) {
mForceStopAction.handlePositiveAction(anomaly, metricsKey);
mBackgroundCheckAction.handlePositiveAction(anomaly, metricsKey);
}
@Override
public boolean isActionActive(Anomaly anomaly) {
return mForceStopAction.isActionActive(anomaly)
&& mBackgroundCheckAction.isActionActive(anomaly);
}
@Override
public int getActionType() {
return Anomaly.AnomalyActionType.STOP_AND_BACKGROUND_CHECK;
}
}

View File

@@ -42,24 +42,21 @@ public class BluetoothScanAnomalyDetector implements AnomalyDetector {
private static final String TAG = "BluetoothScanAnomalyDetector"; private static final String TAG = "BluetoothScanAnomalyDetector";
@VisibleForTesting @VisibleForTesting
BatteryUtils mBatteryUtils; BatteryUtils mBatteryUtils;
@VisibleForTesting
AnomalyAction mAnomalyAction;
private long mBluetoothScanningThreshold; private long mBluetoothScanningThreshold;
private Context mContext; private Context mContext;
private AnomalyUtils mAnomalyUtils;
public BluetoothScanAnomalyDetector(Context context) { public BluetoothScanAnomalyDetector(Context context) {
this(context, new AnomalyDetectionPolicy(context), this(context, new AnomalyDetectionPolicy(context), AnomalyUtils.getInstance(context));
AnomalyUtils.getInstance(context).getAnomalyAction(
Anomaly.AnomalyType.BLUETOOTH_SCAN));
} }
@VisibleForTesting @VisibleForTesting
BluetoothScanAnomalyDetector(Context context, AnomalyDetectionPolicy policy, BluetoothScanAnomalyDetector(Context context, AnomalyDetectionPolicy policy,
AnomalyAction anomalyAction) { AnomalyUtils anomalyUtils) {
mContext = context; mContext = context;
mBatteryUtils = BatteryUtils.getInstance(context); mBatteryUtils = BatteryUtils.getInstance(context);
mAnomalyAction = anomalyAction;
mBluetoothScanningThreshold = policy.bluetoothScanThreshold; mBluetoothScanningThreshold = policy.bluetoothScanThreshold;
mAnomalyUtils = anomalyUtils;
} }
@Override @Override
@@ -98,7 +95,7 @@ public class BluetoothScanAnomalyDetector implements AnomalyDetector {
.setPackageName(packageName) .setPackageName(packageName)
.build(); .build();
if (mAnomalyAction.isActionActive(anomaly)) { if (mAnomalyUtils.getAnomalyAction(anomaly).isActionActive(anomaly)) {
anomalies.add(anomaly); anomalies.add(anomaly);
} }
} }

View File

@@ -29,7 +29,6 @@ import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy; import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils; import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -39,27 +38,25 @@ import java.util.List;
*/ */
public class WakeLockAnomalyDetector implements AnomalyDetector { public class WakeLockAnomalyDetector implements AnomalyDetector {
private static final String TAG = "WakeLockAnomalyChecker"; private static final String TAG = "WakeLockAnomalyChecker";
private PackageManager mPackageManager;
private Context mContext;
@VisibleForTesting @VisibleForTesting
BatteryUtils mBatteryUtils; BatteryUtils mBatteryUtils;
@VisibleForTesting @VisibleForTesting
long mWakeLockThresholdMs; long mWakeLockThresholdMs;
@VisibleForTesting private PackageManager mPackageManager;
AnomalyAction mAnomalyAction; private Context mContext;
private AnomalyUtils mAnomalyUtils;
public WakeLockAnomalyDetector(Context context) { public WakeLockAnomalyDetector(Context context) {
this(context, new AnomalyDetectionPolicy(context)); this(context, new AnomalyDetectionPolicy(context), AnomalyUtils.getInstance(context));
} }
@VisibleForTesting @VisibleForTesting
WakeLockAnomalyDetector(Context context, AnomalyDetectionPolicy policy) { WakeLockAnomalyDetector(Context context, AnomalyDetectionPolicy policy,
AnomalyUtils anomalyUtils) {
mContext = context; mContext = context;
mPackageManager = context.getPackageManager(); mPackageManager = context.getPackageManager();
mBatteryUtils = BatteryUtils.getInstance(context); mBatteryUtils = BatteryUtils.getInstance(context);
mAnomalyAction = AnomalyUtils.getInstance(context).getAnomalyAction( mAnomalyUtils = anomalyUtils;
Anomaly.AnomalyType.WAKE_LOCK);
mWakeLockThresholdMs = policy.wakeLockThreshold; mWakeLockThresholdMs = policy.wakeLockThreshold;
} }
@@ -94,15 +91,20 @@ public class WakeLockAnomalyDetector implements AnomalyDetector {
final String packageName = mBatteryUtils.getPackageName(uid.getUid()); final String packageName = mBatteryUtils.getPackageName(uid.getUid());
final CharSequence displayName = Utils.getApplicationLabel(mContext, final CharSequence displayName = Utils.getApplicationLabel(mContext,
packageName); packageName);
final int targetSdkVersion = mBatteryUtils.getTargetSdkVersion(packageName);
Anomaly anomaly = new Anomaly.Builder() Anomaly anomaly = new Anomaly.Builder()
.setUid(uid.getUid()) .setUid(uid.getUid())
.setType(Anomaly.AnomalyType.WAKE_LOCK) .setType(Anomaly.AnomalyType.WAKE_LOCK)
.setDisplayName(displayName) .setDisplayName(displayName)
.setPackageName(packageName) .setPackageName(packageName)
.setTargetSdkVersion(targetSdkVersion)
.setBackgroundRestrictionEnabled(
mBatteryUtils.isBackgroundRestrictionEnabled(targetSdkVersion,
uid.getUid(), packageName))
.build(); .build();
if (mAnomalyAction.isActionActive(anomaly)) { if (mAnomalyUtils.getAnomalyAction(anomaly).isActionActive(anomaly)) {
anomalies.add(anomaly); anomalies.add(anomaly);
} }
} }

View File

@@ -41,21 +41,20 @@ public class WakeupAlarmAnomalyDetector implements AnomalyDetector {
private static final String TAG = "WakeupAlarmAnomalyDetector"; private static final String TAG = "WakeupAlarmAnomalyDetector";
@VisibleForTesting @VisibleForTesting
BatteryUtils mBatteryUtils; BatteryUtils mBatteryUtils;
@VisibleForTesting
AnomalyAction mAnomalyAction;
private long mWakeupAlarmThreshold; private long mWakeupAlarmThreshold;
private Context mContext; private Context mContext;
private AnomalyUtils mAnomalyUtils;
public WakeupAlarmAnomalyDetector(Context context) { public WakeupAlarmAnomalyDetector(Context context) {
this(context, new AnomalyDetectionPolicy(context)); this(context, new AnomalyDetectionPolicy(context), AnomalyUtils.getInstance(context));
} }
@VisibleForTesting @VisibleForTesting
WakeupAlarmAnomalyDetector(Context context, AnomalyDetectionPolicy policy) { WakeupAlarmAnomalyDetector(Context context, AnomalyDetectionPolicy policy,
AnomalyUtils anomalyUtils) {
mContext = context; mContext = context;
mBatteryUtils = BatteryUtils.getInstance(context); mBatteryUtils = BatteryUtils.getInstance(context);
mAnomalyAction = AnomalyUtils.getInstance(context).getAnomalyAction( mAnomalyUtils = anomalyUtils;
Anomaly.AnomalyType.WAKEUP_ALARM);
mWakeupAlarmThreshold = policy.wakeupAlarmThreshold; mWakeupAlarmThreshold = policy.wakeupAlarmThreshold;
} }
@@ -98,7 +97,7 @@ public class WakeupAlarmAnomalyDetector implements AnomalyDetector {
.setPackageName(packageName) .setPackageName(packageName)
.build(); .build();
if (mAnomalyAction.isActionActive(anomaly)) { if (mAnomalyUtils.getAnomalyAction(anomaly).isActionActive(anomaly)) {
anomalies.add(anomaly); anomalies.add(anomaly);
} }
} }

View File

@@ -18,6 +18,7 @@ package com.android.settings.applications;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.AppOpsManager;
import android.app.Fragment; import android.app.Fragment;
import android.app.LoaderManager; import android.app.LoaderManager;
import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManager;
@@ -113,6 +114,8 @@ public final class InstalledAppDetailsTest {
private BatteryUtils mBatteryUtils; private BatteryUtils mBatteryUtils;
@Mock @Mock
private LoaderManager mLoaderManager; private LoaderManager mLoaderManager;
@Mock
private AppOpsManager mAppOpsManager;
private FakeFeatureFactory mFeatureFactory; private FakeFeatureFactory mFeatureFactory;
private InstalledAppDetails mAppDetail; private InstalledAppDetails mAppDetail;
@@ -138,6 +141,7 @@ public final class InstalledAppDetailsTest {
doReturn(mActivity).when(mAppDetail).getActivity(); doReturn(mActivity).when(mAppDetail).getActivity();
doReturn(mShadowContext).when(mAppDetail).getContext(); doReturn(mShadowContext).when(mAppDetail).getContext();
doReturn(mPackageManager).when(mActivity).getPackageManager(); doReturn(mPackageManager).when(mActivity).getPackageManager();
doReturn(mAppOpsManager).when(mActivity).getSystemService(Context.APP_OPS_SERVICE);
// Default to not considering any apps to be instant (individual tests can override this). // Default to not considering any apps to be instant (individual tests can override this).
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",

View File

@@ -30,6 +30,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.app.Activity; import android.app.Activity;
import android.app.AppOpsManager;
import android.app.Fragment; import android.app.Fragment;
import android.app.LoaderManager; import android.app.LoaderManager;
import android.content.Context; import android.content.Context;
@@ -116,6 +117,8 @@ public class AdvancedPowerUsageDetailTest {
@Mock @Mock
private PackageManager mPackageManager; private PackageManager mPackageManager;
@Mock @Mock
private AppOpsManager mAppOpsManager;
@Mock
private LoaderManager mLoaderManager; private LoaderManager mLoaderManager;
@Mock @Mock
private AnomalySummaryPreferenceController mAnomalySummaryPreferenceController; private AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
@@ -184,6 +187,7 @@ public class AdvancedPowerUsageDetailTest {
mTestActivity = spy(new SettingsActivity()); mTestActivity = spy(new SettingsActivity());
doReturn(mPackageManager).when(mTestActivity).getPackageManager(); doReturn(mPackageManager).when(mTestActivity).getPackageManager();
doReturn(mAppOpsManager).when(mTestActivity).getSystemService(Context.APP_OPS_SERVICE);
final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class); final ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);

View File

@@ -15,9 +15,13 @@
*/ */
package com.android.settings.fuelgauge; package com.android.settings.fuelgauge;
import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.BatteryStats; import android.os.BatteryStats;
import android.os.Bundle; import android.os.Bundle;
import android.os.Build;
import android.os.Process; import android.os.Process;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserManager; import android.os.UserManager;
@@ -58,6 +62,7 @@ import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -96,6 +101,8 @@ public class BatteryUtilsTest {
private static final int DISCHARGE_AMOUNT = 80; private static final int DISCHARGE_AMOUNT = 80;
private static final double PERCENT_SYSTEM_USAGE = 60; private static final double PERCENT_SYSTEM_USAGE = 60;
private static final double PRECISION = 0.001; private static final double PRECISION = 0.001;
private static final int SDK_VERSION = Build.VERSION_CODES.L;
private static final String PACKAGE_NAME = "com.android.app";
@Mock @Mock
private BatteryStats.Uid mUid; private BatteryStats.Uid mUid;
@@ -123,6 +130,12 @@ public class BatteryUtilsTest {
private Bundle mBundle; private Bundle mBundle;
@Mock @Mock
private UserManager mUserManager; private UserManager mUserManager;
@Mock
private PackageManager mPackageManager;
@Mock
private AppOpsManager mAppOpsManager;
@Mock
private ApplicationInfo mApplicationInfo;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext; private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -177,10 +190,11 @@ public class BatteryUtilsTest {
mIdleBatterySipper.drainType = BatterySipper.DrainType.IDLE; mIdleBatterySipper.drainType = BatterySipper.DrainType.IDLE;
mIdleBatterySipper.totalPowerMah = BATTERY_IDLE_USAGE; mIdleBatterySipper.totalPowerMah = BATTERY_IDLE_USAGE;
mBatteryUtils = BatteryUtils.getInstance(RuntimeEnvironment.application); final Context shadowContext = spy(RuntimeEnvironment.application);
doReturn(mPackageManager).when(shadowContext).getPackageManager();
doReturn(mAppOpsManager).when(shadowContext).getSystemService(Context.APP_OPS_SERVICE);
mBatteryUtils = spy(new BatteryUtils(shadowContext));
mBatteryUtils.mPowerUsageFeatureProvider = mProvider; mBatteryUtils.mPowerUsageFeatureProvider = mProvider;
mBatteryUtils = spy(new BatteryUtils(RuntimeEnvironment.application));
} }
@Test @Test
@@ -392,6 +406,44 @@ public class BatteryUtilsTest {
TIME_SINCE_LAST_FULL_CHARGE_US); TIME_SINCE_LAST_FULL_CHARGE_US);
} }
@Test
public void testGetTargetSdkVersion_packageExist_returnSdk() throws
PackageManager.NameNotFoundException {
doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(PACKAGE_NAME,
PackageManager.GET_META_DATA);
mApplicationInfo.targetSdkVersion = SDK_VERSION;
assertThat(mBatteryUtils.getTargetSdkVersion(PACKAGE_NAME)).isEqualTo(SDK_VERSION);
}
@Test
public void testGetTargetSdkVersion_packageNotExist_returnSdkNull() throws
PackageManager.NameNotFoundException {
doThrow(new PackageManager.NameNotFoundException()).when(
mPackageManager).getApplicationInfo(PACKAGE_NAME, PackageManager.GET_META_DATA);
assertThat(mBatteryUtils.getTargetSdkVersion(PACKAGE_NAME)).isEqualTo(
BatteryUtils.SDK_NULL);
}
@Test
public void testBackgroundRestrictionOn_restrictionOn_returnTrue() {
doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManager).checkOpNoThrow(
AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME);
assertThat(mBatteryUtils.isBackgroundRestrictionEnabled(SDK_VERSION, UID,
PACKAGE_NAME)).isTrue();
}
@Test
public void testBackgroundRestrictionOn_restrictionOff_returnFalse() {
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).checkOpNoThrow(
AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME);
assertThat(mBatteryUtils.isBackgroundRestrictionEnabled(SDK_VERSION, UID,
PACKAGE_NAME)).isFalse();
}
private BatterySipper createTestSmearBatterySipper(long topTime, private BatterySipper createTestSmearBatterySipper(long topTime,
double totalPowerMah, int uidCode, boolean isUidNull) { double totalPowerMah, int uidCode, boolean isUidNull) {
final BatterySipper sipper = mock(BatterySipper.class); final BatterySipper sipper = mock(BatterySipper.class);

View File

@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.anomaly;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
@@ -27,6 +28,7 @@ import static org.robolectric.Shadows.shadowOf;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.os.Build;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction; import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
@@ -57,6 +59,7 @@ public class AnomalyDialogFragmentTest {
private AnomalyAction mAnomalyAction; private AnomalyAction mAnomalyAction;
private Anomaly mWakeLockAnomaly; private Anomaly mWakeLockAnomaly;
private Anomaly mWakeupAlarmAnomaly; private Anomaly mWakeupAlarmAnomaly;
private Anomaly mWakeupAlarmAnomaly2;
private Anomaly mBluetoothAnomaly; private Anomaly mBluetoothAnomaly;
private AnomalyDialogFragment mAnomalyDialogFragment; private AnomalyDialogFragment mAnomalyDialogFragment;
private Context mContext; private Context mContext;
@@ -78,6 +81,13 @@ public class AnomalyDialogFragmentTest {
.setPackageName(PACKAGE_NAME) .setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME) .setDisplayName(DISPLAY_NAME)
.build(); .build();
mWakeupAlarmAnomaly2 = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setUid(UID)
.setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.build();
mBluetoothAnomaly = new Anomaly.Builder() mBluetoothAnomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.BLUETOOTH_SCAN) .setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
.setUid(UID) .setUid(UID)
@@ -116,7 +126,7 @@ public class AnomalyDialogFragmentTest {
} }
@Test @Test
public void testOnCreateDialog_wakeupAlarmAnomaly_fireBackgroundCheckDialog() { public void testOnCreateDialog_wakeupAlarmAnomalyPriorO_fireStopAndBackgroundCheckDialog() {
mAnomalyDialogFragment = AnomalyDialogFragment.newInstance(mWakeupAlarmAnomaly, mAnomalyDialogFragment = AnomalyDialogFragment.newInstance(mWakeupAlarmAnomaly,
0 /* metricskey */); 0 /* metricskey */);
@@ -136,12 +146,33 @@ public class AnomalyDialogFragmentTest {
mContext.getString(R.string.dlg_cancel)); mContext.getString(R.string.dlg_cancel));
} }
@Test
public void testOnCreateDialog_wakeupAlarmAnomalyTargetingO_fireForceStopDialog() {
mAnomalyDialogFragment = AnomalyDialogFragment.newInstance(mWakeupAlarmAnomaly2,
0 /* metricskey */);
FragmentTestUtil.startFragment(mAnomalyDialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
ShadowAlertDialog shadowDialog = shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
mContext.getString(R.string.dialog_stop_message_wakeup_alarm,
mWakeLockAnomaly.displayName));
assertThat(shadowDialog.getTitle()).isEqualTo(
mContext.getString(R.string.dialog_stop_title));
assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo(
mContext.getString(R.string.dialog_stop_ok));
assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
mContext.getString(R.string.dlg_cancel));
}
@Test @Test
public void testOnCreateDialog_bluetoothAnomaly_fireLocationCheckDialog() { public void testOnCreateDialog_bluetoothAnomaly_fireLocationCheckDialog() {
mAnomalyDialogFragment = spy(AnomalyDialogFragment.newInstance(mBluetoothAnomaly, mAnomalyDialogFragment = spy(AnomalyDialogFragment.newInstance(mBluetoothAnomaly,
0 /* metricskey */)); 0 /* metricskey */));
mAnomalyDialogFragment.mAnomalyUtils = mAnomalyUtils; mAnomalyDialogFragment.mAnomalyUtils = mAnomalyUtils;
doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(anyInt()); doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any());
doNothing().when(mAnomalyDialogFragment).initAnomalyUtils(); doNothing().when(mAnomalyDialogFragment).initAnomalyUtils();
doReturn(Anomaly.AnomalyActionType.LOCATION_CHECK).when(mAnomalyAction).getActionType(); doReturn(Anomaly.AnomalyActionType.LOCATION_CHECK).when(mAnomalyAction).getActionType();

View File

@@ -18,6 +18,8 @@ package com.android.settings.fuelgauge.anomaly;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.os.Build;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
@@ -30,6 +32,7 @@ import org.robolectric.annotation.Config;
public class AnomalyTest { public class AnomalyTest {
private static int TYPE = Anomaly.AnomalyType.WAKE_LOCK; private static int TYPE = Anomaly.AnomalyType.WAKE_LOCK;
private static int UID = 111; private static int UID = 111;
private static int SDK_VERSION = Build.VERSION_CODES.L;
private static long WAKE_LOCK_TIME_MS = 1500; private static long WAKE_LOCK_TIME_MS = 1500;
private static String PACKAGE_NAME = "com.android.settings"; private static String PACKAGE_NAME = "com.android.settings";
private static String DISPLAY_NAME = "settings"; private static String DISPLAY_NAME = "settings";
@@ -42,6 +45,8 @@ public class AnomalyTest {
.setWakeLockTimeMs(WAKE_LOCK_TIME_MS) .setWakeLockTimeMs(WAKE_LOCK_TIME_MS)
.setPackageName(PACKAGE_NAME) .setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME) .setDisplayName(DISPLAY_NAME)
.setTargetSdkVersion(SDK_VERSION)
.setBackgroundRestrictionEnabled(true)
.build(); .build();
assertThat(anomaly.type).isEqualTo(TYPE); assertThat(anomaly.type).isEqualTo(TYPE);
@@ -49,5 +54,7 @@ public class AnomalyTest {
assertThat(anomaly.wakelockTimeMs).isEqualTo(WAKE_LOCK_TIME_MS); assertThat(anomaly.wakelockTimeMs).isEqualTo(WAKE_LOCK_TIME_MS);
assertThat(anomaly.packageName).isEqualTo(PACKAGE_NAME); assertThat(anomaly.packageName).isEqualTo(PACKAGE_NAME);
assertThat(anomaly.displayName).isEqualTo(DISPLAY_NAME); assertThat(anomaly.displayName).isEqualTo(DISPLAY_NAME);
assertThat(anomaly.targetSdkVersion).isEqualTo(SDK_VERSION);
assertThat(anomaly.backgroundRestrictionEnabled).isTrue();
} }
} }

View File

@@ -18,9 +18,11 @@ package com.android.settings.fuelgauge.anomaly;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.os.Build;
import com.android.settings.fuelgauge.anomaly.action.StopAndBackgroundCheckAction;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.anomaly.action.BackgroundCheckAction;
import com.android.settings.fuelgauge.anomaly.action.ForceStopAction; import com.android.settings.fuelgauge.anomaly.action.ForceStopAction;
import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector; import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
import com.android.settings.testutils.shadow.ShadowKeyValueListParserWrapperImpl; import com.android.settings.testutils.shadow.ShadowKeyValueListParserWrapperImpl;
@@ -45,22 +47,51 @@ public class AnomalyUtilsTest {
@Test @Test
public void testGetAnomalyAction_typeWakeLock_returnForceStop() { public void testGetAnomalyAction_typeWakeLock_returnForceStop() {
assertThat(mAnomalyUtils.getAnomalyAction(Anomaly.AnomalyType.WAKE_LOCK)).isInstanceOf( Anomaly anomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.build();
assertThat(mAnomalyUtils.getAnomalyAction(anomaly)).isInstanceOf(
ForceStopAction.class); ForceStopAction.class);
} }
@Test
public void testGetAnomalyAction_typeWakeUpAlarm_returnBackgroundCheck() {
assertThat(mAnomalyUtils.getAnomalyAction(Anomaly.AnomalyType.WAKEUP_ALARM)).isInstanceOf(
BackgroundCheckAction.class);
}
@Test @Test
public void testGetAnomalyDetector_typeWakeLock_returnWakeLockDetector() { public void testGetAnomalyDetector_typeWakeLock_returnWakeLockDetector() {
assertThat(mAnomalyUtils.getAnomalyDetector(Anomaly.AnomalyType.WAKE_LOCK)).isInstanceOf( assertThat(mAnomalyUtils.getAnomalyDetector(Anomaly.AnomalyType.WAKE_LOCK)).isInstanceOf(
WakeLockAnomalyDetector.class); WakeLockAnomalyDetector.class);
} }
@Test
public void testGetAnomalyAction_typeWakeUpAlarmTargetO_returnForceStop() {
Anomaly anomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setTargetSdkVersion(Build.VERSION_CODES.O)
.build();
assertThat(mAnomalyUtils.getAnomalyAction(anomaly)).isInstanceOf(
ForceStopAction.class);
}
@Test
public void testGetAnomalyAction_typeWakeUpAlarmTargetPriorOAndBgOff_returnStopAndBackground() {
Anomaly anomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setTargetSdkVersion(Build.VERSION_CODES.L)
.setBackgroundRestrictionEnabled(false)
.build();
assertThat(mAnomalyUtils.getAnomalyAction(anomaly)).isInstanceOf(
StopAndBackgroundCheckAction.class);
}
@Test
public void testGetAnomalyAction_typeWakeUpAlarmTargetPriorOAndBgOn_returnForceStop() {
Anomaly anomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setTargetSdkVersion(Build.VERSION_CODES.L)
.setBackgroundRestrictionEnabled(true)
.build();
assertThat(mAnomalyUtils.getAnomalyAction(anomaly)).isInstanceOf(
ForceStopAction.class);
}
@Test @Test
public void testGetAnomalyDetector_typeWakeUpAlarm_returnWakeUpAlarmDetector() { public void testGetAnomalyDetector_typeWakeUpAlarm_returnWakeUpAlarmDetector() {
assertThat(mAnomalyUtils.getAnomalyDetector(Anomaly.AnomalyType.WAKEUP_ALARM)).isInstanceOf( assertThat(mAnomalyUtils.getAnomalyDetector(Anomaly.AnomalyType.WAKEUP_ALARM)).isInstanceOf(

View File

@@ -23,7 +23,9 @@ import static org.mockito.Mockito.verify;
import android.app.AppOpsManager; import android.app.AppOpsManager;
import android.content.Context; import android.content.Context;
import android.os.Build;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.anomaly.Anomaly;
@@ -42,11 +44,14 @@ import org.robolectric.annotation.Config;
public class BackgroundCheckActionTest { public class BackgroundCheckActionTest {
private static final String PACKAGE_NAME = "com.android.app"; private static final String PACKAGE_NAME = "com.android.app";
private static final int UID = 111; private static final int UID = 111;
private static final int SDK_VERSION = Build.VERSION_CODES.L;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext; private Context mContext;
@Mock @Mock
private AppOpsManager mAppOpsManagerr; private AppOpsManager mAppOpsManager;
@Mock
private BatteryUtils mBatteryUtils;
private Anomaly mAnomaly; private Anomaly mAnomaly;
private BackgroundCheckAction mBackgroundCheckAction; private BackgroundCheckAction mBackgroundCheckAction;
@@ -55,35 +60,37 @@ public class BackgroundCheckActionTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest(mContext); FakeFeatureFactory.setupForTest(mContext);
doReturn(mAppOpsManagerr).when(mContext).getSystemService(Context.APP_OPS_SERVICE); doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
mAnomaly = new Anomaly.Builder() mAnomaly = new Anomaly.Builder()
.setUid(UID) .setUid(UID)
.setPackageName(PACKAGE_NAME) .setPackageName(PACKAGE_NAME)
.setTargetSdkVersion(SDK_VERSION)
.build(); .build();
mBackgroundCheckAction = new BackgroundCheckAction(mContext); mBackgroundCheckAction = new BackgroundCheckAction(mContext);
mBackgroundCheckAction.mBatteryUtils = mBatteryUtils;
} }
@Test @Test
public void testHandlePositiveAction_forceStopPackage() { public void testHandlePositiveAction_forceStopPackage() {
mBackgroundCheckAction.handlePositiveAction(mAnomaly, 0 /* metricskey */); mBackgroundCheckAction.handlePositiveAction(mAnomaly, 0 /* metricskey */);
verify(mAppOpsManagerr).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME, verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME,
AppOpsManager.MODE_IGNORED); AppOpsManager.MODE_IGNORED);
} }
@Test @Test
public void testIsActionActive_modeAllowed_returnTrue() { public void testIsActionActive_modeAllowed_returnTrue() {
doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManagerr).checkOpNoThrow( doReturn(false).when(mBatteryUtils).isBackgroundRestrictionEnabled(SDK_VERSION, UID,
AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME); PACKAGE_NAME);
assertThat(mBackgroundCheckAction.isActionActive(mAnomaly)).isTrue(); assertThat(mBackgroundCheckAction.isActionActive(mAnomaly)).isTrue();
} }
@Test @Test
public void testIsActionActive_modeIgnored_returnFalse() { public void testIsActionActive_modeIgnored_returnFalse() {
doReturn(AppOpsManager.MODE_IGNORED).when(mAppOpsManagerr).checkOpNoThrow( doReturn(true).when(mBatteryUtils).isBackgroundRestrictionEnabled(SDK_VERSION, UID,
AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME); PACKAGE_NAME);
assertThat(mBackgroundCheckAction.isActionActive(mAnomaly)).isFalse(); assertThat(mBackgroundCheckAction.isActionActive(mAnomaly)).isFalse();
} }

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.fuelgauge.anomaly.action;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.content.Context;
import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class StopAndBackgroundActionTest {
private static final String PACKAGE_NAME = "com.android.app";
private static final int UID = 111;
private static final int METRICS_KEY = 3;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private BackgroundCheckAction mBackgroundCheckAction;
@Mock
private ForceStopAction mForceStopAction;
private StopAndBackgroundCheckAction mStopAndBackgroundCheckAction;
private Anomaly mAnomaly;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mAnomaly = new Anomaly.Builder()
.setUid(UID)
.setPackageName(PACKAGE_NAME)
.build();
FakeFeatureFactory.setupForTest(mContext);
mStopAndBackgroundCheckAction = new StopAndBackgroundCheckAction(mContext, mForceStopAction,
mBackgroundCheckAction);
}
@Test
public void testHandlePositiveAction_stopAndBackgroundCheck() {
mStopAndBackgroundCheckAction.handlePositiveAction(mAnomaly, METRICS_KEY);
verify(mBackgroundCheckAction).handlePositiveAction(mAnomaly, METRICS_KEY);
verify(mForceStopAction).handlePositiveAction(mAnomaly, METRICS_KEY);
}
@Test
public void testIsActionActive_restrictionEnabled_returnFalse() {
doReturn(true).when(mForceStopAction).isActionActive(mAnomaly);
assertThat(mStopAndBackgroundCheckAction.isActionActive(mAnomaly)).isFalse();
}
@Test
public void testIsActionActive_appNotRunning_returnFalse() {
doReturn(true).when(mBackgroundCheckAction).isActionActive(mAnomaly);
assertThat(mStopAndBackgroundCheckAction.isActionActive(mAnomaly)).isFalse();
}
@Test
public void testIsActionActive_appStoppedAndRestrictionOn_returnFalse() {
assertThat(mStopAndBackgroundCheckAction.isActionActive(mAnomaly)).isFalse();
}
}

View File

@@ -36,6 +36,7 @@ import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly; import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy; import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction; import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -80,6 +81,8 @@ public class BluetoothScanAnomalyDetectorTest {
private AnomalyDetectionPolicy mPolicy; private AnomalyDetectionPolicy mPolicy;
@Mock @Mock
private AnomalyAction mAnomalyAction; private AnomalyAction mAnomalyAction;
@Mock
private AnomalyUtils mAnomalyUtils;
private BluetoothScanAnomalyDetector mBluetoothScanAnomalyDetector; private BluetoothScanAnomalyDetector mBluetoothScanAnomalyDetector;
private Context mContext; private Context mContext;
@@ -92,6 +95,7 @@ public class BluetoothScanAnomalyDetectorTest {
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
ReflectionHelpers.setField(mPolicy, "bluetoothScanThreshold", ReflectionHelpers.setField(mPolicy, "bluetoothScanThreshold",
30 * DateUtils.MINUTE_IN_MILLIS); 30 * DateUtils.MINUTE_IN_MILLIS);
doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any());
mAnomalySipper.uidObj = mAnomalyUid; mAnomalySipper.uidObj = mAnomalyUid;
doReturn(ANOMALY_UID).when(mAnomalyUid).getUid(); doReturn(ANOMALY_UID).when(mAnomalyUid).getUid();
@@ -106,10 +110,9 @@ public class BluetoothScanAnomalyDetectorTest {
mUsageList.add(mTargetSipper); mUsageList.add(mTargetSipper);
doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
mBluetoothScanAnomalyDetector = spy( mBluetoothScanAnomalyDetector = spy(new BluetoothScanAnomalyDetector(mContext, mPolicy,
new BluetoothScanAnomalyDetector(mContext, mPolicy, mAnomalyAction)); mAnomalyUtils));
mBluetoothScanAnomalyDetector.mBatteryUtils = mBatteryUtils; mBluetoothScanAnomalyDetector.mBatteryUtils = mBatteryUtils;
mBluetoothScanAnomalyDetector.mAnomalyAction = mAnomalyAction;
doReturn(false).when(mBatteryUtils).shouldHideSipper(any()); doReturn(false).when(mBatteryUtils).shouldHideSipper(any());
doReturn(true).when(mAnomalyAction).isActionActive(any()); doReturn(true).when(mAnomalyAction).isActionActive(any());

View File

@@ -34,6 +34,7 @@ import android.text.format.DateUtils;
import com.android.internal.os.BatterySipper; import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
@@ -92,6 +93,8 @@ public class WakeLockAnomalyDetectorTest {
private AnomalyDetectionPolicy mPolicy; private AnomalyDetectionPolicy mPolicy;
@Mock @Mock
private AnomalyAction mAnomalyAction; private AnomalyAction mAnomalyAction;
@Mock
private AnomalyUtils mAnomalyUtils;
private WakeLockAnomalyDetector mWakelockAnomalyDetector; private WakeLockAnomalyDetector mWakelockAnomalyDetector;
private Context mContext; private Context mContext;
@@ -109,10 +112,11 @@ public class WakeLockAnomalyDetectorTest {
doReturn(mApplicationInfo).when(mPackageManager) doReturn(mApplicationInfo).when(mPackageManager)
.getApplicationInfo(nullable(String.class), anyInt()); .getApplicationInfo(nullable(String.class), anyInt());
doReturn(true).when(mAnomalyAction).isActionActive(any()); doReturn(true).when(mAnomalyAction).isActionActive(any());
doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any());
mWakelockAnomalyDetector = spy(new WakeLockAnomalyDetector(mContext, mPolicy)); mWakelockAnomalyDetector = spy(
new WakeLockAnomalyDetector(mContext, mPolicy, mAnomalyUtils));
mWakelockAnomalyDetector.mBatteryUtils = mBatteryUtils; mWakelockAnomalyDetector.mBatteryUtils = mBatteryUtils;
mWakelockAnomalyDetector.mAnomalyAction = mAnomalyAction;
mAnomalySipper.uidObj = mAnomalyUid; mAnomalySipper.uidObj = mAnomalyUid;
doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector) doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector)

View File

@@ -33,6 +33,7 @@ import android.util.ArrayMap;
import com.android.internal.os.BatterySipper; import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig; import com.android.settings.TestConfig;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
@@ -89,6 +90,8 @@ public class WakeupAlarmAnomalyDetectorTest {
private AnomalyDetectionPolicy mPolicy; private AnomalyDetectionPolicy mPolicy;
@Mock @Mock
private AnomalyAction mAnomalyAction; private AnomalyAction mAnomalyAction;
@Mock
private AnomalyUtils mAnomalyUtils;
private WakeupAlarmAnomalyDetector mWakeupAlarmAnomalyDetector; private WakeupAlarmAnomalyDetector mWakeupAlarmAnomalyDetector;
private Context mContext; private Context mContext;
@@ -105,6 +108,7 @@ public class WakeupAlarmAnomalyDetectorTest {
doReturn(RUNNING_TIME_MS).when(mBatteryUtils).calculateRunningTimeBasedOnStatsType(any(), doReturn(RUNNING_TIME_MS).when(mBatteryUtils).calculateRunningTimeBasedOnStatsType(any(),
anyInt()); anyInt());
doReturn(true).when(mAnomalyAction).isActionActive(any()); doReturn(true).when(mAnomalyAction).isActionActive(any());
doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any());
mAnomalySipper.uidObj = mAnomalyUid; mAnomalySipper.uidObj = mAnomalyUid;
doReturn(ANOMALY_UID).when(mAnomalyUid).getUid(); doReturn(ANOMALY_UID).when(mAnomalyUid).getUid();
@@ -119,9 +123,9 @@ public class WakeupAlarmAnomalyDetectorTest {
mUsageList.add(mTargetSipper); mUsageList.add(mTargetSipper);
doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList(); doReturn(mUsageList).when(mBatteryStatsHelper).getUsageList();
mWakeupAlarmAnomalyDetector = spy(new WakeupAlarmAnomalyDetector(mContext, mPolicy)); mWakeupAlarmAnomalyDetector = spy(
new WakeupAlarmAnomalyDetector(mContext, mPolicy, mAnomalyUtils));
mWakeupAlarmAnomalyDetector.mBatteryUtils = mBatteryUtils; mWakeupAlarmAnomalyDetector.mBatteryUtils = mBatteryUtils;
mWakeupAlarmAnomalyDetector.mAnomalyAction = mAnomalyAction;
} }
@Test @Test