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;
18
19import android.content.Context;
20import android.content.pm.PackageManager;
21import android.os.BatteryStats;
22import android.os.Bundle;
23import android.os.UserManager;
24import android.support.annotation.VisibleForTesting;
25import android.util.Log;
26
27import com.android.internal.os.BatteryStatsHelper;
28import com.android.internal.util.ArrayUtils;
29import com.android.settings.utils.AsyncLoader;
30
31import java.io.FileDescriptor;
32import java.io.PrintWriter;
33import java.util.ArrayList;
34import java.util.List;
35
36/**
37 * Loader to compute which apps are anomaly and return a anomaly list. It will return
38 * an empty list if there is no anomaly.
39 */
40public class AnomalyLoader extends AsyncLoader<List<Anomaly>> {
41    private static final String TAG = "AnomalyLoader";
42
43    private static final boolean USE_FAKE_DATA = false;
44    private BatteryStatsHelper mBatteryStatsHelper;
45    private String mPackageName;
46    private UserManager mUserManager;
47    @VisibleForTesting
48    AnomalyUtils mAnomalyUtils;
49    @VisibleForTesting
50    AnomalyDetectionPolicy mPolicy;
51
52    /**
53     * Create {@link AnomalyLoader} that runs anomaly check for all apps.
54     */
55    public AnomalyLoader(Context context, BatteryStatsHelper batteryStatsHelper) {
56        this(context, batteryStatsHelper, null, new AnomalyDetectionPolicy(context));
57
58    }
59
60    /**
61     * Create {@link AnomalyLoader} with {@code packageName}, so this loader will only
62     * detect anomalies related to {@code packageName}, or check all apps if {@code packageName}
63     * is {@code null}.
64     *
65     * This constructor will create {@link BatteryStatsHelper} in background thread.
66     *
67     * @param packageName if set, only finds anomalies for this package. If {@code null},
68     *                    detects all anomalies of this type.
69     */
70    public AnomalyLoader(Context context, String packageName) {
71        this(context, null, packageName, new AnomalyDetectionPolicy(context));
72    }
73
74    @VisibleForTesting
75    AnomalyLoader(Context context, BatteryStatsHelper batteryStatsHelper,
76            String packageName, AnomalyDetectionPolicy policy) {
77        super(context);
78        mBatteryStatsHelper = batteryStatsHelper;
79        mPackageName = packageName;
80        mAnomalyUtils = AnomalyUtils.getInstance(context);
81        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
82        mPolicy = policy;
83    }
84
85    @Override
86    protected void onDiscardResult(List<Anomaly> result) {
87    }
88
89    @Override
90    public List<Anomaly> loadInBackground() {
91        if (USE_FAKE_DATA) {
92            return generateFakeData();
93        }
94        if (mBatteryStatsHelper == null) {
95            mBatteryStatsHelper = new BatteryStatsHelper(getContext());
96            mBatteryStatsHelper.create((Bundle) null);
97            mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
98                    mUserManager.getUserProfiles());
99        }
100
101        return mAnomalyUtils.detectAnomalies(mBatteryStatsHelper, mPolicy, mPackageName);
102    }
103
104    @VisibleForTesting
105    List<Anomaly> generateFakeData() {
106        final List<Anomaly> anomalies = new ArrayList<>();
107        final Context context = getContext();
108        final String packageName = "com.android.settings";
109        final CharSequence displayName = "Settings";
110        try {
111            final int uid = context.getPackageManager().getPackageUid(packageName, 0);
112
113            anomalies.add(new Anomaly.Builder()
114                    .setUid(uid)
115                    .setType(Anomaly.AnomalyType.WAKE_LOCK)
116                    .setPackageName(packageName)
117                    .setDisplayName(displayName)
118                    .build());
119            anomalies.add(new Anomaly.Builder()
120                    .setUid(uid)
121                    .setType(Anomaly.AnomalyType.WAKEUP_ALARM)
122                    .setPackageName(packageName)
123                    .setDisplayName(displayName)
124                    .build());
125            anomalies.add(new Anomaly.Builder()
126                    .setUid(uid)
127                    .setType(Anomaly.AnomalyType.BLUETOOTH_SCAN)
128                    .setPackageName(packageName)
129                    .setDisplayName(displayName)
130                    .build());
131        } catch (PackageManager.NameNotFoundException e) {
132            Log.e(TAG, "Cannot find package by name: " + packageName, e);
133        }
134        return anomalies;
135    }
136
137}
138