Remove the anomaly detection added in O-DR

1. Remove unused class and resources
2. Update function in AdvancedPowerUsageDetail since
we don't need pass in anomaly anymore

Change-Id: I96fbe5ddaae902b34b756c7aae0338e49afef1f3
Bug: 74529048
Test: 1. manual test in settings page. 2. robo test still pass
This commit is contained in:
jackqdyulei
2018-07-12 15:16:30 -07:00
parent baea63bf72
commit dd9b8af6b9
50 changed files with 23 additions and 4444 deletions

View File

@@ -4987,18 +4987,6 @@
<!-- Subtitle for list of packages -->
<string name="packages_subtitle">Included packages</string>
<!-- Activity title for battery abnormal details page [CHAR LIMIT=60] -->
<string name="battery_abnormal_details_title">Apps draining battery</string>
<!-- Summary for wakelock anomaly, means device been kept awake [CHAR LIMIT=60] -->
<string name="battery_abnormal_wakelock_summary">Keeping device awake</string>
<!-- Summary for wakeup alarm anomaly, meaning device been wakeup in the background [CHAR LIMIT=60] -->
<string name="battery_abnormal_wakeup_alarm_summary">Waking up device in background</string>
<!-- Summary for location anomaly, meaning device is requesting location too frequently [CHAR LIMIT=60] -->
<string name="battery_abnormal_location_summary">Requesting location frequently</string>
<!-- Summary for anomalies, meaning some apps behaving abnormally [CHAR LIMIT=80] -->
<string name="battery_abnormal_apps_summary"><xliff:g id="number">%1$d</xliff:g> apps misbehaving</string>
<!-- Title for the battery summary tip [CHAR LIMIT=NONE] -->
<string name="battery_tip_summary_title">Apps are running normally</string>
<!-- Summary for the battery summary tip [CHAR LIMIT=NONE] -->
@@ -8509,9 +8497,6 @@
<!-- Label for menu to show app usage [CHAR LIMIT=30] -->
<string name="hide_extra_apps">Show app usage</string>
<!-- Title for high usage item, which means power high usage [CHAR LIMIT=30] -->
<string name="power_high_usage_title">High usage</string>
<!-- Summary for high usage item, showing app/apps are behaving abnormally [CHAR LIMIT=80] -->
<plurals name="power_high_usage_summary">
<item quantity="one"><xliff:g id="app">%1$s</xliff:g> behaving abnormally</item>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/battery_abnormal_details_title">
<PreferenceCategory
android:key="app_abnormal_list"/>
</PreferenceScreen>

View File

@@ -43,11 +43,6 @@
</PreferenceCategory>
<Preference
android:key="high_usage"
android:icon="@drawable/ic_battery_alert_24dp"
android:title="@string/power_high_usage_title"/>
<PreferenceCategory
android:title="@string/battery_detail_info_title">

View File

@@ -99,13 +99,11 @@ public class AppBatteryPreferenceController extends BasePreferenceController
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
final BatteryEntry entry = new BatteryEntry(mContext, null, userManager, mSipper);
entry.defaultPackageName = mPackageName;
AdvancedPowerUsageDetail.startBatteryDetailPage(
(SettingsActivity) mParent.getActivity(), mParent, mBatteryHelper,
BatteryStats.STATS_SINCE_CHARGED, entry, mBatteryPercent,
null /* mAnomalies */);
AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, mBatteryPercent);
} else {
AdvancedPowerUsageDetail.startBatteryDetailPage(
(SettingsActivity) mParent.getActivity(), mParent, mPackageName);
AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
mPackageName);
}
return true;
}

View File

