blob: 231fc695d344f029e8b180d95d2eddb814d7eb66 [file] [log] [blame]
Tony Mantler65eb4ad2015-11-04 15:52:25 -08001/*
2 * Copyright (C) 2015 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.settingslib.location;
18
19import android.app.AppGlobals;
20import android.app.AppOpsManager;
21import android.content.Context;
22import android.content.pm.ApplicationInfo;
23import android.content.pm.IPackageManager;
24import android.content.pm.PackageManager;
25import android.graphics.drawable.Drawable;
26import android.os.Process;
27import android.os.RemoteException;
28import android.os.UserHandle;
29import android.os.UserManager;
30import android.util.Log;
31
32import java.util.ArrayList;
33import java.util.List;
34
35/**
36 * Retrieves the information of applications which accessed location recently.
37 */
38public class RecentLocationApps {
39 private static final String TAG = RecentLocationApps.class.getSimpleName();
40 private static final String ANDROID_SYSTEM_PACKAGE_NAME = "android";
41
42 private static final int RECENT_TIME_INTERVAL_MILLIS = 15 * 60 * 1000;
43
44 private static final int[] LOCATION_OPS = new int[] {
45 AppOpsManager.OP_MONITOR_LOCATION,
46 AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
47 };
48
49 private final PackageManager mPackageManager;
50 private final Context mContext;
51
52 public RecentLocationApps(Context context) {
53 mContext = context;
54 mPackageManager = context.getPackageManager();
55 }
56
57 /**
58 * Fills a list of applications which queried location recently within specified time.
59 */
60 public List<Request> getAppList() {
61 // Retrieve a location usage list from AppOps
62 AppOpsManager aoManager =
63 (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
64 List<AppOpsManager.PackageOps> appOps = aoManager.getPackagesForOps(LOCATION_OPS);
65
Tony Mantlerd6cc79092016-02-09 14:12:43 -080066 final int appOpsCount = appOps != null ? appOps.size() : 0;
67
Tony Mantler65eb4ad2015-11-04 15:52:25 -080068 // Process the AppOps list and generate a preference list.
Tony Mantlerd6cc79092016-02-09 14:12:43 -080069 ArrayList<Request> requests = new ArrayList<>(appOpsCount);
Tony Mantler65eb4ad2015-11-04 15:52:25 -080070 final long now = System.currentTimeMillis();
71 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
72 final List<UserHandle> profiles = um.getUserProfiles();
73
Tony Mantlerd6cc79092016-02-09 14:12:43 -080074 for (int i = 0; i < appOpsCount; ++i) {
Tony Mantler65eb4ad2015-11-04 15:52:25 -080075 AppOpsManager.PackageOps ops = appOps.get(i);
76 // Don't show the Android System in the list - it's not actionable for the user.
77 // Also don't show apps belonging to background users except managed users.
78 String packageName = ops.getPackageName();
79 int uid = ops.getUid();
80 int userId = UserHandle.getUserId(uid);
81 boolean isAndroidOs =
82 (uid == Process.SYSTEM_UID) && ANDROID_SYSTEM_PACKAGE_NAME.equals(packageName);
83 if (isAndroidOs || !profiles.contains(new UserHandle(userId))) {
84 continue;
85 }
86 Request request = getRequestFromOps(now, ops);
87 if (request != null) {
88 requests.add(request);
89 }
90 }
91
92 return requests;
93 }
94
95 /**
96 * Creates a Request entry for the given PackageOps.
97 *
98 * This method examines the time interval of the PackageOps first. If the PackageOps is older
99 * than the designated interval, this method ignores the PackageOps object and returns null.
100 * When the PackageOps is fresh enough, this method returns a Request object for the package
101 */
102 private Request getRequestFromOps(long now,
103 AppOpsManager.PackageOps ops) {
104 String packageName = ops.getPackageName();
105 List<AppOpsManager.OpEntry> entries = ops.getOps();
106 boolean highBattery = false;
107 boolean normalBattery = false;
108 // Earliest time for a location request to end and still be shown in list.
109 long recentLocationCutoffTime = now - RECENT_TIME_INTERVAL_MILLIS;
110 for (AppOpsManager.OpEntry entry : entries) {
111 if (entry.isRunning() || entry.getTime() >= recentLocationCutoffTime) {
112 switch (entry.getOp()) {
113 case AppOpsManager.OP_MONITOR_LOCATION:
114 normalBattery = true;
115 break;
116 case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
117 highBattery = true;
118 break;
119 default:
120 break;
121 }
122 }
123 }
124
125 if (!highBattery && !normalBattery) {
126 if (Log.isLoggable(TAG, Log.VERBOSE)) {
127 Log.v(TAG, packageName + " hadn't used location within the time interval.");
128 }
129 return null;
130 }
131
132 // The package is fresh enough, continue.
133
134 int uid = ops.getUid();
135 int userId = UserHandle.getUserId(uid);
136
137 Request request = null;
138 try {
139 IPackageManager ipm = AppGlobals.getPackageManager();
140 ApplicationInfo appInfo =
141 ipm.getApplicationInfo(packageName, PackageManager.GET_META_DATA, userId);
142 if (appInfo == null) {
143 Log.w(TAG, "Null application info retrieved for package " + packageName
144 + ", userId " + userId);
145 return null;
146 }
147
148 final UserHandle userHandle = new UserHandle(userId);
149 Drawable appIcon = mPackageManager.getApplicationIcon(appInfo);
150 Drawable icon = mPackageManager.getUserBadgedIcon(appIcon, userHandle);
151 CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo);
152 CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle);
153 if (appLabel.toString().contentEquals(badgedAppLabel)) {
154 // If badged label is not different from original then no need for it as
155 // a separate content description.
156 badgedAppLabel = null;
157 }
158 request = new Request(packageName, userHandle, icon, appLabel, highBattery,
159 badgedAppLabel);
160 } catch (RemoteException e) {
161 Log.w(TAG, "Error while retrieving application info for package " + packageName
162 + ", userId " + userId, e);
163 }
164
165 return request;
166 }
167
168 public static class Request {
169 public final String packageName;
170 public final UserHandle userHandle;
171 public final Drawable icon;
172 public final CharSequence label;
173 public final boolean isHighBattery;
174 public final CharSequence contentDescription;
175
176 private Request(String packageName, UserHandle userHandle, Drawable icon,
177 CharSequence label, boolean isHighBattery, CharSequence contentDescription) {
178 this.packageName = packageName;
179 this.userHandle = userHandle;
180 this.icon = icon;
181 this.label = label;
182 this.isHighBattery = isHighBattery;
183 this.contentDescription = contentDescription;
184 }
185 }
186}