1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.fuelgauge.anomaly.checker; 18 19import android.content.Context; 20import android.os.BatteryStats; 21import android.support.annotation.VisibleForTesting; 22import android.text.format.DateUtils; 23import android.util.ArrayMap; 24import android.util.ArraySet; 25import android.util.Log; 26 27import com.android.internal.os.BatterySipper; 28import com.android.internal.os.BatteryStatsHelper; 29import com.android.settings.Utils; 30import com.android.settings.fuelgauge.BatteryUtils; 31import com.android.settings.fuelgauge.anomaly.Anomaly; 32import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy; 33import com.android.settings.fuelgauge.anomaly.AnomalyUtils; 34 35import java.util.ArrayList; 36import java.util.Collections; 37import java.util.List; 38import java.util.Map; 39import java.util.Set; 40 41/** 42 * Check whether apps has too many wakeup alarms 43 */ 44public class WakeupAlarmAnomalyDetector implements AnomalyDetector { 45 private static final String TAG = "WakeupAlarmAnomalyDetector"; 46 @VisibleForTesting 47 BatteryUtils mBatteryUtils; 48 private long mWakeupAlarmThreshold; 49 private Set<String> mWakeupBlacklistedTags; 50 private Context mContext; 51 private AnomalyUtils mAnomalyUtils; 52 53 public WakeupAlarmAnomalyDetector(Context context) { 54 this(context, new AnomalyDetectionPolicy(context), AnomalyUtils.getInstance(context)); 55 } 56 57 @VisibleForTesting 58 WakeupAlarmAnomalyDetector(Context context, AnomalyDetectionPolicy policy, 59 AnomalyUtils anomalyUtils) { 60 mContext = context; 61 mBatteryUtils = BatteryUtils.getInstance(context); 62 mAnomalyUtils = anomalyUtils; 63 mWakeupAlarmThreshold = policy.wakeupAlarmThreshold; 64 mWakeupBlacklistedTags = policy.wakeupBlacklistedTags; 65 } 66 67 @Override 68 public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper) { 69 // Detect all apps if targetPackageName is null 70 return detectAnomalies(batteryStatsHelper, null /* targetPackageName */); 71 } 72 73 @Override 74 public List<Anomaly> detectAnomalies(BatteryStatsHelper batteryStatsHelper, 75 String targetPackageName) { 76 final List<BatterySipper> batterySippers = batteryStatsHelper.getUsageList(); 77 final List<Anomaly> anomalies = new ArrayList<>(); 78 final double totalRunningHours = mBatteryUtils.calculateRunningTimeBasedOnStatsType( 79 batteryStatsHelper, BatteryStats.STATS_SINCE_CHARGED) 80 / (double) DateUtils.HOUR_IN_MILLIS; 81 final int targetUid = mBatteryUtils.getPackageUid(targetPackageName); 82 83 if (totalRunningHours >= 1) { 84 for (int i = 0, size = batterySippers.size(); i < size; i++) { 85 final BatterySipper sipper = batterySippers.get(i); 86 final BatteryStats.Uid uid = sipper.uidObj; 87 if (uid == null 88 || mBatteryUtils.shouldHideSipper(sipper) 89 || (targetUid != BatteryUtils.UID_NULL && targetUid != uid.getUid())) { 90 continue; 91 } 92 93 final int wakeupAlarmCount = (int) (getWakeupAlarmCountFromUid(uid) 94 / totalRunningHours); 95 if (wakeupAlarmCount > mWakeupAlarmThreshold) { 96 final String packageName = mBatteryUtils.getPackageName(uid.getUid()); 97 final CharSequence displayName = Utils.getApplicationLabel(mContext, 98 packageName); 99 final int targetSdkVersion = mBatteryUtils.getTargetSdkVersion(packageName); 100 101 Anomaly anomaly = new Anomaly.Builder() 102 .setUid(uid.getUid()) 103 .setType(Anomaly.AnomalyType.WAKEUP_ALARM) 104 .setDisplayName(displayName) 105 .setPackageName(packageName) 106 .setTargetSdkVersion(targetSdkVersion) 107 .setBackgroundRestrictionEnabled( 108 mBatteryUtils.isBackgroundRestrictionEnabled(targetSdkVersion, 109 uid.getUid(), packageName)) 110 .setWakeupAlarmCount(wakeupAlarmCount) 111 .build(); 112 113 if (mAnomalyUtils.getAnomalyAction(anomaly).isActionActive(anomaly)) { 114 anomalies.add(anomaly); 115 } 116 } 117 } 118 } 119 120 return anomalies; 121 } 122 123 @VisibleForTesting 124 int getWakeupAlarmCountFromUid(BatteryStats.Uid uid) { 125 int wakeups = 0; 126 final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats 127 = uid.getPackageStats(); 128 for (int ipkg = packageStats.size() - 1; ipkg >= 0; ipkg--) { 129 final BatteryStats.Uid.Pkg ps = packageStats.valueAt(ipkg); 130 final ArrayMap<String, ? extends BatteryStats.Counter> alarms = 131 ps.getWakeupAlarmStats(); 132 for (Map.Entry<String, ? extends BatteryStats.Counter> alarm : alarms.entrySet()) { 133 if (mWakeupBlacklistedTags != null 134 && mWakeupBlacklistedTags.contains(alarm.getKey())) { 135 continue; 136 } 137 int count = alarm.getValue().getCountLocked(BatteryStats.STATS_SINCE_CHARGED); 138 wakeups += count; 139 } 140 } 141 142 return wakeups; 143 } 144 145} 146