Home | History | Annotate | Download | only in checker
      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 
     17 package com.android.settings.fuelgauge.anomaly.checker;
     18 
     19 import android.content.Context;
     20 import android.os.BatteryStats;
     21 import android.support.annotation.VisibleForTesting;
     22 import android.text.format.DateUtils;
     23 import android.util.ArrayMap;
     24 import android.util.ArraySet;
     25 import android.util.Log;
     26 
     27 import com.android.internal.os.BatterySipper;
     28 import com.android.internal.os.BatteryStatsHelper;
     29 import com.android.settings.Utils;
     30 import com.android.settings.fuelgauge.BatteryUtils;
     31 import com.android.settings.fuelgauge.anomaly.Anomaly;
     32 import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
     33 import com.android.settings.fuelgauge.anomaly.AnomalyUtils;
     34 
     35 import java.util.ArrayList;
     36 import java.util.Collections;
     37 import java.util.List;
     38 import java.util.Map;
     39 import java.util.Set;
     40 
     41 /**
     42  * Check whether apps has too many wakeup alarms
     43  */
     44 public 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