RecentLocationApps.java revision bfa96c3b10f53a969406755eed36ba98fc1de549
1/*
2 * Copyright (C) 2013 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.location;
18
19import android.app.AppOpsManager;
20import android.content.Context;
21import android.content.pm.ApplicationInfo;
22import android.content.pm.PackageManager;
23import android.os.Bundle;
24import android.preference.Preference;
25import android.preference.PreferenceActivity;
26import android.preference.PreferenceCategory;
27import android.preference.PreferenceManager;
28import android.preference.PreferenceScreen;
29import android.util.Log;
30
31import com.android.settings.R;
32import com.android.settings.applications.InstalledAppDetails;
33import com.android.settings.fuelgauge.BatterySipper;
34import com.android.settings.fuelgauge.BatteryStatsHelper;
35
36import java.util.HashMap;
37import java.util.List;
38import java.util.Map;
39
40/**
41 * Retrieves the information of applications which accessed location recently.
42 */
43public class RecentLocationApps {
44    private static final String TAG = RecentLocationApps.class.getSimpleName();
45
46    private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
47
48    private final PreferenceActivity mActivity;
49    private final BatteryStatsHelper mStatsHelper;
50    private final PackageManager mPackageManager;
51
52    // Stores all the packages that requested location within the designated interval
53    // key - package name of the app
54    // value - whether the app has requested high power location
55
56    public RecentLocationApps(PreferenceActivity activity, BatteryStatsHelper sipperUtil) {
57        mActivity = activity;
58        mPackageManager = activity.getPackageManager();
59        mStatsHelper = sipperUtil;
60    }
61
62    private class UidEntryClickedListener
63            implements Preference.OnPreferenceClickListener {
64        private BatterySipper mSipper;
65
66        public UidEntryClickedListener(BatterySipper sipper) {
67            mSipper = sipper;
68        }
69
70        @Override
71        public boolean onPreferenceClick(Preference preference) {
72            mStatsHelper.startBatteryDetailPage(mActivity, mSipper, false);
73            return true;
74        }
75    }
76
77    private class PackageEntryClickedListener
78            implements Preference.OnPreferenceClickListener {
79        private String mPackage;
80
81        public PackageEntryClickedListener(String packageName) {
82            mPackage = packageName;
83        }
84
85        @Override
86        public boolean onPreferenceClick(Preference preference) {
87            // start new fragment to display extended information
88            Bundle args = new Bundle();
89            args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mPackage);
90            mActivity.startPreferencePanel(InstalledAppDetails.class.getName(), args,
91                    R.string.application_info_label, null, null, 0);
92            return true;
93        }
94    }
95
96    /**
97     * Fills a list of applications which queried location recently within
98     * specified time.
99     */
100    public void fillAppList(PreferenceCategory container) {
101        HashMap<String, Boolean> packageMap = new HashMap<String, Boolean>();
102        AppOpsManager aoManager =
103                (AppOpsManager) mActivity.getSystemService(Context.APP_OPS_SERVICE);
104        List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(
105                new int[] {
106                    AppOpsManager.OP_MONITOR_LOCATION,
107                    AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
108                });
109        PreferenceManager preferenceManager = container.getPreferenceManager();
110        long now = System.currentTimeMillis();
111        for (AppOpsManager.PackageOps ops : appOps) {
112            processPackageOps(now, container, preferenceManager, ops, packageMap);
113        }
114
115        mStatsHelper.refreshStats();
116        List<BatterySipper> usageList = mStatsHelper.getUsageList();
117        for (BatterySipper sipper : usageList) {
118            sipper.loadNameAndIcon();
119            String[] packages = sipper.getPackages();
120            if (packages == null) {
121                continue;
122            }
123            for (String curPackage : packages) {
124                if (packageMap.containsKey(curPackage)) {
125                    PreferenceScreen screen = preferenceManager.createPreferenceScreen(mActivity);
126                    screen.setIcon(sipper.getIcon());
127                    screen.setTitle(sipper.getLabel());
128                    if (packageMap.get(curPackage)) {
129                        screen.setSummary(R.string.location_high_battery_use);
130                    } else {
131                        screen.setSummary(R.string.location_low_battery_use);
132                    }
133                    container.addPreference(screen);
134                    screen.setOnPreferenceClickListener(new UidEntryClickedListener(sipper));
135                    packageMap.remove(curPackage);
136                    break;
137                }
138            }
139        }
140
141        // Typically there shouldn't be any entry left in the HashMap. But if there are any, add
142        // them to the list and link them to the app info page.
143        for (Map.Entry<String, Boolean> entry : packageMap.entrySet()) {
144            try {
145                PreferenceScreen screen = preferenceManager.createPreferenceScreen(mActivity);
146                ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
147                        entry.getKey(), PackageManager.GET_META_DATA);
148                screen.setIcon(mPackageManager.getApplicationIcon(appInfo));
149                screen.setTitle(mPackageManager.getApplicationLabel(appInfo));
150                // if used both high and low battery within the time interval, show as "high
151                // battery"
152                if (entry.getValue()) {
153                    screen.setSummary(R.string.location_high_battery_use);
154                } else {
155                    screen.setSummary(R.string.location_low_battery_use);
156                }
157                screen.setOnPreferenceClickListener(
158                        new PackageEntryClickedListener(entry.getKey()));
159                container.addPreference(screen);
160            } catch (PackageManager.NameNotFoundException e) {
161                // ignore the current app and move on to the next.
162            }
163        }
164    }
165
166    private void processPackageOps(
167            long now,
168            PreferenceCategory container,
169            PreferenceManager preferenceManager,
170            AppOpsManager.PackageOps ops,
171            HashMap<String, Boolean> packageMap) {
172        String packageName = ops.getPackageName();
173        List<AppOpsManager.OpEntry> entries = ops.getOps();
174        boolean highBattery = false;
175        boolean normalBattery = false;
176        for (AppOpsManager.OpEntry entry : entries) {
177            // If previous location activity is older than designated interval, ignore this app.
178            if (now - entry.getTime() <= RECENT_TIME_INTERVAL_MILLIS) {
179                switch (entry.getOp()) {
180                    case AppOpsManager.OP_MONITOR_LOCATION:
181                        normalBattery = true;
182                        break;
183                    case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
184                        highBattery = true;
185                        break;
186                    default:
187                        break;
188                }
189            }
190        }
191
192        if (!highBattery && !normalBattery) {
193            if (Log.isLoggable(TAG, Log.VERBOSE)) {
194                Log.v(TAG, packageName + " hadn't used location within the time interval.");
195            }
196            return;
197        }
198
199        packageMap.put(packageName, highBattery);
200    }
201}
202