@@ -42,11 +42,6 @@ import com.android.settings.applications.appinfo.ButtonActionDialogFragment;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment;
import com.android.settings.fuelgauge.anomaly.AnomalyLoader;
import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.widget.EntityHeaderController;
@@ -71,8 +66,6 @@ import androidx.preference.Preference;
*/
public class AdvancedPowerUsageDetail extends DashboardFragment implements
ButtonActionDialogFragment.AppButtonsDialogListener,
AnomalyDialogFragment.AnomalyDialogListener,
LoaderManager.LoaderCallbacks<List<Anomaly>>,
BatteryTipPreferenceController.BatteryTipListener {
public static final String TAG = "AdvancedPowerDetail";
@@ -84,7 +77,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
public static final String EXTRA_ICON_ID = "extra_icon_id";
public static final String EXTRA_POWER_USAGE_PERCENT = "extra_power_usage_percent";
public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount";
public static final String EXTRA_ANOMALY_LIST = "extra_anomaly_list";
private static final String KEY_PREF_FOREGROUND = "app_usage_foreground";
private static final String KEY_PREF_BACKGROUND = "app_usage_background";
@@ -93,8 +85,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
private static final int REQUEST_UNINSTALL = 0;
private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
private static final int ANOMALY_LOADER = 0;
@VisibleForTesting
LayoutPreference mHeaderPreference;
@VisibleForTesting
@@ -108,18 +98,15 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
Preference mForegroundPreference;
@VisibleForTesting
Preference mBackgroundPreference;
@VisibleForTesting
AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
private AppButtonsPreferenceController mAppButtonsPreferenceController;
private BackgroundActivityPreferenceController mBackgroundActivityPreferenceController;
private List<Anomaly> mAnomalies;
private String mPackageName;
@VisibleForTesting
static void startBatteryDetailPage(Activity caller, BatteryUtils batteryUtils,
InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which,
BatteryEntry entry, String usagePercent, List<Anomaly> anomalies) {
BatteryEntry entry, String usagePercent) {
// Initialize mStats if necessary.
helper.getStats();
@@ -150,7 +137,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
args.putLong(EXTRA_FOREGROUND_TIME, foregroundTimeMs);
args.putString(EXTRA_POWER_USAGE_PERCENT, usagePercent);
args.putInt(EXTRA_POWER_USAGE_AMOUNT, (int) sipper.totalPowerMah);
args.putParcelableList(EXTRA_ANOMALY_LIST, anomalies);
new SubSettingLauncher(caller)
.setDestination(AdvancedPowerUsageDetail.class.getName())
@@ -171,9 +157,9 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
public static void startBatteryDetailPage(Activity caller,
InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which,
BatteryEntry entry, String usagePercent, List<Anomaly> anomalies) {
BatteryEntry entry, String usagePercent) {
startBatteryDetailPage(caller, BatteryUtils.getInstance(caller), fragment, helper, which,
entry, usagePercent, anomalies);
entry, usagePercent);
}
public static void startBatteryDetailPage(Activity caller,
@@ -209,15 +195,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
super.onCreate(icicle);
mPackageName = getArguments().getString(EXTRA_PACKAGE_NAME);
mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController(
(SettingsActivity) getActivity(), this);
mForegroundPreference = findPreference(KEY_PREF_FOREGROUND);
mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
if (mPackageName != null) {
mAppEntry = mState.getEntry(mPackageName, UserHandle.myUserId());
initAnomalyInfo();
}
}
@@ -229,16 +212,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
initPreference();
}
@VisibleForTesting
void initAnomalyInfo() {
mAnomalies = getArguments().getParcelableArrayList(EXTRA_ANOMALY_LIST);
if (mAnomalies == null) {
getLoaderManager().initLoader(ANOMALY_LOADER, Bundle.EMPTY, this);
} else if (mAnomalies != null) {
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(mAnomalies);
}
}
@VisibleForTesting
void initHeader() {
final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header);
@@ -280,8 +253,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
final String usagePercent = bundle.getString(EXTRA_POWER_USAGE_PERCENT);
final int powerMah = bundle.getInt(EXTRA_POWER_USAGE_AMOUNT);
mForegroundPreference.setSummary(
TextUtils.expandTemplate(getText(R.string.battery_used_for),
StringUtil.formatElapsedTime(context, foregroundTimeMs, false)));
@@ -290,15 +261,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
StringUtil.formatElapsedTime(context, backgroundTimeMs, false)));
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (TextUtils.equals(preference.getKey(), AnomalySummaryPreferenceController.ANOMALY_KEY)) {
mAnomalySummaryPreferenceController.onPreferenceTreeClick(preference);
return true;
}
return super.onPreferenceTreeClick(preference);
}
@Override
public int getMetricsCategory() {
return MetricsEvent.FUELGAUGE_POWER_USAGE_DETAIL;
@@ -349,29 +311,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
}
}
@Override
public void onAnomalyHandled(Anomaly anomaly) {
mAnomalySummaryPreferenceController.hideHighUsagePreference();
}
@Override
public Loader<List<Anomaly>> onCreateLoader(int id, Bundle args) {
return new AnomalyLoader(getContext(), mPackageName);
}
@Override
public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) {
final AnomalyUtils anomalyUtils = AnomalyUtils.getInstance(getContext());
anomalyUtils.logAnomalies(mMetricsFeatureProvider, data,
MetricsEvent.FUELGAUGE_POWER_USAGE_DETAIL);
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);
}
@Override
public void onLoaderReset(Loader<List<Anomaly>> loader) {
}
@Override
public void onBatteryTipHandled(BatteryTip batteryTip) {
mBackgroundActivityPreferenceController.updateSummary(

View File

@@ -41,7 +41,6 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -79,7 +78,6 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
private SettingsActivity mActivity;
private InstrumentedPreferenceFragment mFragment;
private Context mPrefContext;
SparseArray<List<Anomaly>> mAnomalySparseArray;
private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -162,30 +160,13 @@ public class BatteryAppListPreferenceController extends AbstractPreferenceContro
if (preference instanceof PowerGaugePreference) {
PowerGaugePreference pgp = (PowerGaugePreference) preference;
BatteryEntry entry = pgp.getInfo();
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity,
mFragment, mBatteryStatsHelper, STATS_TYPE, entry, pgp.getPercent(),
mAnomalySparseArray != null ? mAnomalySparseArray.get(entry.sipper.getUid())
: null);
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils,
mFragment, mBatteryStatsHelper, STATS_TYPE, entry, pgp.getPercent());
return true;
}
return false;
}
public void refreshAnomalyIcon(final SparseArray<List<Anomaly>> anomalySparseArray) {
if (!isAvailable()) {
return;
}
mAnomalySparseArray = anomalySparseArray;
for (int i = 0, size = anomalySparseArray.size(); i < size; i++) {
final String key = extractKeyFromUid(anomalySparseArray.keyAt(i));
final PowerGaugePreference pref = (PowerGaugePreference) mAppListGroup.findPreference(
key);
if (pref != null) {
pref.shouldShowAnomalyIcon(true);
}
}
}
public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps) {
if (!isAvailable()) {
return;

View File

@@ -38,7 +38,6 @@ import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.util.ArrayUtils;
import com.android.settings.R;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.batterytip.AnomalyInfo;
import com.android.settings.fuelgauge.batterytip.StatsManagerConfig;
import com.android.settings.overlay.FeatureFactory;
@@ -392,20 +391,6 @@ public class BatteryUtils {
}
}
@StringRes
public int getSummaryResIdFromAnomalyType(@Anomaly.AnomalyType int type) {
switch (type) {
case Anomaly.AnomalyType.WAKE_LOCK:
return R.string.battery_abnormal_wakelock_summary;
case Anomaly.AnomalyType.WAKEUP_ALARM:
return R.string.battery_abnormal_wakeup_alarm_summary;
case Anomaly.AnomalyType.BLUETOOTH_SCAN:
return R.string.battery_abnormal_location_summary;
default:
throw new IllegalArgumentException("Incorrect anomaly type: " + type);
}
}
public void setForceAppStandby(int uid, String packageName,
int mode) {
final boolean isPreOApp = isPreOApp(packageName);

View File

@@ -1,161 +0,0 @@
/*
* 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;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.IconDrawableFactory;
import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment;
import com.android.settings.fuelgauge.anomaly.AnomalyPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.List;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
/**
* Fragment to show a list of anomaly apps, where user could handle these anomalies
*/
public class PowerUsageAnomalyDetails extends DashboardFragment implements
AnomalyDialogFragment.AnomalyDialogListener {
public static final String TAG = "PowerAbnormalUsageDetail";
@VisibleForTesting
static final String EXTRA_ANOMALY_LIST = "anomaly_list";
private static final int REQUEST_ANOMALY_ACTION = 0;
private static final String KEY_PREF_ANOMALY_LIST = "app_abnormal_list";
@VisibleForTesting
List<Anomaly> mAnomalies;
@VisibleForTesting
PreferenceGroup mAbnormalListGroup;
@VisibleForTesting
PackageManager mPackageManager;
@VisibleForTesting
BatteryUtils mBatteryUtils;
@VisibleForTesting
IconDrawableFactory mIconDrawableFactory;
public static void startBatteryAbnormalPage(SettingsActivity caller,
InstrumentedPreferenceFragment fragment, List<Anomaly> anomalies) {
Bundle args = new Bundle();
args.putParcelableList(EXTRA_ANOMALY_LIST, anomalies);
new SubSettingLauncher(caller)
.setDestination(PowerUsageAnomalyDetails.class.getName())
.setTitleRes(R.string.battery_abnormal_details_title)
.setArguments(args)
.setSourceMetricsCategory(fragment.getMetricsCategory())
.launch();
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Context context = getContext();
mAnomalies = getArguments().getParcelableArrayList(EXTRA_ANOMALY_LIST);
mAbnormalListGroup = (PreferenceGroup) findPreference(KEY_PREF_ANOMALY_LIST);
mPackageManager = context.getPackageManager();
mIconDrawableFactory = IconDrawableFactory.newInstance(context);
mBatteryUtils = BatteryUtils.getInstance(context);
}
@Override
public void onResume() {
super.onResume();
refreshUi();
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference instanceof AnomalyPreference) {
AnomalyPreference anomalyPreference = (AnomalyPreference) preference;
final Anomaly anomaly = anomalyPreference.getAnomaly();
AnomalyDialogFragment dialogFragment = AnomalyDialogFragment.newInstance(anomaly,
MetricsProto.MetricsEvent.FUELGAUGE_ANOMALY_DETAIL);
dialogFragment.setTargetFragment(this, REQUEST_ANOMALY_ACTION);
dialogFragment.show(getFragmentManager(), TAG);
return true;
}
return super.onPreferenceTreeClick(preference);
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.power_abnormal_detail;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return null;
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.FUELGAUGE_ANOMALY_DETAIL;
}
void refreshUi() {
mAbnormalListGroup.removeAll();
for (int i = 0, size = mAnomalies.size(); i < size; i++) {
final Anomaly anomaly = mAnomalies.get(i);
Preference pref = new AnomalyPreference(getPrefContext(), anomaly);
pref.setSummary(mBatteryUtils.getSummaryResIdFromAnomalyType(anomaly.type));
Drawable icon = getBadgedIcon(anomaly.packageName, UserHandle.getUserId(anomaly.uid));
if (icon != null) {
pref.setIcon(icon);
}
mAbnormalListGroup.addPreference(pref);
}
}
@Override
public void onAnomalyHandled(Anomaly anomaly) {
mAnomalies.remove(anomaly);
refreshUi();
}
@VisibleForTesting
Drawable getBadgedIcon(String packageName, int userId) {
return Utils.getBadgedIcon(mIconDrawableFactory, mPackageManager, packageName, userId);
}
}

View File

@@ -41,8 +41,6 @@ import com.android.settings.applications.LayoutPreference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.display.BatteryPercentagePreferenceController;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
@@ -104,11 +102,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
@VisibleForTesting
BatteryInfo mBatteryInfo;
/**
* SparseArray that maps uid to {@link Anomaly}, so we could find {@link Anomaly} by uid
*/
@VisibleForTesting
SparseArray<List<Anomaly>> mAnomalySparseArray;
@VisibleForTesting
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
@VisibleForTesting
@@ -217,7 +210,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
KEY_TIME_SINCE_LAST_FULL_CHARGE);
mFooterPreferenceMixin.createFooterPreference().setTitle(R.string.battery_footer_summary);
mBatteryUtils = BatteryUtils.getInstance(getContext());
mAnomalySparseArray = new SparseArray<>();
restartBatteryInfoLoader();
mBatteryTipPreferenceController.restoreInstanceState(icicle);
@@ -327,11 +319,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
mBatteryLayoutPref = layoutPreference;
}
@VisibleForTesting
AnomalyDetectionPolicy getAnomalyDetectionPolicy() {
return new AnomalyDetectionPolicy(getContext());
}
@VisibleForTesting
void updateLastFullChargePreference() {
if (mBatteryInfo != null && mBatteryInfo.averageTimeToDischarge
@@ -368,17 +355,6 @@ public class PowerUsageSummary extends PowerUsageBase implements OnLongClickList
.getPowerUsageFeatureProvider(context);
}
@VisibleForTesting
void updateAnomalySparseArray(List<Anomaly> anomalies) {
mAnomalySparseArray.clear();
for (final Anomaly anomaly : anomalies) {
if (mAnomalySparseArray.get(anomaly.uid) == null) {
mAnomalySparseArray.append(anomaly.uid, new ArrayList<>());
}
mAnomalySparseArray.get(anomaly.uid).add(anomaly);
}
}
@VisibleForTesting
void restartBatteryInfoLoader() {
getLoaderManager().restartLoader(BATTERY_INFO_LOADER, Bundle.EMPTY,

View File

@@ -1,247 +0,0 @@
/*
* 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;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
import androidx.annotation.IntDef;
/**
* Data that represents an app has been detected as anomaly. It contains
*
* 1. Basic information of the app(i.e. uid, package name)
* 2. Type of anomaly
* 3. Data that has been detected as anomaly(i.e wakelock time)
*/
public class Anomaly implements Parcelable {
@Retention(RetentionPolicy.SOURCE)
@IntDef({AnomalyType.WAKE_LOCK,
AnomalyType.WAKEUP_ALARM,
AnomalyType.BLUETOOTH_SCAN})
public @interface AnomalyType {
int WAKE_LOCK = 0;
int WAKEUP_ALARM = 1;
int BLUETOOTH_SCAN = 2;
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({AnomalyActionType.FORCE_STOP,
AnomalyActionType.BACKGROUND_CHECK,
AnomalyActionType.LOCATION_CHECK,
AnomalyActionType.STOP_AND_BACKGROUND_CHECK})
public @interface AnomalyActionType {
int FORCE_STOP = 0;
int BACKGROUND_CHECK = 1;
int LOCATION_CHECK = 2;
int STOP_AND_BACKGROUND_CHECK = 3;
}
@AnomalyType
public static final int[] ANOMALY_TYPE_LIST = {
AnomalyType.WAKE_LOCK,
AnomalyType.WAKEUP_ALARM,
AnomalyType.BLUETOOTH_SCAN};
/**
* Type of this this anomaly
*/
public final int type;
public final int uid;
public final int targetSdkVersion;
public final long wakelockTimeMs;
public final long bluetoothScanningTimeMs;
public final int wakeupAlarmCount;
/**
* {@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
*/
public final CharSequence displayName;
public final String packageName;
private Anomaly(Builder builder) {
type = builder.mType;
uid = builder.mUid;
displayName = builder.mDisplayName;
packageName = builder.mPackageName;
wakelockTimeMs = builder.mWakeLockTimeMs;
targetSdkVersion = builder.mTargetSdkVersion;
backgroundRestrictionEnabled = builder.mBgRestrictionEnabled;
bluetoothScanningTimeMs = builder.mBluetoothScanningTimeMs;
wakeupAlarmCount = builder.mWakeupAlarmCount;
}
private Anomaly(Parcel in) {
type = in.readInt();
uid = in.readInt();
displayName = in.readCharSequence();
packageName = in.readString();
wakelockTimeMs = in.readLong();
targetSdkVersion = in.readInt();
backgroundRestrictionEnabled = in.readBoolean();
wakeupAlarmCount = in.readInt();
bluetoothScanningTimeMs = in.readLong();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(type);
dest.writeInt(uid);
dest.writeCharSequence(displayName);
dest.writeString(packageName);
dest.writeLong(wakelockTimeMs);
dest.writeInt(targetSdkVersion);
dest.writeBoolean(backgroundRestrictionEnabled);
dest.writeInt(wakeupAlarmCount);
dest.writeLong(bluetoothScanningTimeMs);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Anomaly)) {
return false;
}
Anomaly other = (Anomaly) obj;
return type == other.type
&& uid == other.uid
&& wakelockTimeMs == other.wakelockTimeMs
&& TextUtils.equals(displayName, other.displayName)
&& TextUtils.equals(packageName, other.packageName)
&& targetSdkVersion == other.targetSdkVersion
&& backgroundRestrictionEnabled == other.backgroundRestrictionEnabled
&& wakeupAlarmCount == other.wakeupAlarmCount
&& bluetoothScanningTimeMs == other.bluetoothScanningTimeMs;
}
@Override
public int hashCode() {
return Objects.hash(type, uid, displayName, packageName, wakelockTimeMs, targetSdkVersion,
backgroundRestrictionEnabled, wakeupAlarmCount, bluetoothScanningTimeMs);
}
@Override
public String toString() {
return "type=" + toAnomalyTypeText(type) + " uid=" + uid + " package=" + packageName +
" displayName=" + displayName + " wakelockTimeMs=" + wakelockTimeMs +
" wakeupAlarmCount=" + wakeupAlarmCount + " bluetoothTimeMs="
+ bluetoothScanningTimeMs;
}
private String toAnomalyTypeText(@AnomalyType int type) {
switch (type) {
case AnomalyType.WAKEUP_ALARM:
return "wakeupAlarm";
case AnomalyType.WAKE_LOCK:
return "wakelock";
case AnomalyType.BLUETOOTH_SCAN:
return "unoptimizedBluetoothScan";
}
return "";
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Anomaly createFromParcel(Parcel in) {
return new Anomaly(in);
}
public Anomaly[] newArray(int size) {
return new Anomaly[size];
}
};
public static final class Builder {
@AnomalyType
private int mType;
private int mUid;
private int mTargetSdkVersion;
private CharSequence mDisplayName;
private String mPackageName;
private long mWakeLockTimeMs;
private boolean mBgRestrictionEnabled;
private int mWakeupAlarmCount;
private long mBluetoothScanningTimeMs;
public Builder setType(@AnomalyType int type) {
mType = type;
return this;
}
public Builder setUid(int uid) {
mUid = uid;
return this;
}
public Builder setDisplayName(CharSequence displayName) {
mDisplayName = displayName;
return this;
}
public Builder setPackageName(String packageName) {
mPackageName = packageName;
return this;
}
public Builder setWakeLockTimeMs(long wakeLockTimeMs) {
mWakeLockTimeMs = wakeLockTimeMs;
return this;
}
public Builder setTargetSdkVersion(int targetSdkVersion) {
mTargetSdkVersion = targetSdkVersion;
return this;
}
public Builder setBackgroundRestrictionEnabled(boolean bgRestrictionEnabled) {
mBgRestrictionEnabled = bgRestrictionEnabled;
return this;
}
public Builder setWakeupAlarmCount(int wakeupAlarmCount) {
mWakeupAlarmCount = wakeupAlarmCount;
return this;
}
public Builder setBluetoothScanningTimeMs(long bluetoothScanningTimeMs) {
mBluetoothScanningTimeMs = bluetoothScanningTimeMs;
return this;
}
public Anomaly build() {
return new Anomaly(this);
}
}
}

View File

@@ -1,175 +0,0 @@
/*
* 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;
import android.content.Context;
import android.net.Uri;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.KeyValueListParser;
import android.util.Log;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
import androidx.annotation.VisibleForTesting;
/**
* Class to store the policy for anomaly detection, which comes from
* {@link android.provider.Settings.Global}
*/
public class AnomalyDetectionPolicy {
public static final String TAG = "AnomalyDetectionPolicy";
@VisibleForTesting
static final String KEY_ANOMALY_DETECTION_ENABLED = "anomaly_detection_enabled";
@VisibleForTesting
static final String KEY_WAKELOCK_DETECTION_ENABLED = "wakelock_enabled";
@VisibleForTesting
static final String KEY_WAKEUP_ALARM_DETECTION_ENABLED = "wakeup_alarm_enabled";
@VisibleForTesting
static final String KEY_BLUETOOTH_SCAN_DETECTION_ENABLED = "bluetooth_scan_enabled";
@VisibleForTesting
static final String KEY_WAKELOCK_THRESHOLD = "wakelock_threshold";
@VisibleForTesting
static final String KEY_WAKEUP_ALARM_THRESHOLD = "wakeup_alarm_threshold";
@VisibleForTesting
static final String KEY_WAKEUP_BLACKLISTED_TAGS = "wakeup_blacklisted_tags";
@VisibleForTesting
static final String KEY_BLUETOOTH_SCAN_THRESHOLD = "bluetooth_scan_threshold";
/**
* {@code true} if general anomaly detection is enabled
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
* @see #KEY_ANOMALY_DETECTION_ENABLED
*/
final boolean anomalyDetectionEnabled;
/**
* {@code true} if wakelock anomaly detection is enabled
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
* @see #KEY_WAKELOCK_DETECTION_ENABLED
*/
final boolean wakeLockDetectionEnabled;
/**
* {@code true} if wakeup alarm detection is enabled
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
* @see #KEY_WAKEUP_ALARM_DETECTION_ENABLED
*/
final boolean wakeupAlarmDetectionEnabled;
/**
* {@code true} if bluetooth scanning detection is enabled
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
* @see #KEY_BLUETOOTH_SCAN_THRESHOLD
*/
final boolean bluetoothScanDetectionEnabled;
/**
* Threshold for wakelock time in milli seconds
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
* @see #KEY_WAKELOCK_THRESHOLD
*/
public final long wakeLockThreshold;
/**
* Threshold for wakeup alarm count per hour
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
* @see #KEY_WAKEUP_ALARM_THRESHOLD
*/
public final long wakeupAlarmThreshold;
/**
* Array of blacklisted wakeups, by tag.
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
* @see #KEY_WAKEUP_BLACKLISTED_TAGS
*/
public final Set<String> wakeupBlacklistedTags;
/**
* Threshold for bluetooth unoptimized scanning time in milli seconds
*
* @see Settings.Global#ANOMALY_DETECTION_CONSTANTS
* @see #KEY_BLUETOOTH_SCAN_THRESHOLD
*/
public final long bluetoothScanThreshold;
private final KeyValueListParser mParser;
public AnomalyDetectionPolicy(Context context) {
mParser = new KeyValueListParser(',');
final String value = Settings.Global.getString(context.getContentResolver(),
Settings.Global.ANOMALY_DETECTION_CONSTANTS);
try {
mParser.setString(value);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Bad anomaly detection constants");
}
anomalyDetectionEnabled =
mParser.getBoolean(KEY_ANOMALY_DETECTION_ENABLED, false);
wakeLockDetectionEnabled =
mParser.getBoolean(KEY_WAKELOCK_DETECTION_ENABLED,false);
wakeupAlarmDetectionEnabled =
mParser.getBoolean(KEY_WAKEUP_ALARM_DETECTION_ENABLED,false);
bluetoothScanDetectionEnabled = mParser.getBoolean(
KEY_BLUETOOTH_SCAN_DETECTION_ENABLED, false);
wakeLockThreshold = mParser.getLong(KEY_WAKELOCK_THRESHOLD,
DateUtils.HOUR_IN_MILLIS);
wakeupAlarmThreshold = mParser.getLong(KEY_WAKEUP_ALARM_THRESHOLD, 10);
wakeupBlacklistedTags = parseStringSet(KEY_WAKEUP_BLACKLISTED_TAGS, null);
bluetoothScanThreshold = mParser.getLong(KEY_BLUETOOTH_SCAN_THRESHOLD,
30 * DateUtils.MINUTE_IN_MILLIS);
}
public boolean isAnomalyDetectionEnabled() {
return anomalyDetectionEnabled;
}
public boolean isAnomalyDetectorEnabled(@Anomaly.AnomalyType int type) {
switch (type) {
case Anomaly.AnomalyType.WAKE_LOCK:
return wakeLockDetectionEnabled;
case Anomaly.AnomalyType.WAKEUP_ALARM:
return wakeupAlarmDetectionEnabled;
case Anomaly.AnomalyType.BLUETOOTH_SCAN:
return bluetoothScanDetectionEnabled;
default:
return false; // Disabled when no this type
}
}
private Set<String> parseStringSet(final String key, final Set<String> defaultSet) {
final String value = mParser.getString(key, null);
if (value != null) {
return Arrays.stream(value.split(":"))
.map(String::trim).map(Uri::decode).collect(Collectors.toSet());
} else {
return defaultSet;
}
}
}

View File

@@ -1,143 +0,0 @@
/*
* 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;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import androidx.annotation.VisibleForTesting;
/**
* Dialog Fragment to show action dialog for each anomaly
*/
public class AnomalyDialogFragment extends InstrumentedDialogFragment implements
DialogInterface.OnClickListener {
private static final String ARG_ANOMALY = "anomaly";
private static final String ARG_METRICS_KEY = "metrics_key";
@VisibleForTesting
Anomaly mAnomaly;
@VisibleForTesting
AnomalyUtils mAnomalyUtils;
/**
* Listener to give the control back to target fragment
*/
public interface AnomalyDialogListener {
/**
* This method is invoked once anomaly is handled, then target fragment could do
* extra work. One example is that fragment could remove the anomaly preference
* since it has been handled
*
* @param anomaly that has been handled
*/
void onAnomalyHandled(Anomaly anomaly);
}
public static AnomalyDialogFragment newInstance(Anomaly anomaly, int metricsKey) {
AnomalyDialogFragment dialogFragment = new AnomalyDialogFragment();
Bundle args = new Bundle(2);
args.putParcelable(ARG_ANOMALY, anomaly);
args.putInt(ARG_METRICS_KEY, metricsKey);
dialogFragment.setArguments(args);
return dialogFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initAnomalyUtils();
}
@VisibleForTesting
void initAnomalyUtils() {
mAnomalyUtils = AnomalyUtils.getInstance(getContext());
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.DIALOG_HANDLE_ANOMALY;
}
@Override
public void onClick(DialogInterface dialog, int which) {
final AnomalyDialogListener lsn = (AnomalyDialogListener) getTargetFragment();
if (lsn == null) {
return;
}
final AnomalyAction anomalyAction = mAnomalyUtils.getAnomalyAction(mAnomaly);
final int metricsKey = getArguments().getInt(ARG_METRICS_KEY);
anomalyAction.handlePositiveAction(mAnomaly, metricsKey);
lsn.onAnomalyHandled(mAnomaly);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Bundle bundle = getArguments();
final Context context = getContext();
final AnomalyUtils anomalyUtils = AnomalyUtils.getInstance(context);
mAnomaly = bundle.getParcelable(ARG_ANOMALY);
anomalyUtils.logAnomaly(mMetricsFeatureProvider, mAnomaly,
MetricsProto.MetricsEvent.DIALOG_HANDLE_ANOMALY);
final AnomalyAction anomalyAction = mAnomalyUtils.getAnomalyAction(mAnomaly);
switch (anomalyAction.getActionType()) {
case Anomaly.AnomalyActionType.FORCE_STOP:
return new AlertDialog.Builder(context)
.setTitle(R.string.dialog_stop_title)
.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)
.setNegativeButton(R.string.dlg_cancel, null)
.create();
case Anomaly.AnomalyActionType.STOP_AND_BACKGROUND_CHECK:
return new AlertDialog.Builder(context)
.setTitle(R.string.dialog_background_check_title)
.setMessage(getString(R.string.dialog_background_check_message,
mAnomaly.displayName))
.setPositiveButton(R.string.dialog_background_check_ok, this)
.setNegativeButton(R.string.dlg_cancel, null)
.create();
case Anomaly.AnomalyActionType.LOCATION_CHECK:
return new AlertDialog.Builder(context)
.setTitle(R.string.dialog_location_title)
.setMessage(getString(R.string.dialog_location_message,
mAnomaly.displayName))
.setPositiveButton(R.string.dialog_location_ok, this)
.setNegativeButton(R.string.dlg_cancel, null)
.create();
default:
throw new IllegalArgumentException("unknown type " + mAnomaly.type);
}
}
}

View File

@@ -1,135 +0,0 @@
/*
* 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;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.UserManager;
import android.util.Log;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.VisibleForTesting;
/**
* Loader to compute which apps are anomaly and return a anomaly list. It will return
* an empty list if there is no anomaly.
*/
public class AnomalyLoader extends AsyncLoaderCompat<List<Anomaly>> {
private static final String TAG = "AnomalyLoader";
private static final boolean USE_FAKE_DATA = false;
private BatteryStatsHelper mBatteryStatsHelper;
private String mPackageName;
private UserManager mUserManager;
@VisibleForTesting
AnomalyUtils mAnomalyUtils;
@VisibleForTesting
AnomalyDetectionPolicy mPolicy;
/**
* Create {@link AnomalyLoader} that runs anomaly check for all apps.
*/
public AnomalyLoader(Context context, BatteryStatsHelper batteryStatsHelper) {
this(context, batteryStatsHelper, null, new AnomalyDetectionPolicy(context));
}
/**
* Create {@link AnomalyLoader} with {@code packageName}, so this loader will only
* detect anomalies related to {@code packageName}, or check all apps if {@code packageName}
* is {@code null}.
*
* This constructor will create {@link BatteryStatsHelper} in background thread.
*
* @param packageName if set, only finds anomalies for this package. If {@code null},
* detects all anomalies of this type.
*/
public AnomalyLoader(Context context, String packageName) {
this(context, null, packageName, new AnomalyDetectionPolicy(context));
}
@VisibleForTesting
AnomalyLoader(Context context, BatteryStatsHelper batteryStatsHelper,
String packageName, AnomalyDetectionPolicy policy) {
super(context);
mBatteryStatsHelper = batteryStatsHelper;
mPackageName = packageName;
mAnomalyUtils = AnomalyUtils.getInstance(context);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mPolicy = policy;
}
@Override
protected void onDiscardResult(List<Anomaly> result) {
}
@Override
public List<Anomaly> loadInBackground() {
if (USE_FAKE_DATA) {
return generateFakeData();
}
if (mBatteryStatsHelper == null) {
mBatteryStatsHelper = new BatteryStatsHelper(getContext());
mBatteryStatsHelper.create((Bundle) null);
mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
mUserManager.getUserProfiles());
}
return mAnomalyUtils.detectAnomalies(mBatteryStatsHelper, mPolicy, mPackageName);
}
@VisibleForTesting
List<Anomaly> generateFakeData() {
final List<Anomaly> anomalies = new ArrayList<>();
final Context context = getContext();
final String packageName = "com.android.settings";
final CharSequence displayName = "Settings";
try {
final int uid = context.getPackageManager().getPackageUid(packageName, 0);
anomalies.add(new Anomaly.Builder()
.setUid(uid)
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.setPackageName(packageName)
.setDisplayName(displayName)
.build());
anomalies.add(new Anomaly.Builder()
.setUid(uid)
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setPackageName(packageName)
.setDisplayName(displayName)
.build());
anomalies.add(new Anomaly.Builder()
.setUid(uid)
.setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
.setPackageName(packageName)
.setDisplayName(displayName)
.build());
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Cannot find package by name: " + packageName, e);
}
return anomalies;
}
}

