blob: 704c3c51bdddc094550708098a027718ab234f9f [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;
Chris Wren8a963ba2015-03-20 10:29:14 -040046import com.android.internal.logging.MetricsLogger;
Adam Lesinski468d3912014-07-22 10:01:08 -070047
48import java.util.List;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000049import java.util.Collections;
Adam Lesinski468d3912014-07-22 10:01:08 -070050
51public class UsageAccessSettings extends SettingsPreferenceFragment implements
52 Preference.OnPreferenceChangeListener {
53
54 private static final String TAG = "UsageAccessSettings";
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000055 private static final String BUNDLE_KEY_PROFILEID = "profileId";
Adam Lesinski468d3912014-07-22 10:01:08 -070056
57 private static final String[] PM_USAGE_STATS_PERMISSION = new String[] {
58 Manifest.permission.PACKAGE_USAGE_STATS
59 };
60
61 private static final int[] APP_OPS_OP_CODES = new int[] {
62 AppOpsManager.OP_GET_USAGE_STATS
63 };
64
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000065 private static class PackageEntry implements Comparable<PackageEntry> {
66 public PackageEntry(String packageName, UserHandle userHandle) {
Adam Lesinski468d3912014-07-22 10:01:08 -070067 this.packageName = packageName;
68 this.appOpMode = AppOpsManager.MODE_DEFAULT;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000069 this.userHandle = userHandle;
70 }
71
72 @Override
73 public int compareTo(PackageEntry another) {
74 return packageName.compareTo(another.packageName);
Adam Lesinski468d3912014-07-22 10:01:08 -070075 }
76
77 final String packageName;
78 PackageInfo packageInfo;
79 boolean permissionGranted;
80 int appOpMode;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000081 UserHandle userHandle;
Adam Lesinski468d3912014-07-22 10:01:08 -070082
83 SwitchPreference preference;
84 }
85
86 /**
87 * Fetches the list of Apps that are requesting access to the UsageStats API and updates
88 * the PreferenceScreen with the results when complete.
89 */
90 private class AppsRequestingAccessFetcher extends
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000091 AsyncTask<Void, Void, SparseArray<ArrayMap<String, PackageEntry>>> {
Adam Lesinski468d3912014-07-22 10:01:08 -070092
93 private final Context mContext;
94 private final PackageManager mPackageManager;
95 private final IPackageManager mIPackageManager;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000096 private final UserManager mUserManager;
97 private final List<UserHandle> mProfiles;
Adam Lesinski468d3912014-07-22 10:01:08 -070098
99 public AppsRequestingAccessFetcher(Context context) {
100 mContext = context;
101 mPackageManager = context.getPackageManager();
102 mIPackageManager = ActivityThread.getPackageManager();
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000103 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
104 mProfiles = mUserManager.getUserProfiles();
Adam Lesinski468d3912014-07-22 10:01:08 -0700105 }
106
107 @Override
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000108 protected SparseArray<ArrayMap<String, PackageEntry>> doInBackground(Void... params) {
Adam Lesinski468d3912014-07-22 10:01:08 -0700109 final String[] packages;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000110 SparseArray<ArrayMap<String, PackageEntry>> entries;
Adam Lesinski468d3912014-07-22 10:01:08 -0700111 try {
112 packages = mIPackageManager.getAppOpPermissionPackages(
113 Manifest.permission.PACKAGE_USAGE_STATS);
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000114
115 if (packages == null) {
116 // No packages are requesting permission to use the UsageStats API.
117 return null;
118 }
119
120 entries = new SparseArray<>();
121 for (final UserHandle profile : mProfiles) {
122 final ArrayMap<String, PackageEntry> entriesForProfile = new ArrayMap<>();
123 final int profileId = profile.getIdentifier();
124 entries.put(profileId, entriesForProfile);
125 for (final String packageName : packages) {
126 final boolean isAvailable = mIPackageManager.isPackageAvailable(packageName,
127 profileId);
128 if (!shouldIgnorePackage(packageName) && isAvailable) {
129 final PackageEntry newEntry = new PackageEntry(packageName, profile);
130 entriesForProfile.put(packageName, newEntry);
131 }
132 }
133 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700134 } catch (RemoteException e) {
135 Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting "
136 + Manifest.permission.PACKAGE_USAGE_STATS);
137 return null;
138 }
139
Adam Lesinski468d3912014-07-22 10:01:08 -0700140 // Load the packages that have been granted the PACKAGE_USAGE_STATS permission.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000141 try {
142 for (final UserHandle profile : mProfiles) {
143 final int profileId = profile.getIdentifier();
144 final ArrayMap<String, PackageEntry> entriesForProfile = entries.get(profileId);
145 if (entriesForProfile == null) {
146 continue;
147 }
148 final List<PackageInfo> packageInfos = mIPackageManager
149 .getPackagesHoldingPermissions(PM_USAGE_STATS_PERMISSION, 0, profileId)
150 .getList();
151 final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0;
152 for (int i = 0; i < packageInfoCount; i++) {
153 final PackageInfo packageInfo = packageInfos.get(i);
154 final PackageEntry pe = entriesForProfile.get(packageInfo.packageName);
155 if (pe != null) {
156 pe.packageInfo = packageInfo;
157 pe.permissionGranted = true;
158 }
159 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700160 }
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000161 } catch (RemoteException e) {
162 Log.w(TAG, "PackageManager is dead. Can't get list of packages granted "
163 + Manifest.permission.PACKAGE_USAGE_STATS);
164 return null;
Adam Lesinski468d3912014-07-22 10:01:08 -0700165 }
166
167 // Load the remaining packages that have requested but don't have the
168 // PACKAGE_USAGE_STATS permission.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000169 for (final UserHandle profile : mProfiles) {
170 final int profileId = profile.getIdentifier();
171 final ArrayMap<String, PackageEntry> entriesForProfile = entries.get(profileId);
172 if (entriesForProfile == null) {
173 continue;
174 }
175 int packageCount = entriesForProfile.size();
176 for (int i = packageCount - 1; i >= 0; --i) {
177 final PackageEntry pe = entriesForProfile.valueAt(i);
178 if (pe.packageInfo == null) {
179 try {
180 pe.packageInfo = mIPackageManager.getPackageInfo(pe.packageName, 0,
181 profileId);
182 } catch (RemoteException e) {
183 // This package doesn't exist. This may occur when an app is
184 // uninstalled for one user, but it is not removed from the system.
185 entriesForProfile.removeAt(i);
186 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700187 }
188 }
189 }
190
191 // Find out which packages have been granted permission from AppOps.
192 final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps(
193 APP_OPS_OP_CODES);
194 final int packageOpsCount = packageOps != null ? packageOps.size() : 0;
195 for (int i = 0; i < packageOpsCount; i++) {
196 final AppOpsManager.PackageOps packageOp = packageOps.get(i);
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000197 final int userId = UserHandle.getUserId(packageOp.getUid());
198 if (!isThisUserAProfileOfCurrentUser(userId)) {
199 // This AppOp does not belong to any of this user's profiles.
Adam Lesinski468d3912014-07-22 10:01:08 -0700200 continue;
201 }
202
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000203 final ArrayMap<String, PackageEntry> entriesForProfile = entries.get(userId);
204 if (entriesForProfile == null) {
205 continue;
206 }
207 final PackageEntry pe = entriesForProfile.get(packageOp.getPackageName());
208 if (pe == null) {
209 Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName()
210 + " of user " + userId +
211 " but package doesn't exist or did not request UsageStats access");
Adam Lesinski366e7a22014-07-25 10:20:26 -0700212 continue;
213 }
214
Adam Lesinski468d3912014-07-22 10:01:08 -0700215 if (packageOp.getOps().size() < 1) {
216 Log.w(TAG, "No AppOps permission exists for package "
217 + packageOp.getPackageName());
218 continue;
219 }
220
221 pe.appOpMode = packageOp.getOps().get(0).getMode();
222 }
223
224 return entries;
225 }
226
227 @Override
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000228 protected void onPostExecute(SparseArray<ArrayMap<String, PackageEntry>> newEntries) {
Adam Lesinski468d3912014-07-22 10:01:08 -0700229 mLastFetcherTask = null;
230
231 if (getActivity() == null) {
232 // We must have finished the Activity while we were processing in the background.
233 return;
234 }
235
236 if (newEntries == null) {
237 mPackageEntryMap.clear();
Adam Lesinski1813c622014-08-27 19:00:30 -0700238 mPreferenceScreen.removeAll();
Adam Lesinski468d3912014-07-22 10:01:08 -0700239 return;
240 }
241
242 // Find the deleted entries and remove them from the PreferenceScreen.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000243 final int oldProfileCount = mPackageEntryMap.size();
244 for (int profileIndex = 0; profileIndex < oldProfileCount; ++profileIndex) {
245 final int profileId = mPackageEntryMap.keyAt(profileIndex);
246 final ArrayMap<String, PackageEntry> oldEntriesForProfile = mPackageEntryMap
247 .valueAt(profileIndex);
248 final int oldPackageCount = oldEntriesForProfile.size();
249
250 final ArrayMap<String, PackageEntry> newEntriesForProfile = newEntries.get(
251 profileId);
252
253 for (int i = 0; i < oldPackageCount; i++) {
254 final PackageEntry oldPackageEntry = oldEntriesForProfile.valueAt(i);
255
256 PackageEntry newPackageEntry = null;
257 if (newEntriesForProfile != null) {
258 newPackageEntry = newEntriesForProfile.get(oldPackageEntry.packageName);
259 }
260 if (newPackageEntry == null) {
261 // This package has been removed.
262 mPreferenceScreen.removePreference(oldPackageEntry.preference);
263 } else {
264 // This package already exists in the preference hierarchy, so reuse that
265 // Preference.
266 newPackageEntry.preference = oldPackageEntry.preference;
267 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700268 }
269 }
270
271 // Now add new packages to the PreferenceScreen.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000272 final int newProfileCount = newEntries.size();
273 for (int profileIndex = 0; profileIndex < newProfileCount; ++profileIndex) {
274 final int profileId = newEntries.keyAt(profileIndex);
275 final ArrayMap<String, PackageEntry> newEntriesForProfile = newEntries.get(
276 profileId);
277 final int packageCount = newEntriesForProfile.size();
278 for (int i = 0; i < packageCount; i++) {
279 final PackageEntry packageEntry = newEntriesForProfile.valueAt(i);
280 if (packageEntry.preference == null) {
281 packageEntry.preference = new SwitchPreference(mContext);
282 packageEntry.preference.setPersistent(false);
283 packageEntry.preference.setOnPreferenceChangeListener(
284 UsageAccessSettings.this);
285 mPreferenceScreen.addPreference(packageEntry.preference);
286 }
287 updatePreference(packageEntry);
Adam Lesinski468d3912014-07-22 10:01:08 -0700288 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700289 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700290 mPackageEntryMap.clear();
291 mPackageEntryMap = newEntries;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000292
293 // Add/remove headers if necessary. If there are package entries only for one user and
294 // that user is not the managed profile then do not show headers.
295 if (mPackageEntryMap.size() == 1 &&
296 mPackageEntryMap.keyAt(0) == UserHandle.myUserId()) {
297 for (int i = 0; i < mCategoryHeaders.length; ++i) {
298 if (mCategoryHeaders[i] != null) {
299 mPreferenceScreen.removePreference(mCategoryHeaders[i]);
300 }
301 mCategoryHeaders[i] = null;
302 }
303 } else {
304 for (int i = 0; i < mCategoryHeaders.length; ++i) {
305 if (mCategoryHeaders[i] == null) {
306 final Preference preference = new Preference(mContext, null,
307 com.android.internal.R.attr.preferenceCategoryStyle, 0);
308 mCategoryHeaders[i] = preference;
309 preference.setTitle(mCategoryHeaderTitleResIds[i]);
310 preference.setEnabled(false);
311 mPreferenceScreen.addPreference(preference);
312 }
313 }
314 }
315
316 // Sort preferences alphabetically within categories
317 int order = 0;
318 final int profileCount = mProfiles.size();
319 for (int i = 0; i < profileCount; ++i) {
320 Preference header = mCategoryHeaders[i];
321 if (header != null) {
322 header.setOrder(order++);
323 }
324 ArrayMap<String, PackageEntry> entriesForProfile =
325 mPackageEntryMap.get(mProfiles.get(i).getIdentifier());
326 if (entriesForProfile != null) {
327 List<PackageEntry> sortedEntries = Collections.list(
328 Collections.enumeration(entriesForProfile.values()));
329 Collections.sort(sortedEntries);
330 for (PackageEntry pe : sortedEntries) {
331 pe.preference.setOrder(order++);
332 }
333 }
334 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700335 }
336
337 private void updatePreference(PackageEntry pe) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000338 final int profileId = pe.userHandle.getIdentifier();
339 // Set something as default
340 pe.preference.setEnabled(false);
341 pe.preference.setTitle(pe.packageName);
342 pe.preference.setIcon(mUserManager.getBadgedIconForUser(mPackageManager
343 .getDefaultActivityIcon(), pe.userHandle));
344 try {
345 // Try setting real title and icon
346 final ApplicationInfo info = mIPackageManager.getApplicationInfo(pe.packageName,
347 0 /* no flags */, profileId);
348 if (info != null) {
349 pe.preference.setEnabled(true);
350 pe.preference.setTitle(info.loadLabel(mPackageManager).toString());
351 pe.preference.setIcon(mUserManager.getBadgedIconForUser(info.loadIcon(
352 mPackageManager), pe.userHandle));
353 }
354 } catch (RemoteException e) {
355 Log.w(TAG, "PackageManager is dead. Can't get app info for package " +
356 pe.packageName + " of user " + profileId);
357 // Keep going to update other parts of the preference
358 }
359
Adam Lesinski468d3912014-07-22 10:01:08 -0700360 pe.preference.setKey(pe.packageName);
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000361 Bundle extra = pe.preference.getExtras();
362 extra.putInt(BUNDLE_KEY_PROFILEID, profileId);
Adam Lesinski468d3912014-07-22 10:01:08 -0700363
364 boolean check = false;
365 if (pe.appOpMode == AppOpsManager.MODE_ALLOWED) {
366 check = true;
367 } else if (pe.appOpMode == AppOpsManager.MODE_DEFAULT) {
368 // If the default AppOps mode is set, then fall back to
369 // whether the app has been granted permission by PackageManager.
370 check = pe.permissionGranted;
371 }
372
373 if (check != pe.preference.isChecked()) {
374 pe.preference.setChecked(check);
375 }
376 }
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000377
378 private boolean isThisUserAProfileOfCurrentUser(final int userId) {
379 final int profilesMax = mProfiles.size();
380 for (int i = 0; i < profilesMax; ++i) {
381 if (mProfiles.get(i).getIdentifier() == userId) {
382 return true;
383 }
384 }
385 return false;
386 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700387 }
388
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700389 static boolean shouldIgnorePackage(String packageName) {
Adam Lesinski468d3912014-07-22 10:01:08 -0700390 return packageName.equals("android") || packageName.equals("com.android.settings");
391 }
392
Adam Lesinski468d3912014-07-22 10:01:08 -0700393 private AppsRequestingAccessFetcher mLastFetcherTask;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000394 SparseArray<ArrayMap<String, PackageEntry>> mPackageEntryMap = new SparseArray<>();
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700395 AppOpsManager mAppOpsManager;
Adam Lesinski1813c622014-08-27 19:00:30 -0700396 PreferenceScreen mPreferenceScreen;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000397 private Preference[] mCategoryHeaders = new Preference[2];
398 private static int[] mCategoryHeaderTitleResIds = new int[] {
399 R.string.category_personal,
400 R.string.category_work
401 };
Adam Lesinski468d3912014-07-22 10:01:08 -0700402
403 @Override
Chris Wren8a963ba2015-03-20 10:29:14 -0400404 protected int getMetricsCategory() {
405 return MetricsLogger.USAGE_ACCESS;
406 }
407
408 @Override
Adam Lesinski468d3912014-07-22 10:01:08 -0700409 public void onCreate(Bundle icicle) {
410 super.onCreate(icicle);
411
412 addPreferencesFromResource(R.xml.usage_access_settings);
Adam Lesinski1813c622014-08-27 19:00:30 -0700413 mPreferenceScreen = getPreferenceScreen();
414 mPreferenceScreen.setOrderingAsAdded(false);
Adam Lesinski468d3912014-07-22 10:01:08 -0700415 mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
416 }
417
418 @Override
419 public void onResume() {
420 super.onResume();
421
422 updateInterestedApps();
423 mPackageMonitor.register(getActivity(), Looper.getMainLooper(), false);
424 }
425
426 @Override
427 public void onPause() {
428 super.onPause();
429
430 mPackageMonitor.unregister();
431 if (mLastFetcherTask != null) {
432 mLastFetcherTask.cancel(true);
433 mLastFetcherTask = null;
434 }
435 }
436
437 private void updateInterestedApps() {
438 if (mLastFetcherTask != null) {
439 // Canceling can only fail for some obscure reason since mLastFetcherTask would be
440 // null if the task has already completed. So we ignore the result of cancel and
441 // spawn a new task to get fresh data. AsyncTask executes tasks serially anyways,
442 // so we are safe from running two tasks at the same time.
443 mLastFetcherTask.cancel(true);
444 }
445
446 mLastFetcherTask = new AppsRequestingAccessFetcher(getActivity());
447 mLastFetcherTask.execute();
448 }
449
450 @Override
451 public boolean onPreferenceChange(Preference preference, Object newValue) {
452 final String packageName = preference.getKey();
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000453 final int profileId = preference.getExtras().getInt(BUNDLE_KEY_PROFILEID);
454 final PackageEntry pe = getPackageEntry(packageName, profileId);
Adam Lesinski468d3912014-07-22 10:01:08 -0700455 if (pe == null) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000456 Log.w(TAG, "Preference change event handling failed");
Adam Lesinski468d3912014-07-22 10:01:08 -0700457 return false;
458 }
459
460 if (!(newValue instanceof Boolean)) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000461 Log.w(TAG, "Preference change event for package " + packageName + " of user " +
462 profileId + " had non boolean value of type " + newValue.getClass().getName());
Adam Lesinski468d3912014-07-22 10:01:08 -0700463 return false;
464 }
465
466 final int newMode = (Boolean) newValue ?
467 AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
Adam Lesinski366e7a22014-07-25 10:20:26 -0700468
469 // Check if we need to do any work.
470 if (pe.appOpMode != newMode) {
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700471 if (newMode != AppOpsManager.MODE_ALLOWED) {
472 // Turning off the setting has no warning.
473 setNewMode(pe, newMode);
474 return true;
475 }
476
477 // Turning on the setting has a Warning.
Adam Lesinski21dfa202014-09-09 11:34:55 -0700478 FragmentTransaction ft = getChildFragmentManager().beginTransaction();
479 Fragment prev = getChildFragmentManager().findFragmentByTag("warning");
480 if (prev != null) {
481 ft.remove(prev);
482 }
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000483 WarningDialogFragment.newInstance(pe).show(ft, "warning");
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700484 return false;
Adam Lesinski366e7a22014-07-25 10:20:26 -0700485 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700486 return true;
487 }
488
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700489 void setNewMode(PackageEntry pe, int newMode) {
490 mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS,
491 pe.packageInfo.applicationInfo.uid, pe.packageName, newMode);
492 pe.appOpMode = newMode;
493 }
494
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000495 void allowAccess(String packageName, int profileId) {
496 final PackageEntry entry = getPackageEntry(packageName, profileId);
Adam Lesinski21dfa202014-09-09 11:34:55 -0700497 if (entry == null) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000498 Log.w(TAG, "Unable to give access");
Adam Lesinski21dfa202014-09-09 11:34:55 -0700499 return;
500 }
501
502 setNewMode(entry, AppOpsManager.MODE_ALLOWED);
503 entry.preference.setChecked(true);
504 }
505
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000506 private PackageEntry getPackageEntry(String packageName, int profileId) {
507 ArrayMap<String, PackageEntry> entriesForProfile = mPackageEntryMap.get(profileId);
508 if (entriesForProfile == null) {
509 Log.w(TAG, "getPackageEntry fails for package " + packageName + " of user " +
510 profileId + ": user does not seem to be valid.");
511 return null;
512 }
513 final PackageEntry entry = entriesForProfile.get(packageName);
514 if (entry == null) {
515 Log.w(TAG, "getPackageEntry fails for package " + packageName + " of user " +
516 profileId + ": package does not exist.");
517 }
518 return entry;
519 }
520
Adam Lesinski468d3912014-07-22 10:01:08 -0700521 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
522 @Override
523 public void onPackageAdded(String packageName, int uid) {
524 updateInterestedApps();
525 }
526
527 @Override
528 public void onPackageRemoved(String packageName, int uid) {
529 updateInterestedApps();
530 }
531 };
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700532
Adam Lesinski21dfa202014-09-09 11:34:55 -0700533 public static class WarningDialogFragment extends DialogFragment
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700534 implements DialogInterface.OnClickListener {
Adam Lesinski21dfa202014-09-09 11:34:55 -0700535 private static final String ARG_PACKAGE_NAME = "package";
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000536 private static final String ARG_PROFILE_ID = "profileId";
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700537
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000538 public static WarningDialogFragment newInstance(PackageEntry pe) {
Adam Lesinski21dfa202014-09-09 11:34:55 -0700539 WarningDialogFragment dialog = new WarningDialogFragment();
540 Bundle args = new Bundle();
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000541 args.putString(ARG_PACKAGE_NAME, pe.packageName);
542 args.putInt(ARG_PROFILE_ID, pe.userHandle.getIdentifier());
Adam Lesinski21dfa202014-09-09 11:34:55 -0700543 dialog.setArguments(args);
544 return dialog;
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700545 }
546
547 @Override
548 public Dialog onCreateDialog(Bundle savedInstanceState) {
549 return new AlertDialog.Builder(getActivity())
550 .setTitle(R.string.allow_usage_access_title)
551 .setMessage(R.string.allow_usage_access_message)
552 .setIconAttribute(android.R.attr.alertDialogIcon)
553 .setNegativeButton(R.string.cancel, this)
Adam Lesinski1813c622014-08-27 19:00:30 -0700554 .setPositiveButton(android.R.string.ok, this)
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700555 .create();
556 }
557
558 @Override
559 public void onClick(DialogInterface dialog, int which) {
560 if (which == DialogInterface.BUTTON_POSITIVE) {
Adam Lesinski21dfa202014-09-09 11:34:55 -0700561 ((UsageAccessSettings) getParentFragment()).allowAccess(
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000562 getArguments().getString(ARG_PACKAGE_NAME),
563 getArguments().getInt(ARG_PROFILE_ID));
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700564 } else {
565 dialog.cancel();
566 }
567 }
568 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700569}