blob: 362d27f75f1838031adac5ba224906c821dc83d8 [file] [log] [blame]
Adam Lesinski468d3912014-07-22 10:01:08 -07001/*
2 * Copyright (C) 2014 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;
18
19import com.android.internal.content.PackageMonitor;
Adam Lesinski468d3912014-07-22 10:01:08 -070020import android.Manifest;
21import android.app.ActivityThread;
Adam Lesinskie01dfd12014-08-11 20:54:12 -070022import android.app.AlertDialog;
Adam Lesinski468d3912014-07-22 10:01:08 -070023import android.app.AppOpsManager;
Adam Lesinskie01dfd12014-08-11 20:54:12 -070024import android.app.Dialog;
25import android.app.DialogFragment;
Adam Lesinski21dfa202014-09-09 11:34:55 -070026import android.app.Fragment;
27import android.app.FragmentTransaction;
Adam Lesinski468d3912014-07-22 10:01:08 -070028import android.content.Context;
Adam Lesinskie01dfd12014-08-11 20:54:12 -070029import android.content.DialogInterface;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000030import android.content.pm.ApplicationInfo;
Adam Lesinski468d3912014-07-22 10:01:08 -070031import android.content.pm.IPackageManager;
32import android.content.pm.PackageInfo;
33import android.content.pm.PackageManager;
34import android.os.AsyncTask;
35import android.os.Bundle;
36import android.os.Looper;
37import android.os.RemoteException;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000038import android.os.UserHandle;
39import android.os.UserManager;
Adam Lesinski468d3912014-07-22 10:01:08 -070040import android.preference.Preference;
Adam Lesinski1813c622014-08-27 19:00:30 -070041import android.preference.PreferenceScreen;
Adam Lesinski468d3912014-07-22 10:01:08 -070042import android.preference.SwitchPreference;
43import android.util.ArrayMap;
44import android.util.Log;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000045import android.util.SparseArray;
Adam Lesinski468d3912014-07-22 10:01:08 -070046
47import java.util.List;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000048import java.util.Collections;
Adam Lesinski468d3912014-07-22 10:01:08 -070049
50public class UsageAccessSettings extends SettingsPreferenceFragment implements
51 Preference.OnPreferenceChangeListener {
52
53 private static final String TAG = "UsageAccessSettings";
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000054 private static final String BUNDLE_KEY_PROFILEID = "profileId";
Adam Lesinski468d3912014-07-22 10:01:08 -070055
56 private static final String[] PM_USAGE_STATS_PERMISSION = new String[] {
57 Manifest.permission.PACKAGE_USAGE_STATS
58 };
59
60 private static final int[] APP_OPS_OP_CODES = new int[] {
61 AppOpsManager.OP_GET_USAGE_STATS
62 };
63
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000064 private static class PackageEntry implements Comparable<PackageEntry> {
65 public PackageEntry(String packageName, UserHandle userHandle) {
Adam Lesinski468d3912014-07-22 10:01:08 -070066 this.packageName = packageName;
67 this.appOpMode = AppOpsManager.MODE_DEFAULT;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000068 this.userHandle = userHandle;
69 }
70
71 @Override
72 public int compareTo(PackageEntry another) {
73 return packageName.compareTo(another.packageName);
Adam Lesinski468d3912014-07-22 10:01:08 -070074 }
75
76 final String packageName;
77 PackageInfo packageInfo;
78 boolean permissionGranted;
79 int appOpMode;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000080 UserHandle userHandle;
Adam Lesinski468d3912014-07-22 10:01:08 -070081
82 SwitchPreference preference;
83 }
84
85 /**
86 * Fetches the list of Apps that are requesting access to the UsageStats API and updates
87 * the PreferenceScreen with the results when complete.
88 */
89 private class AppsRequestingAccessFetcher extends
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000090 AsyncTask<Void, Void, SparseArray<ArrayMap<String, PackageEntry>>> {
Adam Lesinski468d3912014-07-22 10:01:08 -070091
92 private final Context mContext;
93 private final PackageManager mPackageManager;
94 private final IPackageManager mIPackageManager;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000095 private final UserManager mUserManager;
96 private final List<UserHandle> mProfiles;
Adam Lesinski468d3912014-07-22 10:01:08 -070097
98 public AppsRequestingAccessFetcher(Context context) {
99 mContext = context;
100 mPackageManager = context.getPackageManager();
101 mIPackageManager = ActivityThread.getPackageManager();
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000102 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
103 mProfiles = mUserManager.getUserProfiles();
Adam Lesinski468d3912014-07-22 10:01:08 -0700104 }
105
106 @Override
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000107 protected SparseArray<ArrayMap<String, PackageEntry>> doInBackground(Void... params) {
Adam Lesinski468d3912014-07-22 10:01:08 -0700108 final String[] packages;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000109 SparseArray<ArrayMap<String, PackageEntry>> entries;
Adam Lesinski468d3912014-07-22 10:01:08 -0700110 try {
111 packages = mIPackageManager.getAppOpPermissionPackages(
112 Manifest.permission.PACKAGE_USAGE_STATS);
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000113
114 if (packages == null) {
115 // No packages are requesting permission to use the UsageStats API.
116 return null;
117 }
118
119 entries = new SparseArray<>();
120 for (final UserHandle profile : mProfiles) {
121 final ArrayMap<String, PackageEntry> entriesForProfile = new ArrayMap<>();
122 final int profileId = profile.getIdentifier();
123 entries.put(profileId, entriesForProfile);
124 for (final String packageName : packages) {
125 final boolean isAvailable = mIPackageManager.isPackageAvailable(packageName,
126 profileId);
127 if (!shouldIgnorePackage(packageName) && isAvailable) {
128 final PackageEntry newEntry = new PackageEntry(packageName, profile);
129 entriesForProfile.put(packageName, newEntry);
130 }
131 }
132 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700133 } catch (RemoteException e) {
134 Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting "
135 + Manifest.permission.PACKAGE_USAGE_STATS);
136 return null;
137 }
138
Adam Lesinski468d3912014-07-22 10:01:08 -0700139 // Load the packages that have been granted the PACKAGE_USAGE_STATS permission.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000140 try {
141 for (final UserHandle profile : mProfiles) {
142 final int profileId = profile.getIdentifier();
143 final ArrayMap<String, PackageEntry> entriesForProfile = entries.get(profileId);
144 if (entriesForProfile == null) {
145 continue;
146 }
147 final List<PackageInfo> packageInfos = mIPackageManager
148 .getPackagesHoldingPermissions(PM_USAGE_STATS_PERMISSION, 0, profileId)
149 .getList();
150 final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0;
151 for (int i = 0; i < packageInfoCount; i++) {
152 final PackageInfo packageInfo = packageInfos.get(i);
153 final PackageEntry pe = entriesForProfile.get(packageInfo.packageName);
154 if (pe != null) {
155 pe.packageInfo = packageInfo;
156 pe.permissionGranted = true;
157 }
158 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700159 }
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000160 } catch (RemoteException e) {
161 Log.w(TAG, "PackageManager is dead. Can't get list of packages granted "
162 + Manifest.permission.PACKAGE_USAGE_STATS);
163 return null;
Adam Lesinski468d3912014-07-22 10:01:08 -0700164 }
165
166 // Load the remaining packages that have requested but don't have the
167 // PACKAGE_USAGE_STATS permission.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000168 for (final UserHandle profile : mProfiles) {
169 final int profileId = profile.getIdentifier();
170 final ArrayMap<String, PackageEntry> entriesForProfile = entries.get(profileId);
171 if (entriesForProfile == null) {
172 continue;
173 }
174 int packageCount = entriesForProfile.size();
175 for (int i = packageCount - 1; i >= 0; --i) {
176 final PackageEntry pe = entriesForProfile.valueAt(i);
177 if (pe.packageInfo == null) {
178 try {
179 pe.packageInfo = mIPackageManager.getPackageInfo(pe.packageName, 0,
180 profileId);
181 } catch (RemoteException e) {
182 // This package doesn't exist. This may occur when an app is
183 // uninstalled for one user, but it is not removed from the system.
184 entriesForProfile.removeAt(i);
185 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700186 }
187 }
188 }
189
190 // Find out which packages have been granted permission from AppOps.
191 final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps(
192 APP_OPS_OP_CODES);
193 final int packageOpsCount = packageOps != null ? packageOps.size() : 0;
194 for (int i = 0; i < packageOpsCount; i++) {
195 final AppOpsManager.PackageOps packageOp = packageOps.get(i);
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000196 final int userId = UserHandle.getUserId(packageOp.getUid());
197 if (!isThisUserAProfileOfCurrentUser(userId)) {
198 // This AppOp does not belong to any of this user's profiles.
Adam Lesinski468d3912014-07-22 10:01:08 -0700199 continue;
200 }
201
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000202 final ArrayMap<String, PackageEntry> entriesForProfile = entries.get(userId);
203 if (entriesForProfile == null) {
204 continue;
205 }
206 final PackageEntry pe = entriesForProfile.get(packageOp.getPackageName());
207 if (pe == null) {
208 Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName()
209 + " of user " + userId +
210 " but package doesn't exist or did not request UsageStats access");
Adam Lesinski366e7a22014-07-25 10:20:26 -0700211 continue;
212 }
213
Adam Lesinski468d3912014-07-22 10:01:08 -0700214 if (packageOp.getOps().size() < 1) {
215 Log.w(TAG, "No AppOps permission exists for package "
216 + packageOp.getPackageName());
217 continue;
218 }
219
220 pe.appOpMode = packageOp.getOps().get(0).getMode();
221 }
222
223 return entries;
224 }
225
226 @Override
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000227 protected void onPostExecute(SparseArray<ArrayMap<String, PackageEntry>> newEntries) {
Adam Lesinski468d3912014-07-22 10:01:08 -0700228 mLastFetcherTask = null;
229
230 if (getActivity() == null) {
231 // We must have finished the Activity while we were processing in the background.
232 return;
233 }
234
235 if (newEntries == null) {
236 mPackageEntryMap.clear();
Adam Lesinski1813c622014-08-27 19:00:30 -0700237 mPreferenceScreen.removeAll();
Adam Lesinski468d3912014-07-22 10:01:08 -0700238 return;
239 }
240
241 // Find the deleted entries and remove them from the PreferenceScreen.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000242 final int oldProfileCount = mPackageEntryMap.size();
243 for (int profileIndex = 0; profileIndex < oldProfileCount; ++profileIndex) {
244 final int profileId = mPackageEntryMap.keyAt(profileIndex);
245 final ArrayMap<String, PackageEntry> oldEntriesForProfile = mPackageEntryMap
246 .valueAt(profileIndex);
247 final int oldPackageCount = oldEntriesForProfile.size();
248
249 final ArrayMap<String, PackageEntry> newEntriesForProfile = newEntries.get(
250 profileId);
251
252 for (int i = 0; i < oldPackageCount; i++) {
253 final PackageEntry oldPackageEntry = oldEntriesForProfile.valueAt(i);
254
255 PackageEntry newPackageEntry = null;
256 if (newEntriesForProfile != null) {
257 newPackageEntry = newEntriesForProfile.get(oldPackageEntry.packageName);
258 }
259 if (newPackageEntry == null) {
260 // This package has been removed.
261 mPreferenceScreen.removePreference(oldPackageEntry.preference);
262 } else {
263 // This package already exists in the preference hierarchy, so reuse that
264 // Preference.
265 newPackageEntry.preference = oldPackageEntry.preference;
266 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700267 }
268 }
269
270 // Now add new packages to the PreferenceScreen.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000271 final int newProfileCount = newEntries.size();
272 for (int profileIndex = 0; profileIndex < newProfileCount; ++profileIndex) {
273 final int profileId = newEntries.keyAt(profileIndex);
274 final ArrayMap<String, PackageEntry> newEntriesForProfile = newEntries.get(
275 profileId);
276 final int packageCount = newEntriesForProfile.size();
277 for (int i = 0; i < packageCount; i++) {
278 final PackageEntry packageEntry = newEntriesForProfile.valueAt(i);
279 if (packageEntry.preference == null) {
280 packageEntry.preference = new SwitchPreference(mContext);
281 packageEntry.preference.setPersistent(false);
282 packageEntry.preference.setOnPreferenceChangeListener(
283 UsageAccessSettings.this);
284 mPreferenceScreen.addPreference(packageEntry.preference);
285 }
286 updatePreference(packageEntry);
Adam Lesinski468d3912014-07-22 10:01:08 -0700287 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700288 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700289 mPackageEntryMap.clear();
290 mPackageEntryMap = newEntries;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000291
292 // Add/remove headers if necessary. If there are package entries only for one user and
293 // that user is not the managed profile then do not show headers.
294 if (mPackageEntryMap.size() == 1 &&
295 mPackageEntryMap.keyAt(0) == UserHandle.myUserId()) {
296 for (int i = 0; i < mCategoryHeaders.length; ++i) {
297 if (mCategoryHeaders[i] != null) {
298 mPreferenceScreen.removePreference(mCategoryHeaders[i]);
299 }
300 mCategoryHeaders[i] = null;
301 }
302 } else {
303 for (int i = 0; i < mCategoryHeaders.length; ++i) {
304 if (mCategoryHeaders[i] == null) {
305 final Preference preference = new Preference(mContext, null,
306 com.android.internal.R.attr.preferenceCategoryStyle, 0);
307 mCategoryHeaders[i] = preference;
308 preference.setTitle(mCategoryHeaderTitleResIds[i]);
309 preference.setEnabled(false);
310 mPreferenceScreen.addPreference(preference);
311 }
312 }
313 }
314
315 // Sort preferences alphabetically within categories
316 int order = 0;
317 final int profileCount = mProfiles.size();
318 for (int i = 0; i < profileCount; ++i) {
319 Preference header = mCategoryHeaders[i];
320 if (header != null) {
321 header.setOrder(order++);
322 }
323 ArrayMap<String, PackageEntry> entriesForProfile =
324 mPackageEntryMap.get(mProfiles.get(i).getIdentifier());
325 if (entriesForProfile != null) {
326 List<PackageEntry> sortedEntries = Collections.list(
327 Collections.enumeration(entriesForProfile.values()));
328 Collections.sort(sortedEntries);
329 for (PackageEntry pe : sortedEntries) {
330 pe.preference.setOrder(order++);
331 }
332 }
333 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700334 }
335
336 private void updatePreference(PackageEntry pe) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000337 final int profileId = pe.userHandle.getIdentifier();
338 // Set something as default
339 pe.preference.setEnabled(false);
340 pe.preference.setTitle(pe.packageName);
341 pe.preference.setIcon(mUserManager.getBadgedIconForUser(mPackageManager
342 .getDefaultActivityIcon(), pe.userHandle));
343 try {
344 // Try setting real title and icon
345 final ApplicationInfo info = mIPackageManager.getApplicationInfo(pe.packageName,
346 0 /* no flags */, profileId);
347 if (info != null) {
348 pe.preference.setEnabled(true);
349 pe.preference.setTitle(info.loadLabel(mPackageManager).toString());
350 pe.preference.setIcon(mUserManager.getBadgedIconForUser(info.loadIcon(
351 mPackageManager), pe.userHandle));
352 }
353 } catch (RemoteException e) {
354 Log.w(TAG, "PackageManager is dead. Can't get app info for package " +
355 pe.packageName + " of user " + profileId);
356 // Keep going to update other parts of the preference
357 }
358
Adam Lesinski468d3912014-07-22 10:01:08 -0700359 pe.preference.setKey(pe.packageName);
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000360 Bundle extra = pe.preference.getExtras();
361 extra.putInt(BUNDLE_KEY_PROFILEID, profileId);
Adam Lesinski468d3912014-07-22 10:01:08 -0700362
363 boolean check = false;
364 if (pe.appOpMode == AppOpsManager.MODE_ALLOWED) {
365 check = true;
366 } else if (pe.appOpMode == AppOpsManager.MODE_DEFAULT) {
367 // If the default AppOps mode is set, then fall back to
368 // whether the app has been granted permission by PackageManager.
369 check = pe.permissionGranted;
370 }
371
372 if (check != pe.preference.isChecked()) {
373 pe.preference.setChecked(check);
374 }
375 }
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000376
377 private boolean isThisUserAProfileOfCurrentUser(final int userId) {
378 final int profilesMax = mProfiles.size();
379 for (int i = 0; i < profilesMax; ++i) {
380 if (mProfiles.get(i).getIdentifier() == userId) {
381 return true;
382 }
383 }
384 return false;
385 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700386 }
387
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700388 static boolean shouldIgnorePackage(String packageName) {
Adam Lesinski468d3912014-07-22 10:01:08 -0700389 return packageName.equals("android") || packageName.equals("com.android.settings");
390 }
391
Adam Lesinski468d3912014-07-22 10:01:08 -0700392 private AppsRequestingAccessFetcher mLastFetcherTask;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000393 SparseArray<ArrayMap<String, PackageEntry>> mPackageEntryMap = new SparseArray<>();
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700394 AppOpsManager mAppOpsManager;
Adam Lesinski1813c622014-08-27 19:00:30 -0700395 PreferenceScreen mPreferenceScreen;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000396 private Preference[] mCategoryHeaders = new Preference[2];
397 private static int[] mCategoryHeaderTitleResIds = new int[] {
398 R.string.category_personal,
399 R.string.category_work
400 };
Adam Lesinski468d3912014-07-22 10:01:08 -0700401
402 @Override
403 public void onCreate(Bundle icicle) {
404 super.onCreate(icicle);
405
406 addPreferencesFromResource(R.xml.usage_access_settings);
Adam Lesinski1813c622014-08-27 19:00:30 -0700407 mPreferenceScreen = getPreferenceScreen();
408 mPreferenceScreen.setOrderingAsAdded(false);
Adam Lesinski468d3912014-07-22 10:01:08 -0700409 mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
410 }
411
412 @Override
413 public void onResume() {
414 super.onResume();
415
416 updateInterestedApps();
417 mPackageMonitor.register(getActivity(), Looper.getMainLooper(), false);
418 }
419
420 @Override
421 public void onPause() {
422 super.onPause();
423
424 mPackageMonitor.unregister();
425 if (mLastFetcherTask != null) {
426 mLastFetcherTask.cancel(true);
427 mLastFetcherTask = null;
428 }
429 }
430
431 private void updateInterestedApps() {
432 if (mLastFetcherTask != null) {
433 // Canceling can only fail for some obscure reason since mLastFetcherTask would be
434 // null if the task has already completed. So we ignore the result of cancel and
435 // spawn a new task to get fresh data. AsyncTask executes tasks serially anyways,
436 // so we are safe from running two tasks at the same time.
437 mLastFetcherTask.cancel(true);
438 }
439
440 mLastFetcherTask = new AppsRequestingAccessFetcher(getActivity());
441 mLastFetcherTask.execute();
442 }
443
444 @Override
445 public boolean onPreferenceChange(Preference preference, Object newValue) {
446 final String packageName = preference.getKey();
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000447 final int profileId = preference.getExtras().getInt(BUNDLE_KEY_PROFILEID);
448 final PackageEntry pe = getPackageEntry(packageName, profileId);
Adam Lesinski468d3912014-07-22 10:01:08 -0700449 if (pe == null) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000450 Log.w(TAG, "Preference change event handling failed");
Adam Lesinski468d3912014-07-22 10:01:08 -0700451 return false;
452 }
453
454 if (!(newValue instanceof Boolean)) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000455 Log.w(TAG, "Preference change event for package " + packageName + " of user " +
456 profileId + " had non boolean value of type " + newValue.getClass().getName());
Adam Lesinski468d3912014-07-22 10:01:08 -0700457 return false;
458 }
459
460 final int newMode = (Boolean) newValue ?
461 AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
Adam Lesinski366e7a22014-07-25 10:20:26 -0700462
463 // Check if we need to do any work.
464 if (pe.appOpMode != newMode) {
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700465 if (newMode != AppOpsManager.MODE_ALLOWED) {
466 // Turning off the setting has no warning.
467 setNewMode(pe, newMode);
468 return true;
469 }
470
471 // Turning on the setting has a Warning.
Adam Lesinski21dfa202014-09-09 11:34:55 -0700472 FragmentTransaction ft = getChildFragmentManager().beginTransaction();
473 Fragment prev = getChildFragmentManager().findFragmentByTag("warning");
474 if (prev != null) {
475 ft.remove(prev);
476 }
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000477 WarningDialogFragment.newInstance(pe).show(ft, "warning");
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700478 return false;
Adam Lesinski366e7a22014-07-25 10:20:26 -0700479 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700480 return true;
481 }
482
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700483 void setNewMode(PackageEntry pe, int newMode) {
484 mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS,
485 pe.packageInfo.applicationInfo.uid, pe.packageName, newMode);
486 pe.appOpMode = newMode;
487 }
488
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000489 void allowAccess(String packageName, int profileId) {
490 final PackageEntry entry = getPackageEntry(packageName, profileId);
Adam Lesinski21dfa202014-09-09 11:34:55 -0700491 if (entry == null) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000492 Log.w(TAG, "Unable to give access");
Adam Lesinski21dfa202014-09-09 11:34:55 -0700493 return;
494 }
495
496 setNewMode(entry, AppOpsManager.MODE_ALLOWED);
497 entry.preference.setChecked(true);
498 }
499
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000500 private PackageEntry getPackageEntry(String packageName, int profileId) {
501 ArrayMap<String, PackageEntry> entriesForProfile = mPackageEntryMap.get(profileId);
502 if (entriesForProfile == null) {
503 Log.w(TAG, "getPackageEntry fails for package " + packageName + " of user " +
504 profileId + ": user does not seem to be valid.");
505 return null;
506 }
507 final PackageEntry entry = entriesForProfile.get(packageName);
508 if (entry == null) {
509 Log.w(TAG, "getPackageEntry fails for package " + packageName + " of user " +
510 profileId + ": package does not exist.");
511 }
512 return entry;
513 }
514
Adam Lesinski468d3912014-07-22 10:01:08 -0700515 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
516 @Override
517 public void onPackageAdded(String packageName, int uid) {
518 updateInterestedApps();
519 }
520
521 @Override
522 public void onPackageRemoved(String packageName, int uid) {
523 updateInterestedApps();
524 }
525 };
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700526
Adam Lesinski21dfa202014-09-09 11:34:55 -0700527 public static class WarningDialogFragment extends DialogFragment
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700528 implements DialogInterface.OnClickListener {
Adam Lesinski21dfa202014-09-09 11:34:55 -0700529 private static final String ARG_PACKAGE_NAME = "package";
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000530 private static final String ARG_PROFILE_ID = "profileId";
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700531
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000532 public static WarningDialogFragment newInstance(PackageEntry pe) {
Adam Lesinski21dfa202014-09-09 11:34:55 -0700533 WarningDialogFragment dialog = new WarningDialogFragment();
534 Bundle args = new Bundle();
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000535 args.putString(ARG_PACKAGE_NAME, pe.packageName);
536 args.putInt(ARG_PROFILE_ID, pe.userHandle.getIdentifier());
Adam Lesinski21dfa202014-09-09 11:34:55 -0700537 dialog.setArguments(args);
538 return dialog;
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700539 }
540
541 @Override
542 public Dialog onCreateDialog(Bundle savedInstanceState) {
543 return new AlertDialog.Builder(getActivity())
544 .setTitle(R.string.allow_usage_access_title)
545 .setMessage(R.string.allow_usage_access_message)
546 .setIconAttribute(android.R.attr.alertDialogIcon)
547 .setNegativeButton(R.string.cancel, this)
Adam Lesinski1813c622014-08-27 19:00:30 -0700548 .setPositiveButton(android.R.string.ok, this)
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700549 .create();
550 }
551
552 @Override
553 public void onClick(DialogInterface dialog, int which) {
554 if (which == DialogInterface.BUTTON_POSITIVE) {
Adam Lesinski21dfa202014-09-09 11:34:55 -0700555 ((UsageAccessSettings) getParentFragment()).allowAccess(
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000556 getArguments().getString(ARG_PACKAGE_NAME),
557 getArguments().getInt(ARG_PROFILE_ID));
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700558 } else {
559 dialog.cancel();
560 }
561 }
562 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700563}