View File

@@ -1,26 +0,0 @@
package com.android.settings.fuelgauge.anomaly;
import android.content.Context;
import androidx.preference.Preference;
/**
* Preference that stores {@link Anomaly}
*/
public class AnomalyPreference extends Preference {
private Anomaly mAnomaly;
public AnomalyPreference(Context context, Anomaly anomaly) {
super(context);
mAnomaly = anomaly;
if (anomaly != null) {
setTitle(anomaly.displayName);
}
}
public Anomaly getAnomaly() {
return mAnomaly;
}
}

View File

@@ -1,114 +0,0 @@
/*
* 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;
import android.content.Context;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.PowerUsageAnomalyDetails;
import java.util.List;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
/**
* Manager that responsible for updating high usage preference and handling preference click.
*/
public class AnomalySummaryPreferenceController {
private static final String TAG = "HighUsagePreferenceController";
public static final String ANOMALY_KEY = "high_usage";
private static final int REQUEST_ANOMALY_ACTION = 0;
private InstrumentedPreferenceFragment mFragment;
@VisibleForTesting
Preference mAnomalyPreference;
@VisibleForTesting
List<Anomaly> mAnomalies;
@VisibleForTesting
BatteryUtils mBatteryUtils;
private SettingsActivity mSettingsActivity;
/**
* Metrics key about fragment that create this controller
*
* @see com.android.internal.logging.nano.MetricsProto.MetricsEvent
*/
private int mMetricsKey;
public AnomalySummaryPreferenceController(SettingsActivity activity,
InstrumentedPreferenceFragment fragment) {
mFragment = fragment;
mSettingsActivity = activity;
mAnomalyPreference = mFragment.getPreferenceScreen().findPreference(ANOMALY_KEY);
mMetricsKey = fragment.getMetricsCategory();
mBatteryUtils = BatteryUtils.getInstance(activity.getApplicationContext());
hideHighUsagePreference();
}
public boolean onPreferenceTreeClick(Preference preference) {
if (mAnomalies != null && ANOMALY_KEY.equals(preference.getKey())) {
if (mAnomalies.size() == 1) {
final Anomaly anomaly = mAnomalies.get(0);
AnomalyDialogFragment dialogFragment = AnomalyDialogFragment.newInstance(anomaly,
mMetricsKey);
dialogFragment.setTargetFragment(mFragment, REQUEST_ANOMALY_ACTION);
dialogFragment.show(mFragment.getFragmentManager(), TAG);
} else {
PowerUsageAnomalyDetails.startBatteryAbnormalPage(mSettingsActivity, mFragment,
mAnomalies);
}
return true;
}
return false;
}
/**
* Update anomaly preference based on {@code anomalies}, also store a reference
* of {@paramref anomalies}, which would be used in {@link #onPreferenceTreeClick(Preference)}
*
* @param anomalies used to update the summary, this method will store a reference of it
*/
public void updateAnomalySummaryPreference(List<Anomaly> anomalies) {
final Context context = mFragment.getContext();
mAnomalies = anomalies;
if (!mAnomalies.isEmpty()) {
mAnomalyPreference.setVisible(true);
final int count = mAnomalies.size();
final String title = context.getResources().getQuantityString(
R.plurals.power_high_usage_title, count, mAnomalies.get(0).displayName);
final String summary = count > 1 ?
context.getString(R.string.battery_abnormal_apps_summary, count)
: context.getString(
mBatteryUtils.getSummaryResIdFromAnomalyType(mAnomalies.get(0).type));
mAnomalyPreference.setTitle(title);
mAnomalyPreference.setSummary(summary);
} else {
mAnomalyPreference.setVisible(false);
}
}
public void hideHighUsagePreference() {
mAnomalyPreference.setVisible(false);
}
}

View File

@@ -1,172 +0,0 @@
/*
* 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;
import android.content.Context;
import android.os.Build;
import android.util.Pair;
import android.util.SparseIntArray;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import com.android.settings.fuelgauge.anomaly.action.ForceStopAction;
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.BluetoothScanAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeupAlarmAnomalyDetector;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.VisibleForTesting;
/**
* Utility class for anomaly detection
*/
public class AnomalyUtils {
private Context mContext;
private static AnomalyUtils sInstance;
private static final SparseIntArray mMetricArray;
static {
mMetricArray = new SparseIntArray();
mMetricArray.append(Anomaly.AnomalyType.WAKE_LOCK,
MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKELOCK);
mMetricArray.append(Anomaly.AnomalyType.WAKEUP_ALARM,
MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKEUP_ALARM);
mMetricArray.append(Anomaly.AnomalyType.BLUETOOTH_SCAN,
MetricsProto.MetricsEvent.ANOMALY_TYPE_UNOPTIMIZED_BT);
}
@VisibleForTesting
AnomalyUtils(Context context) {
mContext = context.getApplicationContext();
}
public static AnomalyUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new AnomalyUtils(context);
}
return sInstance;
}
/**
* Return the corresponding {@link AnomalyAction} according to
* {@link com.android.settings.fuelgauge.anomaly.Anomaly}
*
* @return corresponding {@link AnomalyAction}, or null if cannot find it.
*/
public AnomalyAction getAnomalyAction(Anomaly anomaly) {
switch (anomaly.type) {
case Anomaly.AnomalyType.WAKE_LOCK:
return new ForceStopAction(mContext);
case Anomaly.AnomalyType.WAKEUP_ALARM:
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:
return new LocationCheckAction(mContext);
default:
return null;
}
}
/**
* Return the corresponding {@link AnomalyDetector} according to
* {@link com.android.settings.fuelgauge.anomaly.Anomaly.AnomalyType}
*
* @return corresponding {@link AnomalyDetector}, or null if cannot find it.
*/
public AnomalyDetector getAnomalyDetector(@Anomaly.AnomalyType int anomalyType) {
switch (anomalyType) {
case Anomaly.AnomalyType.WAKE_LOCK:
return new WakeLockAnomalyDetector(mContext);
case Anomaly.AnomalyType.WAKEUP_ALARM:
return new WakeupAlarmAnomalyDetector(mContext);
case Anomaly.AnomalyType.BLUETOOTH_SCAN:
return new BluetoothScanAnomalyDetector(mContext);
default:
return null;
}
}
/**
* Detect whether application with {@code targetPackageName} has anomaly. When
* {@code targetPackageName} is null, start detection among all the applications.
*
* @param batteryStatsHelper contains battery stats, used to detect anomaly
* @param policy contains configuration about anomaly check
* @param targetPackageName represents the app need to be detected
* @return the list of anomalies
*/
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper,
AnomalyDetectionPolicy policy, String targetPackageName) {
final List<Anomaly> anomalies = new ArrayList<>();
for (@Anomaly.AnomalyType int type : Anomaly.ANOMALY_TYPE_LIST) {
if (policy.isAnomalyDetectorEnabled(type)) {
anomalies.addAll(getAnomalyDetector(type).detectAnomalies(
batteryStatsHelper, targetPackageName));
}
}
return anomalies;
}
/**
* Log the list of {@link Anomaly} using {@link MetricsFeatureProvider}, which contains
* anomaly type, package name, field_context, field_action_type
*
* @param provider provider to do the logging
* @param anomalies contains the data to log
* @param contextId which page invoke this logging
* @see #logAnomaly(MetricsFeatureProvider, Anomaly, int)
*/
public void logAnomalies(MetricsFeatureProvider provider, List<Anomaly> anomalies,
int contextId) {
for (int i = 0, size = anomalies.size(); i < size; i++) {
logAnomaly(provider, anomalies.get(i), contextId);
}
}
/**
* Log the {@link Anomaly} using {@link MetricsFeatureProvider}, which contains
* anomaly type, package name, field_context, field_action_type
*
* @param provider provider to do the logging
* @param anomaly contains the data to log
* @param contextId which page invoke this logging
* @see #logAnomalies(MetricsFeatureProvider, List, int)
*/
public void logAnomaly(MetricsFeatureProvider provider, Anomaly anomaly, int contextId) {
provider.action(
mContext,
mMetricArray.get(anomaly.type, MetricsProto.MetricsEvent.VIEW_UNKNOWN),
anomaly.packageName,
Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, contextId),
Pair.create(MetricsProto.MetricsEvent.FIELD_ANOMALY_ACTION_TYPE,
getAnomalyAction(anomaly).getActionType()));
}
}

View File

@@ -1,63 +0,0 @@
/*
* 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.util.Pair;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/**
* Abstract class for anomaly action, which is triggered if we need to handle the anomaly
*/
public abstract class AnomalyAction {
protected Context mContext;
protected int mActionMetricKey;
private MetricsFeatureProvider mMetricsFeatureProvider;
public AnomalyAction(Context context) {
mContext = context;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
}
/**
* handle the action when user clicks positive button
*
* @param anomaly about the app that we need to handle
* @param contextMetricsKey key for the page that invokes the action
* @see com.android.internal.logging.nano.MetricsProto
*/
public void handlePositiveAction(Anomaly anomaly, int contextMetricsKey) {
mMetricsFeatureProvider.action(mContext, mActionMetricKey, anomaly.packageName,
Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, contextMetricsKey));
}
/**
* Check whether the action is active for {@code anomaly}
*
* @param anomaly about the app that we need to handle
* @return {@code true} if action is active, otherwise return {@code false}
*/
public abstract boolean isActionActive(Anomaly anomaly);
@Anomaly.AnomalyActionType
public abstract int getActionType();
}

View File

@@ -1,65 +0,0 @@
/*
* 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.app.AppOpsManager;
import android.content.Context;
import android.os.Build;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import androidx.annotation.VisibleForTesting;
/**
* Background check action for anomaly app, which means to stop app running in the background
*/
public class BackgroundCheckAction extends AnomalyAction {
private AppOpsManager mAppOpsManager;
@VisibleForTesting
BatteryUtils mBatteryUtils;
public BackgroundCheckAction(Context context) {
super(context);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mActionMetricKey = MetricsProto.MetricsEvent.ACTION_APP_BACKGROUND_CHECK;
mBatteryUtils = BatteryUtils.getInstance(context);
}
@Override
public void handlePositiveAction(Anomaly anomaly, int contextMetricsKey) {
super.handlePositiveAction(anomaly, contextMetricsKey);
if (anomaly.targetSdkVersion < Build.VERSION_CODES.O) {
mAppOpsManager.setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, anomaly.uid,
anomaly.packageName,
AppOpsManager.MODE_IGNORED);
}
}
@Override
public boolean isActionActive(Anomaly anomaly) {
return !mBatteryUtils.isBackgroundRestrictionEnabled(anomaly.targetSdkVersion, anomaly.uid,
anomaly.packageName);
}
@Override
public int getActionType() {
return Anomaly.AnomalyActionType.BACKGROUND_CHECK;
}
}

View File

@@ -1,68 +0,0 @@
/*
* 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.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.fuelgauge.anomaly.Anomaly;
/**
* Force stop action for anomaly app, which means to stop the app which causes anomaly
*/
public class ForceStopAction extends AnomalyAction {
private static final String TAG = "ForceStopAction";
private ActivityManager mActivityManager;
private PackageManager mPackageManager;
public ForceStopAction(Context context) {
super(context);
mActivityManager = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
mPackageManager = context.getPackageManager();
mActionMetricKey = MetricsProto.MetricsEvent.ACTION_APP_FORCE_STOP;
}
@Override
public void handlePositiveAction(Anomaly anomaly, int contextMetricsKey) {
super.handlePositiveAction(anomaly, contextMetricsKey);
mActivityManager.forceStopPackage(anomaly.packageName);
}
@Override
public boolean isActionActive(Anomaly anomaly) {
try {
ApplicationInfo info = mPackageManager.getApplicationInfo(anomaly.packageName,
PackageManager.GET_META_DATA);
return (info.flags & ApplicationInfo.FLAG_STOPPED) == 0;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Cannot find info for app: " + anomaly.packageName);
}
return false;
}
@Override
public int getActionType() {
return Anomaly.AnomalyActionType.FORCE_STOP;
}
}

View File

@@ -1,73 +0,0 @@
/*
* 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.Manifest;
import android.content.Context;
import android.content.pm.permission.RuntimePermissionPresenter;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.PermissionChecker;
/**
* Location action for anomaly app, which means to turn off location permission for this app
*/
public class LocationCheckAction extends AnomalyAction {
private static final String TAG = "LocationCheckAction";
private final RuntimePermissionPresenter mRuntimePermissionPresenter;
public LocationCheckAction(Context context) {
this(context, RuntimePermissionPresenter.getInstance(context));
}
@VisibleForTesting
LocationCheckAction(Context context, RuntimePermissionPresenter runtimePermissionPresenter) {
super(context);
mRuntimePermissionPresenter = runtimePermissionPresenter;
mActionMetricKey = MetricsProto.MetricsEvent.ACTION_APP_LOCATION_CHECK;
}
@Override
public void handlePositiveAction(Anomaly anomaly, int contextMetricsKey) {
super.handlePositiveAction(anomaly, contextMetricsKey);
mRuntimePermissionPresenter.revokeRuntimePermission(anomaly.packageName,
Manifest.permission.ACCESS_COARSE_LOCATION);
mRuntimePermissionPresenter.revokeRuntimePermission(anomaly.packageName,
Manifest.permission.ACCESS_FINE_LOCATION);
}
@Override
public boolean isActionActive(Anomaly anomaly) {
return isPermissionGranted(anomaly, Manifest.permission.ACCESS_COARSE_LOCATION)
|| isPermissionGranted(anomaly, Manifest.permission.ACCESS_FINE_LOCATION);
}
@Override
public int getActionType() {
return Anomaly.AnomalyActionType.LOCATION_CHECK;
}
private boolean isPermissionGranted(Anomaly anomaly, String permission) {
return PermissionChecker.checkPermission(mContext, permission, -1, anomaly.uid,
anomaly.packageName) == PermissionChecker.PERMISSION_GRANTED;
}
}

View File

@@ -1,67 +0,0 @@
/*
* 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 com.android.internal.logging.nano.MetricsProto;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import androidx.annotation.VisibleForTesting;
/**
* 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));
mActionMetricKey = MetricsProto.MetricsEvent.ACTION_APP_STOP_AND_BACKGROUND_CHECK;
}
@VisibleForTesting
StopAndBackgroundCheckAction(Context context, ForceStopAction forceStopAction,
BackgroundCheckAction backgroundCheckAction) {
super(context);
mForceStopAction = forceStopAction;
mBackgroundCheckAction = backgroundCheckAction;
}
@Override
public void handlePositiveAction(Anomaly anomaly, int metricsKey) {
super.handlePositiveAction(anomaly, 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

@@ -1,46 +0,0 @@
/*
* 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.checker;
import android.annotation.Nullable;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import java.util.List;
public interface AnomalyDetector {
/**
* Detect whether there is anomaly among all the applications in the device
*
* @param batteryStatsHelper used to detect the anomaly
* @return anomaly list
*/
List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper);
/**
* Detect whether application with {@code targetPackageName} has anomaly. When
* {@code targetPackageName} is null, start detection among all the applications.
*
* @param batteryStatsHelper used to detect the anomaly
* @param targetPackageName represents the app need to be detected
* @return anomaly list
*/
List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper,
@Nullable String targetPackageName);
}

View File

@@ -1,113 +0,0 @@
/*
* 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.checker;
import android.content.Context;
import android.os.BatteryStats;
import android.os.SystemClock;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.Utils;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.VisibleForTesting;
/**
* Check whether apps have unoptimized bluetooth scanning in the background
*/
public class BluetoothScanAnomalyDetector implements AnomalyDetector {
private static final String TAG = "BluetoothScanAnomalyDetector";
@VisibleForTesting
BatteryUtils mBatteryUtils;
private long mBluetoothScanningThreshold;
private Context mContext;
private AnomalyUtils mAnomalyUtils;
public BluetoothScanAnomalyDetector(Context context) {
this(context, new AnomalyDetectionPolicy(context), AnomalyUtils.getInstance(context));
}
@VisibleForTesting
BluetoothScanAnomalyDetector(Context context, AnomalyDetectionPolicy policy,
AnomalyUtils anomalyUtils) {
mContext = context;
mBatteryUtils = BatteryUtils.getInstance(context);
mBluetoothScanningThreshold = policy.bluetoothScanThreshold;
mAnomalyUtils = anomalyUtils;
}
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) {
// Detect all apps if targetPackageName is null
return detectAnomalies(batteryStatsHelper, null /* targetPackageName */);
}
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper,
String targetPackageName) {
final List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList();
final List<Anomaly> anomalies = new ArrayList<>();
final int targetUid = mBatteryUtils.getPackageUid(targetPackageName);
final long elapsedRealtimeMs = SystemClock.elapsedRealtime();
for (int i = 0, size = batterySippers.size(); i < size; i++) {
final BatterySipper sipper = batterySippers.get(i);
final BatteryStats.Uid uid = sipper.uidObj;
if (uid == null
|| mBatteryUtils.shouldHideSipper(sipper)
|| (targetUid != BatteryUtils.UID_NULL && targetUid != uid.getUid())) {
continue;
}
final long bluetoothTimeMs = getBluetoothUnoptimizedBgTimeMs(uid, elapsedRealtimeMs);
if (bluetoothTimeMs > mBluetoothScanningThreshold) {
final String packageName = mBatteryUtils.getPackageName(uid.getUid());
final CharSequence displayName = Utils.getApplicationLabel(mContext,
packageName);
Anomaly anomaly = new Anomaly.Builder()
.setUid(uid.getUid())
.setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
.setDisplayName(displayName)
.setPackageName(packageName)
.setBluetoothScanningTimeMs(bluetoothTimeMs)
.build();
if (mAnomalyUtils.getAnomalyAction(anomaly).isActionActive(anomaly)) {
anomalies.add(anomaly);
}
}
}
return anomalies;
}
@VisibleForTesting
public long getBluetoothUnoptimizedBgTimeMs(BatteryStats.Uid uid, long elapsedRealtimeMs) {
BatteryStats.Timer timer = uid.getBluetoothUnoptimizedScanBackgroundTimer();
return timer != null ? timer.getTotalDurationMsLocked(elapsedRealtimeMs) : 0;
}
}

View File

@@ -1,126 +0,0 @@
/*
* 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.checker;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.BatteryStats;
import android.os.SystemClock;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.Utils;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.VisibleForTesting;
/**
* Check whether apps holding wakelock too long
*/
public class WakeLockAnomalyDetector implements AnomalyDetector {
private static final String TAG = "WakeLockAnomalyChecker";
@VisibleForTesting
BatteryUtils mBatteryUtils;
@VisibleForTesting
long mWakeLockThresholdMs;
private PackageManager mPackageManager;
private Context mContext;
private AnomalyUtils mAnomalyUtils;
public WakeLockAnomalyDetector(Context context) {
this(context, new AnomalyDetectionPolicy(context), AnomalyUtils.getInstance(context));
}
@VisibleForTesting
WakeLockAnomalyDetector(Context context, AnomalyDetectionPolicy policy,
AnomalyUtils anomalyUtils) {
mContext = context;
mPackageManager = context.getPackageManager();
mBatteryUtils = BatteryUtils.getInstance(context);
mAnomalyUtils = anomalyUtils;
mWakeLockThresholdMs = policy.wakeLockThreshold;
}
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) {
// Detect all apps if targetPackageName is null
return detectAnomalies(batteryStatsHelper, null /* targetPackageName */);
}
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper,
String targetPackageName) {
final List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList();
final List<Anomaly> anomalies = new ArrayList<>();
final long rawRealtime = SystemClock.elapsedRealtime();
final int targetUid = mBatteryUtils.getPackageUid(targetPackageName);
// Check the app one by one
for (int i = 0, size = batterySippers.size(); i < size; i++) {
final BatterySipper sipper = batterySippers.get(i);
final BatteryStats.Uid uid = sipper.uidObj;
if (uid == null
|| mBatteryUtils.shouldHideSipper(sipper)
|| (targetUid != BatteryUtils.UID_NULL && targetUid != uid.getUid())) {
continue;
}
final long currentDurationMs = getCurrentDurationMs(uid, rawRealtime);
final long backgroundDurationMs = getBackgroundTotalDurationMs(uid, rawRealtime);
if (backgroundDurationMs > mWakeLockThresholdMs && currentDurationMs != 0) {
final String packageName = mBatteryUtils.getPackageName(uid.getUid());
final CharSequence displayName = Utils.getApplicationLabel(mContext,
packageName);
Anomaly anomaly = new Anomaly.Builder()
.setUid(uid.getUid())
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.setDisplayName(displayName)
.setPackageName(packageName)
.setWakeLockTimeMs(backgroundDurationMs)
.build();
if (mAnomalyUtils.getAnomalyAction(anomaly).isActionActive(anomaly)) {
anomalies.add(anomaly);
}
}
}
return anomalies;
}
@VisibleForTesting
long getCurrentDurationMs(BatteryStats.Uid uid, long elapsedRealtimeMs) {
BatteryStats.Timer timer = uid.getAggregatedPartialWakelockTimer();
return timer != null ? timer.getCurrentDurationMsLocked(elapsedRealtimeMs) : 0;
}
@VisibleForTesting
long getBackgroundTotalDurationMs(BatteryStats.Uid uid, long elapsedRealtimeMs) {
BatteryStats.Timer timer = uid.getAggregatedPartialWakelockTimer();
BatteryStats.Timer subTimer = timer != null ? timer.getSubTimer() : null;
return subTimer != null ? subTimer.getTotalDurationMsLocked(elapsedRealtimeMs) : 0;
}
}

View File

@@ -1,143 +0,0 @@
/*
* 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.checker;
import android.content.Context;
import android.os.BatteryStats;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.Utils;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import androidx.annotation.VisibleForTesting;
/**
* Check whether apps has too many wakeup alarms
*/
public class WakeupAlarmAnomalyDetector implements AnomalyDetector {
private static final String TAG = "WakeupAlarmAnomalyDetector";
@VisibleForTesting
BatteryUtils mBatteryUtils;
private long mWakeupAlarmThreshold;
private Set<String> mWakeupBlacklistedTags;
private Context mContext;
private AnomalyUtils mAnomalyUtils;
public WakeupAlarmAnomalyDetector(Context context) {
this(context, new AnomalyDetectionPolicy(context), AnomalyUtils.getInstance(context));
}
@VisibleForTesting
WakeupAlarmAnomalyDetector(Context context, AnomalyDetectionPolicy policy,
AnomalyUtils anomalyUtils) {
mContext = context;
mBatteryUtils = BatteryUtils.getInstance(context);
mAnomalyUtils = anomalyUtils;
mWakeupAlarmThreshold = policy.wakeupAlarmThreshold;
mWakeupBlacklistedTags = policy.wakeupBlacklistedTags;
}
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) {
// Detect all apps if targetPackageName is null
return detectAnomalies(batteryStatsHelper, null /* targetPackageName */);
}
@Override
public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper,
String targetPackageName) {
final List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList();
final List<Anomaly> anomalies = new ArrayList<>();
final double totalRunningHours = mBatteryUtils.calculateRunningTimeBasedOnStatsType(
batteryStatsHelper, BatteryStats.STATS_SINCE_CHARGED)
/ (double) DateUtils.HOUR_IN_MILLIS;
final int targetUid = mBatteryUtils.getPackageUid(targetPackageName);
if (totalRunningHours >= 1) {
for (int i = 0, size = batterySippers.size(); i < size; i++) {
final BatterySipper sipper = batterySippers.get(i);
final BatteryStats.Uid uid = sipper.uidObj;
if (uid == null
|| mBatteryUtils.shouldHideSipper(sipper)
|| (targetUid != BatteryUtils.UID_NULL && targetUid != uid.getUid())) {
continue;
}
final int wakeupAlarmCount = (int) (getWakeupAlarmCountFromUid(uid)
/ totalRunningHours);
if (wakeupAlarmCount > mWakeupAlarmThreshold) {
final String packageName = mBatteryUtils.getPackageName(uid.getUid());
final CharSequence displayName = Utils.getApplicationLabel(mContext,
packageName);
final int targetSdkVersion = mBatteryUtils.getTargetSdkVersion(packageName);
Anomaly anomaly = new Anomaly.Builder()
.setUid(uid.getUid())
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setDisplayName(displayName)
.setPackageName(packageName)
.setTargetSdkVersion(targetSdkVersion)
.setBackgroundRestrictionEnabled(
mBatteryUtils.isBackgroundRestrictionEnabled(targetSdkVersion,
uid.getUid(), packageName))
.setWakeupAlarmCount(wakeupAlarmCount)
.build();
if (mAnomalyUtils.getAnomalyAction(anomaly).isActionActive(anomaly)) {
anomalies.add(anomaly);
}
}
}
}
return anomalies;
}
@VisibleForTesting
int getWakeupAlarmCountFromUid(BatteryStats.Uid uid) {
int wakeups = 0;
final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats
= uid.getPackageStats();
for (int ipkg = packageStats.size() - 1; ipkg >= 0; ipkg--) {
final BatteryStats.Uid.Pkg ps = packageStats.valueAt(ipkg);
final ArrayMap<String, ? extends BatteryStats.Counter> alarms =
ps.getWakeupAlarmStats();
for (Map.Entry<String, ? extends BatteryStats.Counter> alarm : alarms.entrySet()) {
if (mWakeupBlacklistedTags != null
&& mWakeupBlacklistedTags.contains(alarm.getKey())) {
continue;
}
int count = alarm.getValue().getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
wakeups += count;
}
}
return wakeups;
}
}

View File

@@ -21,8 +21,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -62,7 +60,7 @@ public class AnomalyDatabaseHelper extends SQLiteOpenHelper {
String UID = "uid";
/**
* The type of the anomaly app
* @see Anomaly.AnomalyType
* @see StatsManagerConfig.AnomalyType
*/
String ANOMALY_TYPE = "anomaly_type";
/**

View File

@@ -21,8 +21,6 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArraySet;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import java.util.Objects;
import androidx.annotation.VisibleForTesting;
@@ -34,7 +32,7 @@ public class AppInfo implements Comparable<AppInfo>, Parcelable {
public final String packageName;
/**
* Anomaly type of the app
* @see Anomaly.AnomalyType
* @see StatsManagerConfig.AnomalyType
*/
public final ArraySet<Integer> anomalyTypes;
public final long screenOnTimeMs;

View File

@@ -41,7 +41,6 @@ com.android.settings.enterprise.EnterpriseSetDefaultAppsListFragment
com.android.settings.fuelgauge.AdvancedPowerUsageDetail
com.android.settings.fuelgauge.BatteryHistoryDetail
com.android.settings.fuelgauge.InactiveApps
com.android.settings.fuelgauge.PowerUsageAnomalyDetails
com.android.settings.fuelgauge.RestrictedAppDetails
com.android.settings.IccLockSettings
com.android.settings.inputmethod.InputMethodAndSubtypeEnabler

View File

@@ -45,8 +45,6 @@ import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowActivityManager;
@@ -70,9 +68,6 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
import androidx.fragment.app.FragmentActivity;
import androidx.loader.app.LoaderManager;
import androidx.preference.Preference;
@@ -129,8 +124,6 @@ public class AdvancedPowerUsageDetailTest {
@Mock
private LoaderManager mLoaderManager;
@Mock
private AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
@Mock
private BatteryStats.Timer mForegroundActivityTimer;
@Mock
private BatteryUtils mBatteryUtils;
@@ -139,7 +132,6 @@ public class AdvancedPowerUsageDetailTest {
private Preference mBackgroundPreference;
private AdvancedPowerUsageDetail mFragment;
private SettingsActivity mTestActivity;
private List<Anomaly> mAnomalies;
@Before
public void setUp() {
@@ -216,11 +208,6 @@ public class AdvancedPowerUsageDetailTest {
mBackgroundPreference = new Preference(mContext);
mFragment.mForegroundPreference = mForegroundPreference;
mFragment.mBackgroundPreference = mBackgroundPreference;
mFragment.mAnomalySummaryPreferenceController = mAnomalySummaryPreferenceController;
mAnomalies = new ArrayList<>();
mAnomalies.add(new Anomaly.Builder().setUid(UID).setType(
Anomaly.AnomalyType.WAKE_LOCK).build());
}
@After
@@ -275,7 +262,7 @@ public class AdvancedPowerUsageDetailTest {
@Test
public void testStartBatteryDetailPage_hasBasicData() {
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT, mAnomalies);
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID);
assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME))
@@ -284,8 +271,6 @@ public class AdvancedPowerUsageDetailTest {
.isEqualTo(FOREGROUND_TIME_MS);
assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT))
.isEqualTo(USAGE_PERCENT);
assertThat(mBundle.getParcelableArrayList(
AdvancedPowerUsageDetail.EXTRA_ANOMALY_LIST)).isEqualTo(mAnomalies);
}
@Test
@@ -294,7 +279,7 @@ public class AdvancedPowerUsageDetailTest {
mBatterySipper.usageTimeMs = PHONE_FOREGROUND_TIME_MS;
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT, null);
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID);
assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME))
@@ -303,8 +288,6 @@ public class AdvancedPowerUsageDetailTest {
.isEqualTo(PHONE_BACKGROUND_TIME_MS);
assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT))
.isEqualTo(USAGE_PERCENT);
assertThat(mBundle.getParcelableArrayList(
AdvancedPowerUsageDetail.EXTRA_ANOMALY_LIST)).isNull();
}
@Test
@@ -312,25 +295,21 @@ public class AdvancedPowerUsageDetailTest {
mBatterySipper.mPackages = PACKAGE_NAME;
mBatteryEntry.defaultPackageName = PACKAGE_NAME[0];
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT, mAnomalies);
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME)).isEqualTo(
PACKAGE_NAME[0]);
assertThat(mBundle.getParcelableArrayList(
AdvancedPowerUsageDetail.EXTRA_ANOMALY_LIST)).isEqualTo(mAnomalies);
}
@Test
public void testStartBatteryDetailPage_SystemApp() {
mBatterySipper.mPackages = null;
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT, null);
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_LABEL)).isEqualTo(APP_LABEL);
assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_ICON_ID)).isEqualTo(ICON_ID);
assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME)).isNull();
assertThat(mBundle.getParcelableArrayList(
AdvancedPowerUsageDetail.EXTRA_ANOMALY_LIST)).isNull();
}
@Test
@@ -339,8 +318,7 @@ public class AdvancedPowerUsageDetailTest {
mBatterySipper.mPackages = PACKAGE_NAME;
doReturn(appUid).when(mBatterySipper).getUid();
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT, null);
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
verify(mActivity).startActivityAsUser(any(Intent.class), eq(new UserHandle(10)));
}
@@ -353,7 +331,7 @@ public class AdvancedPowerUsageDetailTest {
final int currentUser = 20;
ShadowActivityManager.setCurrentUser(currentUser);
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT, null);
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
verify(mActivity).startActivityAsUser(any(Intent.class), eq(new UserHandle(currentUser)));
}
@@ -391,7 +369,7 @@ public class AdvancedPowerUsageDetailTest {
mBatteryEntry.sipper.mPackages = PACKAGE_NAME;
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT, null);
mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME))
.isEqualTo(PACKAGE_NAME[0]);
@@ -416,24 +394,4 @@ public class AdvancedPowerUsageDetailTest {
assertThat(mForegroundPreference.getSummary().toString()).isEqualTo("Used for 0 min");
assertThat(mBackgroundPreference.getSummary().toString()).isEqualTo("Active for 0 min");
}
@Test
public void testInitAnomalyInfo_anomalyNull_startAnomalyLoader() {
doReturn(null).when(mBundle)
.getParcelableArrayList(AdvancedPowerUsageDetail.EXTRA_ANOMALY_LIST);
mFragment.initAnomalyInfo();
verify(mLoaderManager).initLoader(eq(0), eq(Bundle.EMPTY), any());
}
@Test
public void testInitAnomalyInfo_anomalyExisted_updateAnomaly() {
doReturn(mAnomalies).when(mBundle)
.getParcelableArrayList(AdvancedPowerUsageDetail.EXTRA_ANOMALY_LIST);
mFragment.initAnomalyInfo();
verify(mAnomalySummaryPreferenceController).updateAnomalySummaryPreference(mAnomalies);
}
}

View File

@@ -17,6 +17,7 @@
package com.android.settings.fuelgauge;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -24,16 +25,12 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.FeatureFlagUtils;
import android.util.SparseArray;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsImpl;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -44,8 +41,6 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.List;
import androidx.preference.PreferenceGroup;
@RunWith(RobolectricTestRunner.class)
@@ -167,21 +162,6 @@ public class BatteryAppListPreferenceControllerTest {
assertThat(mPreference.getSummary().toString()).isEqualTo("2 min");
}
@Test
public void testRefreshAnomalyIcon_containsAnomaly_showAnomalyIcon() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, true);
PowerGaugePreference preference = new PowerGaugePreference(mContext);
final String key = mPreferenceController.extractKeyFromUid(UID);
final SparseArray<List<Anomaly>> anomalySparseArray = new SparseArray<>();
anomalySparseArray.append(UID, null);
preference.setKey(key);
doReturn(preference).when(mAppListGroup).findPreference(key);
mPreferenceController.refreshAnomalyIcon(anomalySparseArray);
assertThat(preference.showAnomalyIcon()).isTrue();
}
@Test
public void testShouldHideSipper_typeOvercounted_returnTrue() {
mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;

View File

@@ -51,8 +51,6 @@ import android.text.format.DateUtils;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.batterytip.AnomalyInfo;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -428,16 +426,6 @@ public class BatteryUtilsTest {
mBatteryStatsHelper, currentTimeMs)).isEqualTo(TIME_SINCE_LAST_FULL_CHARGE_MS);
}
@Test
public void testGetSummaryResIdFromAnomalyType() {
assertThat(mBatteryUtils.getSummaryResIdFromAnomalyType(Anomaly.AnomalyType.WAKE_LOCK))
.isEqualTo(R.string.battery_abnormal_wakelock_summary);
assertThat(mBatteryUtils.getSummaryResIdFromAnomalyType(Anomaly.AnomalyType.WAKEUP_ALARM))
.isEqualTo(R.string.battery_abnormal_wakeup_alarm_summary);
assertThat(mBatteryUtils.getSummaryResIdFromAnomalyType(Anomaly.AnomalyType.BLUETOOTH_SCAN))
.isEqualTo(R.string.battery_abnormal_location_summary);
}
@Test
public void testGetForegroundActivityTotalTimeMs_returnMilliseconds() {
final long rawRealtimeUs = SystemClock.elapsedRealtime() * 1000;

View File

@@ -1,183 +0,0 @@
/*
* 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;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.util.IconDrawableFactory;
import com.android.settings.SettingsActivity;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
@RunWith(SettingsRobolectricTestRunner.class)
public class PowerUsageAnomalyDetailsTest {
private static final String NAME_APP_1 = "app1";
private static final String NAME_APP_2 = "app2";
private static final String NAME_APP_3 = "app3";
private static final String PACKAGE_NAME_1 = "com.android.app1";
private static final String PACKAGE_NAME_2 = "com.android.app2";
private static final String PACKAGE_NAME_3 = "com.android.app3";
@Mock
private SettingsActivity mSettingsActivity;
@Mock
private PreferenceManager mPreferenceManager;
@Mock
private Drawable mDrawable1;
@Mock
private Drawable mDrawable2;
@Mock
private Drawable mDrawable3;
@Mock
private PackageManager mPackageManager;
@Mock
private IconDrawableFactory mIconDrawableFactory;
private PowerUsageAnomalyDetails mFragment;
private PreferenceGroup mAbnormalListGroup;
private List<Anomaly> mAnomalyList;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mAbnormalListGroup = spy(new PreferenceCategory(RuntimeEnvironment.application));
mAnomalyList = new ArrayList<>();
Anomaly anomaly1 = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.setPackageName(PACKAGE_NAME_1)
.setDisplayName(NAME_APP_1)
.build();
mAnomalyList.add(anomaly1);
Anomaly anomaly2 = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setPackageName(PACKAGE_NAME_2)
.setDisplayName(NAME_APP_2)
.build();
mAnomalyList.add(anomaly2);
Anomaly anomaly3 = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
.setPackageName(PACKAGE_NAME_3)
.setDisplayName(NAME_APP_3)
.build();
mAnomalyList.add(anomaly3);
mFragment = spy(new PowerUsageAnomalyDetails());
mFragment.mAbnormalListGroup = mAbnormalListGroup;
mFragment.mAnomalies = mAnomalyList;
mFragment.mBatteryUtils = new BatteryUtils(RuntimeEnvironment.application);
mFragment.mPackageManager = mPackageManager;
mFragment.mIconDrawableFactory = mIconDrawableFactory;
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
when(mPreferenceManager.getContext()).thenReturn(RuntimeEnvironment.application);
}
@Test
public void testRefreshUi_displayCorrectTitleAndSummary() {
final List<Preference> testPreferences = new ArrayList<>();
final ArgumentCaptor<Preference> preferenceCaptor =
ArgumentCaptor.forClass(Preference.class);
Answer<Void> prefCallable = new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
testPreferences.add(preferenceCaptor.getValue());
return null;
}
};
doAnswer(prefCallable).when(mAbnormalListGroup).addPreference(preferenceCaptor.capture());
mFragment.refreshUi();
final Preference wakelockPreference = testPreferences.get(0);
assertThat(wakelockPreference.getTitle()).isEqualTo(NAME_APP_1);
assertThat(wakelockPreference.getSummary()).isEqualTo("Keeping device awake");
final Preference wakeupPreference = testPreferences.get(1);
assertThat(wakeupPreference.getTitle()).isEqualTo(NAME_APP_2);
assertThat(wakeupPreference.getSummary()).isEqualTo("Waking up device in background");
final Preference bluetoothPreference = testPreferences.get(2);
assertThat(bluetoothPreference.getTitle()).isEqualTo(NAME_APP_3);
assertThat(bluetoothPreference.getSummary()).isEqualTo("Requesting location frequently");
}
@Test
public void testRefreshUi_iconCorrect() {
doReturn(mDrawable1).when(mFragment).getBadgedIcon(eq(PACKAGE_NAME_1), anyInt());
doReturn(mDrawable2).when(mFragment).getBadgedIcon(eq(PACKAGE_NAME_2), anyInt());
doReturn(mDrawable3).when(mFragment).getBadgedIcon(eq(PACKAGE_NAME_3), anyInt());
final List<Drawable> testIcons = new ArrayList<>();
final ArgumentCaptor<Preference> preferenceCaptor = ArgumentCaptor.forClass(
Preference.class);
Answer<Void> prefCallable = new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
testIcons.add(preferenceCaptor.getValue().getIcon());
return null;
}
};
doAnswer(prefCallable).when(mAbnormalListGroup).addPreference(preferenceCaptor.capture());
mFragment.refreshUi();
assertThat(testIcons).containsExactly(mDrawable1, mDrawable2, mDrawable3);
}
@Test
public void testStartBatteryAbnormalPage_dataCorrect() {
final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
PowerUsageAnomalyDetails.startBatteryAbnormalPage(mSettingsActivity, mFragment,
mAnomalyList);
verify(mSettingsActivity).startActivity(intent.capture());
assertThat(intent.getValue().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS)
.getParcelableArrayList(PowerUsageAnomalyDetails.EXTRA_ANOMALY_LIST))
.isEqualTo(mAnomalyList);
}
}

View File

@@ -34,7 +34,6 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -46,7 +45,6 @@ import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.applications.LayoutPreference;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
@@ -239,23 +237,6 @@ public class PowerUsageSummaryTest {
assertThat(preferenceScreenKeys).containsAllIn(preferenceKeys);
}
@Test
public void updateAnomalySparseArray() {
mFragment.mAnomalySparseArray = new SparseArray<>();
final List<Anomaly> anomalies = new ArrayList<>();
final Anomaly anomaly1 = new Anomaly.Builder().setUid(UID).build();
final Anomaly anomaly2 = new Anomaly.Builder().setUid(UID).build();
final Anomaly anomaly3 = new Anomaly.Builder().setUid(UID_2).build();
anomalies.add(anomaly1);
anomalies.add(anomaly2);
anomalies.add(anomaly3);
mFragment.updateAnomalySparseArray(anomalies);
assertThat(mFragment.mAnomalySparseArray.get(UID)).containsExactly(anomaly1, anomaly2);
assertThat(mFragment.mAnomalySparseArray.get(UID_2)).containsExactly(anomaly3);
}
@Test
public void restartBatteryTipLoader() {
//TODO: add policy logic here when BatteryTipPolicy is implemented

View File

@@ -1,138 +0,0 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.provider.Settings;
import android.text.format.DateUtils;
import android.util.KeyValueListParser;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@RunWith(SettingsRobolectricTestRunner.class)
public class AnomalyDetectionPolicyTest {
private static final String ANOMALY_DETECTION_CONSTANTS_VALUE =
"anomaly_detection_enabled=true"
+ ",wakelock_enabled=false"
+ ",wakelock_threshold=3000"
+ ",wakeup_alarm_enabled=true"
+ ",wakeup_alarm_threshold=100"
+ ",wakeup_blacklisted_tags=tag1:tag2:with%2Ccomma:with%3Acolon"
+ ",bluetooth_scan_enabled=true"
+ ",bluetooth_scan_threshold=2000";
private Context mContext;
private KeyValueListParser mKeyValueListParser;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mKeyValueListParser = spy((new KeyValueListParser(',')));
}
@Test
public void testInit_usesConfigValues() {
AnomalyDetectionPolicy anomalyDetectionPolicy = createAnomalyPolicyWithConfig();
assertThat(anomalyDetectionPolicy.anomalyDetectionEnabled).isTrue();
assertThat(anomalyDetectionPolicy.wakeLockDetectionEnabled).isFalse();
assertThat(anomalyDetectionPolicy.wakeLockThreshold).isEqualTo(3000);
assertThat(anomalyDetectionPolicy.wakeupAlarmDetectionEnabled).isTrue();
assertThat(anomalyDetectionPolicy.wakeupAlarmThreshold).isEqualTo(100);
assertThat(anomalyDetectionPolicy.wakeupBlacklistedTags)
.containsExactly("tag1", "tag2", "with,comma", "with:colon");
assertThat(anomalyDetectionPolicy.bluetoothScanDetectionEnabled).isTrue();
assertThat(anomalyDetectionPolicy.bluetoothScanThreshold).isEqualTo(2000);
}
@Test
public void testInit_defaultValues() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.ANOMALY_DETECTION_CONSTANTS, "");
// Mock it to avoid noSuchMethodError
doReturn(true).when(mKeyValueListParser).getBoolean(anyString(), eq(true));
doReturn(false).when(mKeyValueListParser).getBoolean(anyString(), eq(false));
AnomalyDetectionPolicy anomalyDetectionPolicy = new AnomalyDetectionPolicy(mContext);
ReflectionHelpers.setField(anomalyDetectionPolicy, "mParser", mKeyValueListParser);
assertThat(anomalyDetectionPolicy.anomalyDetectionEnabled).isFalse();
assertThat(anomalyDetectionPolicy.wakeLockDetectionEnabled).isFalse();
assertThat(anomalyDetectionPolicy.wakeLockThreshold).isEqualTo(DateUtils.HOUR_IN_MILLIS);
assertThat(anomalyDetectionPolicy.wakeupAlarmDetectionEnabled).isFalse();
assertThat(anomalyDetectionPolicy.wakeupAlarmThreshold).isEqualTo(10);
assertThat(anomalyDetectionPolicy.wakeupBlacklistedTags).isNull();
assertThat(anomalyDetectionPolicy.bluetoothScanDetectionEnabled).isFalse();
assertThat(anomalyDetectionPolicy.bluetoothScanThreshold)
.isEqualTo(30 * DateUtils.MINUTE_IN_MILLIS);
}
@Test
public void testIsAnomalyDetectorEnabled_usesConfigValues() {
AnomalyDetectionPolicy policy = createAnomalyPolicyWithConfig();
assertThat(policy.isAnomalyDetectorEnabled(Anomaly.AnomalyType.WAKE_LOCK)).isFalse();
assertThat(policy.isAnomalyDetectorEnabled(Anomaly.AnomalyType.WAKEUP_ALARM)).isTrue();
assertThat(policy.isAnomalyDetectorEnabled(Anomaly.AnomalyType.BLUETOOTH_SCAN)).isTrue();
}
@Test
public void testIsAnomalyDetectorEnabled_usesDefaultValues() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.ANOMALY_DETECTION_CONSTANTS, "");
// Mock it to avoid noSuchMethodError
doReturn(true).when(mKeyValueListParser).getBoolean(anyString(), eq(true));
doReturn(false).when(mKeyValueListParser).getBoolean(anyString(), eq(false));
AnomalyDetectionPolicy policy = new AnomalyDetectionPolicy(mContext);
ReflectionHelpers.setField(policy, "mParser", mKeyValueListParser);
assertThat(policy.isAnomalyDetectorEnabled(Anomaly.AnomalyType.WAKE_LOCK)).isFalse();
assertThat(policy.isAnomalyDetectorEnabled(Anomaly.AnomalyType.WAKEUP_ALARM)).isFalse();
assertThat(policy.isAnomalyDetectorEnabled(Anomaly.AnomalyType.BLUETOOTH_SCAN)).isFalse();
}
private AnomalyDetectionPolicy createAnomalyPolicyWithConfig() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.ANOMALY_DETECTION_CONSTANTS, ANOMALY_DETECTION_CONSTANTS_VALUE);
// Mock it to avoid noSuchMethodError
doReturn(true).when(mKeyValueListParser)
.getBoolean(AnomalyDetectionPolicy.KEY_ANOMALY_DETECTION_ENABLED, false);
doReturn(false).when(mKeyValueListParser)
.getBoolean(AnomalyDetectionPolicy.KEY_WAKELOCK_DETECTION_ENABLED, false);
doReturn(true).when(mKeyValueListParser)
.getBoolean(AnomalyDetectionPolicy.KEY_WAKEUP_ALARM_DETECTION_ENABLED, false);
doReturn(true).when(mKeyValueListParser)
.getBoolean(AnomalyDetectionPolicy.KEY_BLUETOOTH_SCAN_DETECTION_ENABLED, false);
final AnomalyDetectionPolicy policy = new AnomalyDetectionPolicy(mContext);
ReflectionHelpers.setField(policy, "mParser", mKeyValueListParser);
return policy;
}
}

View File

@@ -1,203 +0,0 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.permission.RuntimePermissionPresenter;
import android.os.Build;
import com.android.settings.R;
import com.android.settings.fuelgauge.anomaly.action.AnomalyAction;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowRuntimePermissionPresenter;
import com.android.settingslib.testutils.FragmentTestUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowAlertDialog;
import org.robolectric.shadows.ShadowDialog;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = ShadowRuntimePermissionPresenter.class)
public class AnomalyDialogFragmentTest {
private static final String PACKAGE_NAME = "com.android.app";
private static final String DISPLAY_NAME = "app";
private static final int UID = 111;
@Mock
private AnomalyUtils mAnomalyUtils;
@Mock
private AnomalyAction mAnomalyAction;
@Mock
private RuntimePermissionPresenter mRuntimePermissionPresenter;
private Anomaly mWakeLockAnomaly;
private Anomaly mWakeupAlarmAnomaly;
private Anomaly mWakeupAlarmAnomaly2;
private Anomaly mBluetoothAnomaly;
private AnomalyDialogFragment mAnomalyDialogFragment;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mWakeLockAnomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.setUid(UID)
.setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME)
.build();
mWakeupAlarmAnomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setUid(UID)
.setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME)
.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()
.setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
.setUid(UID)
.setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME)
.build();
FakeFeatureFactory.setupForTest();
ShadowRuntimePermissionPresenter.setRuntimePermissionPresenter(mRuntimePermissionPresenter);
}
@Test
public void testOnCreateDialog_hasCorrectData() {
mAnomalyDialogFragment =
AnomalyDialogFragment.newInstance(mWakeLockAnomaly, 0 /* metricskey */);
FragmentTestUtils.startFragment(mAnomalyDialogFragment);
assertThat(mAnomalyDialogFragment.mAnomaly).isEqualTo(mWakeLockAnomaly);
}
@Test
public void testOnCreateDialog_wakelockAnomaly_fireForceStopDialog() {
mAnomalyDialogFragment =
AnomalyDialogFragment.newInstance(mWakeLockAnomaly, 0 /* metricskey */);
FragmentTestUtils.startFragment(mAnomalyDialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
assertThat(dialog).isNotNull();
ShadowAlertDialog shadowDialog = Shadows.shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
mContext.getString(R.string.dialog_stop_message, 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
public void testOnCreateDialog_wakeupAlarmAnomalyPriorO_fireStopAndBackgroundCheckDialog() {
mAnomalyDialogFragment =
AnomalyDialogFragment.newInstance(mWakeupAlarmAnomaly, 0 /* metricskey */);
FragmentTestUtils.startFragment(mAnomalyDialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
assertThat(dialog).isNotNull();
ShadowAlertDialog shadowDialog = Shadows.shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
mContext.getString(R.string.dialog_background_check_message,
mWakeLockAnomaly.displayName));
assertThat(shadowDialog.getTitle()).isEqualTo(
mContext.getString(R.string.dialog_background_check_title));
assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo(
mContext.getString(R.string.dialog_background_check_ok));
assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
mContext.getString(R.string.dlg_cancel));
}
@Test
public void testOnCreateDialog_wakeupAlarmAnomalyTargetingO_fireForceStopDialog() {
mAnomalyDialogFragment =
AnomalyDialogFragment.newInstance(mWakeupAlarmAnomaly2, 0 /* metricskey */);
FragmentTestUtils.startFragment(mAnomalyDialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
assertThat(dialog).isNotNull();
ShadowAlertDialog shadowDialog = Shadows.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
public void testOnCreateDialog_bluetoothAnomaly_fireLocationCheckDialog() {
mAnomalyDialogFragment = spy(AnomalyDialogFragment.newInstance(mBluetoothAnomaly,
0 /* metricskey */));
mAnomalyDialogFragment.mAnomalyUtils = mAnomalyUtils;
doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any());
doNothing().when(mAnomalyDialogFragment).initAnomalyUtils();
doReturn(Anomaly.AnomalyActionType.LOCATION_CHECK).when(mAnomalyAction).getActionType();
FragmentTestUtils.startFragment(mAnomalyDialogFragment);
final AlertDialog dialog = (AlertDialog) ShadowDialog.getLatestDialog();
assertThat(dialog).isNotNull();
ShadowAlertDialog shadowDialog = Shadows.shadowOf(dialog);
assertThat(shadowDialog.getMessage()).isEqualTo(
mContext.getString(R.string.dialog_location_message,
mWakeLockAnomaly.displayName));
assertThat(shadowDialog.getTitle()).isEqualTo(
mContext.getString(R.string.dialog_location_title));
assertThat(dialog.getButton(DialogInterface.BUTTON_POSITIVE).getText()).isEqualTo(
mContext.getString(R.string.dialog_location_ok));
assertThat(dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText()).isEqualTo(
mContext.getString(R.string.dlg_cancel));
}
}

View File

@@ -1,139 +0,0 @@
/*
* 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;
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.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserManager;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.anomaly.checker.BluetoothScanAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeupAlarmAnomalyDetector;
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 java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class AnomalyLoaderTest {
private static final String PACKAGE_NAME = "com.android.settings";
private static final CharSequence DISPLAY_NAME = "Settings";
private static final int UID = 0;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
private WakeLockAnomalyDetector mWakeLockAnomalyDetector;
@Mock
private WakeupAlarmAnomalyDetector mWakeupAlarmAnomalyDetector;
@Mock
private BluetoothScanAnomalyDetector mBluetoothScanAnomalyDetector;
@Mock
private AnomalyDetectionPolicy mAnomalyDetectionPolicy;
@Mock
private UserManager mUserManager;
private Anomaly mWakeLockAnomaly;
private Anomaly mWakeupAlarmAnomaly;
private Anomaly mBluetoothScanAnomaly;
private List<Anomaly> mWakeLockAnomalies;
private List<Anomaly> mWakeupAlarmAnomalies;
private List<Anomaly> mBluetoothScanAnomalies;
private AnomalyLoader mAnomalyLoader;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest();
doReturn(true).when(mAnomalyDetectionPolicy).isAnomalyDetectorEnabled(anyInt());
doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
when(mContext.getPackageManager().getPackageUid(anyString(), anyInt())).thenReturn(UID);
mWakeLockAnomalies = new ArrayList<>();
mWakeLockAnomaly = createAnomaly(Anomaly.AnomalyType.WAKE_LOCK);
mWakeLockAnomalies.add(mWakeLockAnomaly);
doReturn(mWakeLockAnomalies).when(mWakeLockAnomalyDetector).detectAnomalies(any(), any());
mWakeupAlarmAnomalies = new ArrayList<>();
mWakeupAlarmAnomaly = createAnomaly(Anomaly.AnomalyType.WAKEUP_ALARM);
mWakeupAlarmAnomalies.add(mWakeupAlarmAnomaly);
doReturn(mWakeupAlarmAnomalies)
.when(mWakeupAlarmAnomalyDetector).detectAnomalies(any(), any());
mBluetoothScanAnomalies = new ArrayList<>();
mBluetoothScanAnomaly = createAnomaly(Anomaly.AnomalyType.BLUETOOTH_SCAN);
mBluetoothScanAnomalies.add(mBluetoothScanAnomaly);
doReturn(mBluetoothScanAnomalies)
.when(mBluetoothScanAnomalyDetector).detectAnomalies(any(), any());
mAnomalyLoader = new AnomalyLoader(mContext, mBatteryStatsHelper, null,
mAnomalyDetectionPolicy);
mAnomalyLoader.mAnomalyUtils = spy(new AnomalyUtils(mContext));
}
@Test
public void testLoadInBackground_containsValidAnomalies() {
doReturn(mWakeLockAnomalyDetector).when(mAnomalyLoader.mAnomalyUtils).getAnomalyDetector(
Anomaly.AnomalyType.WAKE_LOCK);
doReturn(mWakeupAlarmAnomalyDetector).when(mAnomalyLoader.mAnomalyUtils).getAnomalyDetector(
Anomaly.AnomalyType.WAKEUP_ALARM);
doReturn(mBluetoothScanAnomalyDetector).when(
mAnomalyLoader.mAnomalyUtils).getAnomalyDetector(
Anomaly.AnomalyType.BLUETOOTH_SCAN);
List<Anomaly> anomalies = mAnomalyLoader.loadInBackground();
assertThat(anomalies)
.containsExactly(mWakeLockAnomaly, mWakeupAlarmAnomaly, mBluetoothScanAnomaly);
}
private Anomaly createAnomaly(@Anomaly.AnomalyType int type) {
return new Anomaly.Builder()
.setType(type)
.setUid(UID)
.setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME)
.build();
}
@Test
public void testGenerateFakeData() {
List<Anomaly> anomalies = mAnomalyLoader.generateFakeData();
assertThat(anomalies)
.containsExactly(mWakeLockAnomaly, mWakeupAlarmAnomaly, mBluetoothScanAnomaly);
}
}

View File

@@ -1,64 +0,0 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsRobolectricTestRunner.class)
public class AnomalyPreferenceTest {
@Anomaly.AnomalyType
private static final int ANOMALY_TYPE = Anomaly.AnomalyType.WAKE_LOCK;
private static final String PACKAGE_NAME = "com.android.app";
private static final String DISPLAY_NAME = "app";
private static final int UID = 111;
private Context mContext;
private Anomaly mAnomaly;
private AnomalyPreference mAnomalyPreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mAnomaly = new Anomaly.Builder()
.setType(ANOMALY_TYPE)
.setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME)
.setUid(UID)
.build();
}
@Test
public void testAnomalyPreference_containsCorrectData() {
mAnomalyPreference = new AnomalyPreference(mContext, mAnomaly);
assertThat(mAnomalyPreference.getTitle()).isEqualTo(DISPLAY_NAME);
}
}

View File

@@ -1,150 +0,0 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.List;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@RunWith(SettingsRobolectricTestRunner.class)
public class AnomalySummaryPreferenceControllerTest {
@Anomaly.AnomalyType
private static final int ANOMALY_TYPE = Anomaly.AnomalyType.WAKE_LOCK;
private static final String PACKAGE_NAME = "com.android.app";
private static final String DISPLAY_NAME = "appName";
private static final int UID = 111;
@Mock
private InstrumentedPreferenceFragment mFragment;
@Mock
private FragmentManager mFragmentManager;
@Mock
private FragmentTransaction mFragmentTransaction;
@Mock
private SettingsActivity mSettingsActivity;
@Mock
private PreferenceScreen mPreferenceScreen;
private AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
private Preference mPreference;
private Context mContext;
private List<Anomaly> mAnomalyList;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPreference = new Preference(mContext);
mPreference.setKey(AnomalySummaryPreferenceController.ANOMALY_KEY);
when(mFragment.getPreferenceScreen()).thenReturn(mPreferenceScreen);
when(mFragment.getFragmentManager()).thenReturn(mFragmentManager);
when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
when(mFragment.getContext()).thenReturn(mContext);
when(mSettingsActivity.getApplicationContext()).thenReturn(mContext);
when(mPreferenceScreen.findPreference(any())).thenReturn(mPreference);
mAnomalyList = new ArrayList<>();
mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController(
mSettingsActivity, mFragment);
}
@Test
public void testUpdateHighUsageSummaryPreference_hasCorrectData() {
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(mAnomalyList);
assertThat(mAnomalySummaryPreferenceController.mAnomalies).isEqualTo(mAnomalyList);
}
@Test
public void testUpdateAnomalySummaryPreference_oneAnomaly_showCorrectSummary() {
mAnomalyList.add(createTestAnomaly());
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(mAnomalyList);
assertThat(mPreference.getTitle()).isEqualTo("appName draining battery");
assertThat(mPreference.getSummary()).isEqualTo("Keeping device awake");
}
@Test
public void testUpdateAnomalySummaryPreference_emptyAnomaly_preferenceInvisible() {
mPreference.setVisible(true);
mAnomalyList.clear();
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(mAnomalyList);
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void testUpdateAnomalySummaryPreference_multipleAnomalies_showCorrectSummary() {
mAnomalyList.add(createTestAnomaly());
mAnomalyList.add(createTestAnomaly());
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(mAnomalyList);
assertThat(mPreference.getTitle()).isEqualTo("Apps draining battery");
assertThat(mPreference.getSummary()).isEqualTo("2 apps misbehaving");
}
@Test
public void testOnPreferenceTreeClick_oneAnomaly_showDialog() {
mAnomalyList.add(createTestAnomaly());
mAnomalySummaryPreferenceController.mAnomalies = mAnomalyList;
mAnomalySummaryPreferenceController.onPreferenceTreeClick(mPreference);
verify(mFragmentManager).beginTransaction();
verify(mFragmentTransaction).add(any(), anyString());
verify(mFragmentTransaction).commit();
}
private Anomaly createTestAnomaly() {
return new Anomaly.Builder()
.setType(ANOMALY_TYPE)
.setUid(UID)
.setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME)
.build();
}
}

View File

@@ -1,77 +0,0 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import android.os.Build;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(SettingsRobolectricTestRunner.class)
public class AnomalyTest {
private static int TYPE = Anomaly.AnomalyType.WAKE_LOCK;
private static int UID = 111;
private static int SDK_VERSION = Build.VERSION_CODES.L;
private static long WAKE_LOCK_TIME_MS = 1500;
private static String PACKAGE_NAME = "com.android.settings";
private static String DISPLAY_NAME = "settings";
private static long BLUETOOTH_TIME_MS = 2555555;
private static int WAKEUP_ALARM_COUNT = 100;
private Anomaly mAnomaly;
@Before
public void setUp() {
mAnomaly = new Anomaly.Builder()
.setType(TYPE)
.setUid(UID)
.setWakeLockTimeMs(WAKE_LOCK_TIME_MS)
.setPackageName(PACKAGE_NAME)
.setDisplayName(DISPLAY_NAME)
.setTargetSdkVersion(SDK_VERSION)
.setBackgroundRestrictionEnabled(true)
.setBluetoothScanningTimeMs(BLUETOOTH_TIME_MS)
.setWakeupAlarmCount(WAKEUP_ALARM_COUNT)
.build();
}
@Test
public void testBuilder_buildCorrectly() {
assertThat(mAnomaly.type).isEqualTo(TYPE);
assertThat(mAnomaly.uid).isEqualTo(UID);
assertThat(mAnomaly.wakelockTimeMs).isEqualTo(WAKE_LOCK_TIME_MS);
assertThat(mAnomaly.packageName).isEqualTo(PACKAGE_NAME);
assertThat(mAnomaly.displayName).isEqualTo(DISPLAY_NAME);
assertThat(mAnomaly.targetSdkVersion).isEqualTo(SDK_VERSION);
assertThat(mAnomaly.backgroundRestrictionEnabled).isTrue();
assertThat(mAnomaly.wakeupAlarmCount).isEqualTo(WAKEUP_ALARM_COUNT);
assertThat(mAnomaly.bluetoothScanningTimeMs).isEqualTo(BLUETOOTH_TIME_MS);
}
@Test
public void testToString() {
assertThat(mAnomaly.toString()).isEqualTo(
"type=wakelock uid=111 package=com.android.settings displayName=settings"
+ " wakelockTimeMs=1500 wakeupAlarmCount=100 bluetoothTimeMs=2555555");
}
}

View File

@@ -1,170 +0,0 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import android.os.Build;
import android.util.Pair;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.fuelgauge.anomaly.action.ForceStopAction;
import com.android.settings.fuelgauge.anomaly.action.StopAndBackgroundCheckAction;
import com.android.settings.fuelgauge.anomaly.checker.WakeLockAnomalyDetector;
import com.android.settings.fuelgauge.anomaly.checker.WakeupAlarmAnomalyDetector;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowKeyValueListParser;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = ShadowKeyValueListParser.class)
public class AnomalyUtilsTest {
private static final String PACKAGE_NAME_WAKEUP = "com.android.app1";
private static final String PACKAGE_NAME_WAKELOCK = "com.android.app2";
private static final int CONTEXT_ID = 55;
@Mock
private MetricsFeatureProvider mMetricsFeatureProvider;
private AnomalyUtils mAnomalyUtils;
private Anomaly mWakeupAnomaly;
private Anomaly mWakeLockAnomaly;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mAnomalyUtils = new AnomalyUtils(RuntimeEnvironment.application);
mWakeLockAnomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.setPackageName(PACKAGE_NAME_WAKELOCK)
.build();
mWakeupAnomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setPackageName(PACKAGE_NAME_WAKEUP)
.build();
}
@Test
public void testGetAnomalyAction_typeWakeLock_returnForceStop() {
Anomaly anomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.build();
assertThat(mAnomalyUtils.getAnomalyAction(anomaly)).isInstanceOf(
ForceStopAction.class);
}
@Test
public void testGetAnomalyDetector_typeWakeLock_returnWakeLockDetector() {
assertThat(mAnomalyUtils.getAnomalyDetector(Anomaly.AnomalyType.WAKE_LOCK)).isInstanceOf(
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
public void testGetAnomalyDetector_typeWakeUpAlarm_returnWakeUpAlarmDetector() {
assertThat(mAnomalyUtils.getAnomalyDetector(Anomaly.AnomalyType.WAKEUP_ALARM)).isInstanceOf(
WakeupAlarmAnomalyDetector.class);
}
@Test
public void testLogAnomaly() {
mAnomalyUtils.logAnomaly(mMetricsFeatureProvider, mWakeLockAnomaly, CONTEXT_ID);
verify(mMetricsFeatureProvider).action(RuntimeEnvironment.application,
MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKELOCK,
PACKAGE_NAME_WAKELOCK,
Pair.create(
MetricsProto.MetricsEvent.FIELD_CONTEXT,
CONTEXT_ID),
Pair.create(
MetricsProto.MetricsEvent.FIELD_ANOMALY_ACTION_TYPE,
Anomaly.AnomalyActionType.FORCE_STOP));
}
@Test
public void testLogAnomalies() {
final List<Anomaly> anomalies = new ArrayList<>();
anomalies.add(mWakeLockAnomaly);
anomalies.add(mWakeupAnomaly);
mAnomalyUtils.logAnomalies(mMetricsFeatureProvider, anomalies, CONTEXT_ID);
verify(mMetricsFeatureProvider).action(RuntimeEnvironment.application,
MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKELOCK,
PACKAGE_NAME_WAKELOCK,
Pair.create(
MetricsProto.MetricsEvent.FIELD_CONTEXT,
CONTEXT_ID),
Pair.create(
MetricsProto.MetricsEvent.FIELD_ANOMALY_ACTION_TYPE,
Anomaly.AnomalyActionType.FORCE_STOP));
verify(mMetricsFeatureProvider).action(RuntimeEnvironment.application,
MetricsProto.MetricsEvent.ANOMALY_TYPE_WAKEUP_ALARM,
PACKAGE_NAME_WAKEUP,
Pair.create(
MetricsProto.MetricsEvent.FIELD_CONTEXT,
CONTEXT_ID),
Pair.create(
MetricsProto.MetricsEvent.FIELD_ANOMALY_ACTION_TYPE,
Anomaly.AnomalyActionType.STOP_AND_BACKGROUND_CHECK));
}
}

View File

@@ -1,89 +0,0 @@
/*
* 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 org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.app.AppOpsManager;
import android.content.Context;
import android.util.Pair;
import com.android.internal.logging.nano.MetricsProto;
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;
@RunWith(SettingsRobolectricTestRunner.class)
public class AnomalyActionTest {
private static final String PACKAGE_NAME = "com.android.app";
private static final int UID = 111;
private static final int ACTION_KEY = 2;
private static final int METRIC_KEY = 3;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private AppOpsManager mAppOpsManager;
private Anomaly mAnomaly;
private TestAnomalyAction mTestAnomalyAction;
private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFeatureFactory = FakeFeatureFactory.setupForTest();
doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
mAnomaly = new Anomaly.Builder().setUid(UID).setPackageName(PACKAGE_NAME).build();
mTestAnomalyAction = new TestAnomalyAction(mContext);
}
@Test
public void testHandlePositiveAction_logAction() {
mTestAnomalyAction.handlePositiveAction(mAnomaly, METRIC_KEY);
verify(mFeatureFactory.metricsFeatureProvider).action(mContext, ACTION_KEY, PACKAGE_NAME,
Pair.create(MetricsProto.MetricsEvent.FIELD_CONTEXT, METRIC_KEY));
}
private class TestAnomalyAction extends AnomalyAction {
private TestAnomalyAction(Context context) {
super(context);
mActionMetricKey = ACTION_KEY;
}
@Override
public boolean isActionActive(Anomaly anomaly) {
return false;
}
@Override
public int getActionType() {
return 0;
}
}
}

View File

@@ -1,101 +0,0 @@
/*
* 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.app.AppOpsManager;
import android.content.Context;
import android.os.Build;
import com.android.settings.fuelgauge.BatteryUtils;
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;
@RunWith(SettingsRobolectricTestRunner.class)
public class BackgroundCheckActionTest {
private static final String PACKAGE_NAME = "com.android.app";
private static final int UID = 111;
private static final int SDK_VERSION = Build.VERSION_CODES.L;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private AppOpsManager mAppOpsManager;
@Mock
private BatteryUtils mBatteryUtils;
private Anomaly mAnomaly;
private BackgroundCheckAction mBackgroundCheckAction;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest();
doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
mAnomaly = new Anomaly.Builder()
.setUid(UID)
.setPackageName(PACKAGE_NAME)
.setTargetSdkVersion(SDK_VERSION)
.build();
mBackgroundCheckAction = new BackgroundCheckAction(mContext);
mBackgroundCheckAction.mBatteryUtils = mBatteryUtils;
}
@Test
public void testHandlePositiveAction_forceStopPackage() {
mBackgroundCheckAction.handlePositiveAction(mAnomaly, 0 /* metricskey */);
verify(mAppOpsManager).setMode(AppOpsManager.OP_RUN_IN_BACKGROUND, UID, PACKAGE_NAME,
AppOpsManager.MODE_IGNORED);
}
@Test
public void testIsActionActive_modeAllowed_returnTrue() {
doReturn(false).when(mBatteryUtils)
.isBackgroundRestrictionEnabled(SDK_VERSION, UID, PACKAGE_NAME);
assertThat(mBackgroundCheckAction.isActionActive(mAnomaly)).isTrue();
}
@Test
public void testIsActionActive_modeIgnored_returnFalse() {
doReturn(true).when(mBatteryUtils)
.isBackgroundRestrictionEnabled(SDK_VERSION, UID, PACKAGE_NAME);
assertThat(mBackgroundCheckAction.isActionActive(mAnomaly)).isFalse();
}
@Test
public void testConstructor_batteryUtilsNotNull() {
mBackgroundCheckAction = new BackgroundCheckAction(mContext);
assertThat(mBackgroundCheckAction.mBatteryUtils).isNotNull();
}
}

View File

@@ -1,89 +0,0 @@
/*
* 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.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
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;
@RunWith(SettingsRobolectricTestRunner.class)
public class ForceStopActionTest {
private static final String PACKAGE_NAME = "com.android.app";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private ActivityManager mActivityManager;
@Mock
private ApplicationInfo mApplicationInfo;
@Mock
private PackageManager mPackageManager;
private Anomaly mAnomaly;
private ForceStopAction mForceStopAction;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest();
doReturn(mActivityManager).when(mContext).getSystemService(Context.ACTIVITY_SERVICE);
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mApplicationInfo).when(mPackageManager)
.getApplicationInfo(PACKAGE_NAME, PackageManager.GET_META_DATA);
mAnomaly = new Anomaly.Builder().setPackageName(PACKAGE_NAME).build();
mForceStopAction = new ForceStopAction(mContext);
}
@Test
public void testHandlePositiveAction_forceStopPackage() {
mForceStopAction.handlePositiveAction(mAnomaly, 0 /* metricskey */);
verify(mActivityManager).forceStopPackage(PACKAGE_NAME);
}
@Test
public void testIsActionActive_appStopped_returnFalse() {
mApplicationInfo.flags = ApplicationInfo.FLAG_STOPPED;
assertThat(mForceStopAction.isActionActive(mAnomaly)).isFalse();
}
@Test
public void testIsActionActive_appRunning_returnTrue() {
mApplicationInfo.flags = 0;
assertThat(mForceStopAction.isActionActive(mAnomaly)).isTrue();
}
}

View File

@@ -1,83 +0,0 @@
/*
* 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 android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowPermissionChecker;
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(shadows = ShadowPermissionChecker.class)
public class LocationCheckActionTest {
private static final String PACKAGE_NAME = "com.android.app";
private static final int UID = 12345;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
private Anomaly mAnomaly;
private LocationCheckAction mLocationCheckAction;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory.setupForTest();
mLocationCheckAction = new LocationCheckAction(mContext, null);
mAnomaly = new Anomaly.Builder()
.setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
.setPackageName(PACKAGE_NAME)
.setUid(UID)
.build();
ShadowPermissionChecker.clear();
}
@Test
public void testIsActionActive_coarseLocationGranted_returnTrue() {
ShadowPermissionChecker.addPermission(Manifest.permission.ACCESS_COARSE_LOCATION, -1, UID,
PACKAGE_NAME, PackageManager.PERMISSION_GRANTED);
assertThat(mLocationCheckAction.isActionActive(mAnomaly)).isTrue();
}
@Test
public void testIsActionActive_fineLocationGranted_returnTrue() {
ShadowPermissionChecker.addPermission(Manifest.permission.ACCESS_FINE_LOCATION, -1, UID,
PACKAGE_NAME, PackageManager.PERMISSION_GRANTED);
assertThat(mLocationCheckAction.isActionActive(mAnomaly)).isTrue();
}
@Test
public void testIsActionActive_noLocationGranted_returnFalse() {
assertThat(mLocationCheckAction.isActionActive(mAnomaly)).isFalse();
}
}

View File

@@ -1,88 +0,0 @@
/*
* 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.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;
@RunWith(SettingsRobolectricTestRunner.class)
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();
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

@@ -1,157 +0,0 @@
/*
* 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.checker;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.BatteryStats;
import android.text.format.DateUtils;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly;
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.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class BluetoothScanAnomalyDetectorTest {
private static final String TARGET_PACKAGE_NAME = "com.android.app";
private static final int ANOMALY_UID = 111;
private static final int NORMAL_UID = 222;
private static final int TARGET_UID = 333;
private static final long ANOMALY_BLUETOOTH_SCANNING_TIME = DateUtils.HOUR_IN_MILLIS;
private static final long NORMAL_BLUETOOTH_SCANNING_TIME = DateUtils.MINUTE_IN_MILLIS;
@Mock
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
private BatterySipper mAnomalySipper;
@Mock
private BatterySipper mNormalSipper;
@Mock
private BatterySipper mTargetSipper;
@Mock
private BatteryStats.Uid mAnomalyUid;
@Mock
private BatteryStats.Uid mNormalUid;
@Mock
private BatteryStats.Uid mTargetUid;
@Mock
private BatteryUtils mBatteryUtils;
@Mock
private AnomalyDetectionPolicy mPolicy;
@Mock
private AnomalyAction mAnomalyAction;
@Mock
private AnomalyUtils mAnomalyUtils;
private BluetoothScanAnomalyDetector mBluetoothScanAnomalyDetector;
private Context mContext;
private List<BatterySipper> mUsageList;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
ReflectionHelpers.setField(mPolicy, "bluetoothScanThreshold",
30 * DateUtils.MINUTE_IN_MILLIS);
doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any());
mAnomalySipper.uidObj = mAnomalyUid;
doReturn(ANOMALY_UID).when(mAnomalyUid).getUid();
mNormalSipper.uidObj = mNormalUid;
doReturn(NORMAL_UID).when(mNormalUid).getUid();
mTargetSipper.uidObj = mTargetUid;
doReturn(TARGET_UID).when(mTargetUid).getUid();
mUsageList = new ArrayList<>();
mUsageList.add(mAnomalySipper);
mUsageList.add(mNormalSipper);
mUsageList.add(mTargetSipper);
when(mBatteryStatsHelper.getUsageList()).thenReturn(mUsageList);
mBluetoothScanAnomalyDetector = spy(new BluetoothScanAnomalyDetector(mContext, mPolicy,
mAnomalyUtils));
mBluetoothScanAnomalyDetector.mBatteryUtils = mBatteryUtils;
doReturn(false).when(mBatteryUtils).shouldHideSipper(any());
doReturn(true).when(mAnomalyAction).isActionActive(any());
doReturn(ANOMALY_BLUETOOTH_SCANNING_TIME).when(
mBluetoothScanAnomalyDetector).getBluetoothUnoptimizedBgTimeMs(eq(mAnomalyUid),
anyLong());
doReturn(ANOMALY_BLUETOOTH_SCANNING_TIME).when(
mBluetoothScanAnomalyDetector).getBluetoothUnoptimizedBgTimeMs(eq(mTargetUid),
anyLong());
doReturn(NORMAL_BLUETOOTH_SCANNING_TIME).when(
mBluetoothScanAnomalyDetector).getBluetoothUnoptimizedBgTimeMs(eq(mNormalUid),
anyLong());
}
@Test
public void testDetectAnomalies_containsAnomaly_detectIt() {
doReturn(-1).when(mBatteryUtils).getPackageUid(nullable(String.class));
final Anomaly anomaly = createBluetoothAnomaly(ANOMALY_UID);
final Anomaly targetAnomaly = createBluetoothAnomaly(TARGET_UID);
List<Anomaly> mAnomalies =
mBluetoothScanAnomalyDetector.detectAnomalies(mBatteryStatsHelper);
assertThat(mAnomalies).containsExactly(anomaly, targetAnomaly);
}
@Test
public void testDetectAnomalies_detectTargetAnomaly_detectIt() {
doReturn(TARGET_UID).when(mBatteryUtils).getPackageUid(TARGET_PACKAGE_NAME);
final Anomaly targetAnomaly = createBluetoothAnomaly(TARGET_UID);
List<Anomaly> mAnomalies =
mBluetoothScanAnomalyDetector.detectAnomalies(mBatteryStatsHelper, TARGET_PACKAGE_NAME);
assertThat(mAnomalies).containsExactly(targetAnomaly);
}
private Anomaly createBluetoothAnomaly(int uid) {
return new Anomaly.Builder()
.setUid(uid)
.setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
.setBluetoothScanningTimeMs(ANOMALY_BLUETOOTH_SCANNING_TIME)
.build();
}
}

View File

@@ -1,191 +0,0 @@
/*
* 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.checker;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.BatteryStats;
import android.text.format.DateUtils;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly;
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.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@RunWith(SettingsRobolectricTestRunner.class)
public class WakeLockAnomalyDetectorTest {
private static final String TARGET_PACKAGE_NAME = "com.android.app";
private static final long ANOMALY_WAKELOCK_TIME_MS = 2 * DateUtils.HOUR_IN_MILLIS;
private static final long NORMAL_WAKELOCK_TIME_MS = DateUtils.SECOND_IN_MILLIS;
private static final long WAKELOCK_THRESHOLD_MS = DateUtils.HOUR_IN_MILLIS;
private static final int ANOMALY_UID = 111;
private static final int NORMAL_UID = 222;
private static final int TARGET_UID = 333;
private static final int INACTIVE_UID = 444;
@Mock
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
private BatterySipper mAnomalySipper;
@Mock
private BatterySipper mTargetSipper;
@Mock
private BatterySipper mNormalSipper;
@Mock
private BatterySipper mInactiveSipper;
@Mock
private BatteryStats.Uid mAnomalyUid;
@Mock
private BatteryStats.Uid mNormalUid;
@Mock
private BatteryStats.Uid mTargetUid;
@Mock
private BatteryStats.Uid mInactiveUid;
@Mock
private BatteryUtils mBatteryUtils;
@Mock
private PackageManager mPackageManager;
@Mock
private ApplicationInfo mApplicationInfo;
@Mock
private AnomalyDetectionPolicy mPolicy;
@Mock
private AnomalyAction mAnomalyAction;
@Mock
private AnomalyUtils mAnomalyUtils;
private WakeLockAnomalyDetector mWakelockAnomalyDetector;
private Context mContext;
private List<BatterySipper> mUsageList;
private Anomaly mAnomaly;
private Anomaly mTargetAnomaly;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
ReflectionHelpers.setField(mPolicy, "wakeLockThreshold", WAKELOCK_THRESHOLD_MS);
doReturn(false).when(mBatteryUtils).shouldHideSipper(nullable(BatterySipper.class));
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mApplicationInfo).when(mPackageManager)
.getApplicationInfo(nullable(String.class), anyInt());
doReturn(true).when(mAnomalyAction).isActionActive(any());
doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any());
mWakelockAnomalyDetector =
spy(new WakeLockAnomalyDetector(mContext, mPolicy, mAnomalyUtils));
mWakelockAnomalyDetector.mBatteryUtils = mBatteryUtils;
mAnomalySipper.uidObj = mAnomalyUid;
doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector)
.getBackgroundTotalDurationMs(eq(mAnomalyUid), anyLong());
doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector)
.getCurrentDurationMs(eq(mAnomalyUid), anyLong());
doReturn(ANOMALY_UID).when(mAnomalyUid).getUid();
mNormalSipper.uidObj = mNormalUid;
doReturn(NORMAL_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector)
.getBackgroundTotalDurationMs(eq(mNormalUid), anyLong());
doReturn(0L).when(mWakelockAnomalyDetector)
.getCurrentDurationMs(eq(mNormalUid), anyLong());
doReturn(NORMAL_UID).when(mNormalUid).getUid();
mTargetSipper.uidObj = mTargetUid;
doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector)
.getBackgroundTotalDurationMs(eq(mTargetUid), anyLong());
doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector)
.getCurrentDurationMs(eq(mTargetUid), anyLong());
doReturn(TARGET_UID).when(mTargetUid).getUid();
mInactiveSipper.uidObj = mInactiveUid;
doReturn(ANOMALY_WAKELOCK_TIME_MS).when(mWakelockAnomalyDetector)
.getBackgroundTotalDurationMs(eq(mInactiveUid), anyLong());
doReturn(0L).when(mWakelockAnomalyDetector)
.getCurrentDurationMs(eq(mInactiveUid), anyLong());
doReturn(INACTIVE_UID).when(mInactiveUid).getUid();
mUsageList = new ArrayList<>();
mUsageList.add(mAnomalySipper);
mUsageList.add(mNormalSipper);
mUsageList.add(mTargetSipper);
mUsageList.add(mInactiveSipper);
when(mBatteryStatsHelper.getUsageList()).thenReturn(mUsageList);
mAnomaly = createWakeLockAnomaly(ANOMALY_UID);
mTargetAnomaly = createWakeLockAnomaly(TARGET_UID);
}
@Test
public void testDetectAnomalies_containsAnomaly_detectIt() {
doReturn(BatteryUtils.UID_NULL).when(mBatteryUtils).getPackageUid(nullable(String.class));
List<Anomaly> mAnomalies = mWakelockAnomalyDetector.detectAnomalies(mBatteryStatsHelper);
assertThat(mAnomalies).containsExactly(mAnomaly, mTargetAnomaly);
}
@Test
public void testDetectAnomalies_containsTargetPackage_detectIt() {
doReturn(TARGET_UID).when(mBatteryUtils).getPackageUid(TARGET_PACKAGE_NAME);
List<Anomaly> mAnomalies =
mWakelockAnomalyDetector.detectAnomalies(mBatteryStatsHelper, TARGET_PACKAGE_NAME);
assertThat(mAnomalies).containsExactly(mTargetAnomaly);
}
@Test
public void testContainsThresholdFromPolicy() {
assertThat(mWakelockAnomalyDetector.mWakeLockThresholdMs).isEqualTo(WAKELOCK_THRESHOLD_MS);
}
private Anomaly createWakeLockAnomaly(int uid) {
return new Anomaly.Builder()
.setUid(uid)
.setType(Anomaly.AnomalyType.WAKE_LOCK)
.setWakeLockTimeMs(ANOMALY_WAKELOCK_TIME_MS)
.build();
}
}

View File

@@ -1,229 +0,0 @@
/*
* 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.checker;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.BatteryStats;
import android.os.Build;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly;
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.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@RunWith(SettingsRobolectricTestRunner.class)
public class WakeupAlarmAnomalyDetectorTest {
private static final String TARGET_PACKAGE_NAME = "com.android.target";
private static final String ANOMALY_PACKAGE_NAME = "com.android.anomaly";
private static final boolean TARGET_BACKGROUND_RESTRICTION_ON = false;
private static final boolean ANOMALY_BACKGROUND_RESTRICTION_ON = true;
private static final int TARGET_SDK = Build.VERSION_CODES.L;
private static final int ANOMALY_SDK = Build.VERSION_CODES.O;
private static final int ANOMALY_UID = 111;
private static final int NORMAL_UID = 222;
private static final int TARGET_UID = 333;
private static final long RUNNING_TIME_MS =
1 * DateUtils.HOUR_IN_MILLIS + 10 * DateUtils.MINUTE_IN_MILLIS;
private static final int ANOMALY_WAKEUP_COUNT = 500;
private static final int NORMAL_WAKEUP_COUNT = 61;
private static final int BLACKLISTED_WAKEUP_COUNT = 37;
private static final int ANOMALY_WAKEUP_FREQUENCY = 428; // count per hour
@Mock
private BatteryStatsHelper mBatteryStatsHelper;
@Mock
private BatterySipper mAnomalySipper;
@Mock
private BatterySipper mNormalSipper;
@Mock
private BatterySipper mTargetSipper;
@Mock
private BatteryStats.Uid mAnomalyUid;
@Mock
private BatteryStats.Uid mNormalUid;
@Mock
private BatteryStats.Uid mTargetUid;
@Mock
private BatteryUtils mBatteryUtils;
@Mock
private BatteryStats.Uid.Pkg mPkg;
@Mock
private BatteryStats.Counter mCounter;
@Mock
private BatteryStats.Counter mCounter2;
@Mock
private AnomalyDetectionPolicy mPolicy;
@Mock
private AnomalyAction mAnomalyAction;
@Mock
private AnomalyUtils mAnomalyUtils;
private WakeupAlarmAnomalyDetector mWakeupAlarmAnomalyDetector;
private Context mContext;
private List<BatterySipper> mUsageList;
private Anomaly mAnomaly;
private Anomaly mTargetAnomaly;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
ReflectionHelpers.setField(mPolicy, "wakeupAlarmThreshold", 60);
final Set<String> blacklistedTags = new ArraySet<>();
blacklistedTags.add("blacklistedTag");
ReflectionHelpers.setField(mPolicy, "wakeupBlacklistedTags", blacklistedTags);
doReturn(false).when(mBatteryUtils).shouldHideSipper(any());
doReturn(RUNNING_TIME_MS).when(mBatteryUtils)
.calculateRunningTimeBasedOnStatsType(any(), anyInt());
doReturn(true).when(mAnomalyAction).isActionActive(any());
doReturn(mAnomalyAction).when(mAnomalyUtils).getAnomalyAction(any());
mAnomalySipper.uidObj = mAnomalyUid;
doReturn(ANOMALY_UID).when(mAnomalyUid).getUid();
mNormalSipper.uidObj = mNormalUid;
doReturn(NORMAL_UID).when(mNormalUid).getUid();
mTargetSipper.uidObj = mTargetUid;
doReturn(TARGET_UID).when(mTargetUid).getUid();
mUsageList = new ArrayList<>();
mUsageList.add(mAnomalySipper);
mUsageList.add(mNormalSipper);
mUsageList.add(mTargetSipper);
when(mBatteryStatsHelper.getUsageList()).thenReturn(mUsageList);
doReturn(TARGET_PACKAGE_NAME).when(mBatteryUtils).getPackageName(TARGET_UID);
doReturn(ANOMALY_PACKAGE_NAME).when(mBatteryUtils).getPackageName(ANOMALY_UID);
doReturn(TARGET_SDK).when(mBatteryUtils).getTargetSdkVersion(TARGET_PACKAGE_NAME);
doReturn(ANOMALY_SDK).when(mBatteryUtils).getTargetSdkVersion(ANOMALY_PACKAGE_NAME);
doReturn(TARGET_BACKGROUND_RESTRICTION_ON).when(mBatteryUtils)
.isBackgroundRestrictionEnabled(TARGET_SDK, TARGET_UID, TARGET_PACKAGE_NAME);
doReturn(ANOMALY_BACKGROUND_RESTRICTION_ON).when(mBatteryUtils)
.isBackgroundRestrictionEnabled(ANOMALY_SDK, ANOMALY_UID, ANOMALY_PACKAGE_NAME);
mAnomaly = new Anomaly.Builder()
.setUid(ANOMALY_UID)
.setPackageName(ANOMALY_PACKAGE_NAME)
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setTargetSdkVersion(ANOMALY_SDK)
.setBackgroundRestrictionEnabled(ANOMALY_BACKGROUND_RESTRICTION_ON)
.setWakeupAlarmCount(ANOMALY_WAKEUP_FREQUENCY)
.build();
mTargetAnomaly = new Anomaly.Builder()
.setUid(TARGET_UID)
.setPackageName(TARGET_PACKAGE_NAME)
.setType(Anomaly.AnomalyType.WAKEUP_ALARM)
.setTargetSdkVersion(TARGET_SDK)
.setBackgroundRestrictionEnabled(TARGET_BACKGROUND_RESTRICTION_ON)
.setWakeupAlarmCount(ANOMALY_WAKEUP_FREQUENCY)
.build();
mWakeupAlarmAnomalyDetector =
spy(new WakeupAlarmAnomalyDetector(mContext, mPolicy, mAnomalyUtils));
mWakeupAlarmAnomalyDetector.mBatteryUtils = mBatteryUtils;
}
@Test
public void testDetectAnomalies_containsAnomaly_detectIt() {
doReturn(-1).when(mBatteryUtils).getPackageUid(nullable(String.class));
doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector)
.getWakeupAlarmCountFromUid(mAnomalyUid);
doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector)
.getWakeupAlarmCountFromUid(mTargetUid);
doReturn(NORMAL_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector)
.getWakeupAlarmCountFromUid(mNormalUid);
List<Anomaly> mAnomalies = mWakeupAlarmAnomalyDetector.detectAnomalies(mBatteryStatsHelper);
assertThat(mAnomalies).containsExactly(mAnomaly, mTargetAnomaly);
}
@Test
public void testDetectAnomalies_detectTargetAnomaly_detectIt() {
doReturn(TARGET_UID).when(mBatteryUtils).getPackageUid(TARGET_PACKAGE_NAME);
doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector)
.getWakeupAlarmCountFromUid(mAnomalyUid);
doReturn(ANOMALY_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector)
.getWakeupAlarmCountFromUid(mTargetUid);
doReturn(NORMAL_WAKEUP_COUNT).when(mWakeupAlarmAnomalyDetector)
.getWakeupAlarmCountFromUid(mNormalUid);
List<Anomaly> mAnomalies = mWakeupAlarmAnomalyDetector
.detectAnomalies(mBatteryStatsHelper, TARGET_PACKAGE_NAME);
assertThat(mAnomalies).containsExactly(mTargetAnomaly);
}
@Test
public void testGetWakeupAlarmCountFromUid_countCorrect() {
final ArrayMap<String, BatteryStats.Uid.Pkg> packageStats = new ArrayMap<>();
final ArrayMap<String, BatteryStats.Counter> alarms = new ArrayMap<>();
doReturn(alarms).when(mPkg).getWakeupAlarmStats();
doReturn(NORMAL_WAKEUP_COUNT).when(mCounter).getCountLocked(anyInt());
doReturn(packageStats).when(mAnomalyUid).getPackageStats();
packageStats.put("", mPkg);
alarms.put("1", mCounter);
alarms.put("2", mCounter);
assertThat(mWakeupAlarmAnomalyDetector.getWakeupAlarmCountFromUid(mAnomalyUid))
.isEqualTo(2 * NORMAL_WAKEUP_COUNT);
}
@Test
public void testGetWakeupAlarmCountFromUid_filterOutBlacklistedTags() {
final ArrayMap<String, BatteryStats.Uid.Pkg> packageStats = new ArrayMap<>();
final ArrayMap<String, BatteryStats.Counter> alarms = new ArrayMap<>();
doReturn(alarms).when(mPkg).getWakeupAlarmStats();
doReturn(NORMAL_WAKEUP_COUNT).when(mCounter).getCountLocked(anyInt());
doReturn(BLACKLISTED_WAKEUP_COUNT).when(mCounter2).getCountLocked(anyInt());
doReturn(packageStats).when(mAnomalyUid).getPackageStats();
packageStats.put("", mPkg);
alarms.put("allowedTag", mCounter);
alarms.put("blacklistedTag", mCounter2);
assertThat(mWakeupAlarmAnomalyDetector.getWakeupAlarmCountFromUid(mAnomalyUid))
.isEqualTo(NORMAL_WAKEUP_COUNT);
}
}

View File

@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
import android.text.format.DateUtils;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
@@ -36,8 +35,10 @@ import java.util.List;
public class AppInfoTest {
private static final String PACKAGE_NAME = "com.android.app";
private static final int TYPE_WAKELOCK = Anomaly.AnomalyType.WAKE_LOCK;
private static final int TYPE_WAKEUP = Anomaly.AnomalyType.WAKEUP_ALARM;
private static final int TYPE_WAKELOCK =
StatsManagerConfig.AnomalyType.EXCESSIVE_WAKELOCK_ALL_SCREEN_OFF;
private static final int TYPE_WAKEUP =
StatsManagerConfig.AnomalyType.EXCESSIVE_WAKEUPS_IN_BACKGROUND;
private static final long SCREEN_TIME_MS = DateUtils.HOUR_IN_MILLIS;
private static final int UID = 3452;

View File

@@ -1,71 +0,0 @@
/*
* 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 android.content.Context;
import android.content.pm.PackageManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.settings.fuelgauge.anomaly.Anomaly;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class LocationCheckActionTest {
private static final String PACKAGE_NAME = "com.android.chrome";
private Context mContext;
private LocationCheckAction mLocationCheckAction;
private Anomaly mAnomaly;
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
mLocationCheckAction = new LocationCheckAction(mContext);
mAnomaly = new Anomaly.Builder()
.setUid(getPackageUid(mContext, PACKAGE_NAME))
.setPackageName(PACKAGE_NAME)
.build();
}
@Test
public void testRevokeAndCheck() {
mLocationCheckAction.handlePositiveAction(mAnomaly, 0 /* metric key */);
assertThat(mLocationCheckAction.isActionActive(mAnomaly)).isFalse();
}
private int getPackageUid(Context context, String packageName) {
try {
return context.getPackageManager().getPackageUid(packageName,
PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
return -1;
}
}
}