blob: fd98b51a5424e0caff7351424595b090c0879687 [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;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000020import com.android.settings.DataUsageSummary.AppItem;
Adam Lesinski468d3912014-07-22 10:01:08 -070021
22import android.Manifest;
23import android.app.ActivityThread;
Adam Lesinskie01dfd12014-08-11 20:54:12 -070024import android.app.AlertDialog;
Adam Lesinski468d3912014-07-22 10:01:08 -070025import android.app.AppOpsManager;
Adam Lesinskie01dfd12014-08-11 20:54:12 -070026import android.app.Dialog;
27import android.app.DialogFragment;
Adam Lesinski21dfa202014-09-09 11:34:55 -070028import android.app.Fragment;
29import android.app.FragmentTransaction;
Adam Lesinski468d3912014-07-22 10:01:08 -070030import android.content.Context;
Adam Lesinskie01dfd12014-08-11 20:54:12 -070031import android.content.DialogInterface;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000032import android.content.pm.ApplicationInfo;
Adam Lesinski468d3912014-07-22 10:01:08 -070033import android.content.pm.IPackageManager;
34import android.content.pm.PackageInfo;
35import android.content.pm.PackageManager;
36import android.os.AsyncTask;
37import android.os.Bundle;
38import android.os.Looper;
39import android.os.RemoteException;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000040import android.os.UserHandle;
41import android.os.UserManager;
Adam Lesinski468d3912014-07-22 10:01:08 -070042import android.preference.Preference;
Adam Lesinski1813c622014-08-27 19:00:30 -070043import android.preference.PreferenceScreen;
Adam Lesinski468d3912014-07-22 10:01:08 -070044import android.preference.SwitchPreference;
45import android.util.ArrayMap;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000046import android.util.AttributeSet;
Adam Lesinski468d3912014-07-22 10:01:08 -070047import android.util.Log;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000048import android.util.SparseArray;
Adam Lesinski468d3912014-07-22 10:01:08 -070049
50import java.util.List;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000051import java.util.Collections;
Adam Lesinski468d3912014-07-22 10:01:08 -070052
53public class UsageAccessSettings extends SettingsPreferenceFragment implements
54 Preference.OnPreferenceChangeListener {
55
56 private static final String TAG = "UsageAccessSettings";
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000057 private static final String BUNDLE_KEY_PROFILEID = "profileId";
Adam Lesinski468d3912014-07-22 10:01:08 -070058
59 private static final String[] PM_USAGE_STATS_PERMISSION = new String[] {
60 Manifest.permission.PACKAGE_USAGE_STATS
61 };
62
63 private static final int[] APP_OPS_OP_CODES = new int[] {
64 AppOpsManager.OP_GET_USAGE_STATS
65 };
66
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000067 private static class PackageEntry implements Comparable<PackageEntry> {
68 public PackageEntry(String packageName, UserHandle userHandle) {
Adam Lesinski468d3912014-07-22 10:01:08 -070069 this.packageName = packageName;
70 this.appOpMode = AppOpsManager.MODE_DEFAULT;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000071 this.userHandle = userHandle;
72 }
73
74 @Override
75 public int compareTo(PackageEntry another) {
76 return packageName.compareTo(another.packageName);
Adam Lesinski468d3912014-07-22 10:01:08 -070077 }
78
79 final String packageName;
80 PackageInfo packageInfo;
81 boolean permissionGranted;
82 int appOpMode;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000083 UserHandle userHandle;
Adam Lesinski468d3912014-07-22 10:01:08 -070084
85 SwitchPreference preference;
86 }
87
88 /**
89 * Fetches the list of Apps that are requesting access to the UsageStats API and updates
90 * the PreferenceScreen with the results when complete.
91 */
92 private class AppsRequestingAccessFetcher extends
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000093 AsyncTask<Void, Void, SparseArray<ArrayMap<String, PackageEntry>>> {
Adam Lesinski468d3912014-07-22 10:01:08 -070094
95 private final Context mContext;
96 private final PackageManager mPackageManager;
97 private final IPackageManager mIPackageManager;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +000098 private final UserManager mUserManager;
99 private final List<UserHandle> mProfiles;
Adam Lesinski468d3912014-07-22 10:01:08 -0700100
101 public AppsRequestingAccessFetcher(Context context) {
102 mContext = context;
103 mPackageManager = context.getPackageManager();
104 mIPackageManager = ActivityThread.getPackageManager();
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000105 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
106 mProfiles = mUserManager.getUserProfiles();
Adam Lesinski468d3912014-07-22 10:01:08 -0700107 }
108
109 @Override
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000110 protected SparseArray<ArrayMap<String, PackageEntry>> doInBackground(Void... params) {
Adam Lesinski468d3912014-07-22 10:01:08 -0700111 final String[] packages;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000112 SparseArray<ArrayMap<String, PackageEntry>> entries;
Adam Lesinski468d3912014-07-22 10:01:08 -0700113 try {
114 packages = mIPackageManager.getAppOpPermissionPackages(
115 Manifest.permission.PACKAGE_USAGE_STATS);
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000116
117 if (packages == null) {
118 // No packages are requesting permission to use the UsageStats API.
119 return null;
120 }
121
122 entries = new SparseArray<>();
123 for (final UserHandle profile : mProfiles) {
124 final ArrayMap<String, PackageEntry> entriesForProfile = new ArrayMap<>();
125 final int profileId = profile.getIdentifier();
126 entries.put(profileId, entriesForProfile);
127 for (final String packageName : packages) {
128 final boolean isAvailable = mIPackageManager.isPackageAvailable(packageName,
129 profileId);
130 if (!shouldIgnorePackage(packageName) && isAvailable) {
131 final PackageEntry newEntry = new PackageEntry(packageName, profile);
132 entriesForProfile.put(packageName, newEntry);
133 }
134 }
135 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700136 } catch (RemoteException e) {
137 Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting "
138 + Manifest.permission.PACKAGE_USAGE_STATS);
139 return null;
140 }
141
Adam Lesinski468d3912014-07-22 10:01:08 -0700142 // Load the packages that have been granted the PACKAGE_USAGE_STATS permission.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000143 try {
144 for (final UserHandle profile : mProfiles) {
145 final int profileId = profile.getIdentifier();
146 final ArrayMap<String, PackageEntry> entriesForProfile = entries.get(profileId);
147 if (entriesForProfile == null) {
148 continue;
149 }
150 final List<PackageInfo> packageInfos = mIPackageManager
151 .getPackagesHoldingPermissions(PM_USAGE_STATS_PERMISSION, 0, profileId)
152 .getList();
153 final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0;
154 for (int i = 0; i < packageInfoCount; i++) {
155 final PackageInfo packageInfo = packageInfos.get(i);
156 final PackageEntry pe = entriesForProfile.get(packageInfo.packageName);
157 if (pe != null) {
158 pe.packageInfo = packageInfo;
159 pe.permissionGranted = true;
160 }
161 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700162 }
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000163 } catch (RemoteException e) {
164 Log.w(TAG, "PackageManager is dead. Can't get list of packages granted "
165 + Manifest.permission.PACKAGE_USAGE_STATS);
166 return null;
Adam Lesinski468d3912014-07-22 10:01:08 -0700167 }
168
169 // Load the remaining packages that have requested but don't have the
170 // PACKAGE_USAGE_STATS permission.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000171 for (final UserHandle profile : mProfiles) {
172 final int profileId = profile.getIdentifier();
173 final ArrayMap<String, PackageEntry> entriesForProfile = entries.get(profileId);
174 if (entriesForProfile == null) {
175 continue;
176 }
177 int packageCount = entriesForProfile.size();
178 for (int i = packageCount - 1; i >= 0; --i) {
179 final PackageEntry pe = entriesForProfile.valueAt(i);
180 if (pe.packageInfo == null) {
181 try {
182 pe.packageInfo = mIPackageManager.getPackageInfo(pe.packageName, 0,
183 profileId);
184 } catch (RemoteException e) {
185 // This package doesn't exist. This may occur when an app is
186 // uninstalled for one user, but it is not removed from the system.
187 entriesForProfile.removeAt(i);
188 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700189 }
190 }
191 }
192
193 // Find out which packages have been granted permission from AppOps.
194 final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps(
195 APP_OPS_OP_CODES);
196 final int packageOpsCount = packageOps != null ? packageOps.size() : 0;
197 for (int i = 0; i < packageOpsCount; i++) {
198 final AppOpsManager.PackageOps packageOp = packageOps.get(i);
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000199 final int userId = UserHandle.getUserId(packageOp.getUid());
200 if (!isThisUserAProfileOfCurrentUser(userId)) {
201 // This AppOp does not belong to any of this user's profiles.
Adam Lesinski468d3912014-07-22 10:01:08 -0700202 continue;
203 }
204
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000205 final ArrayMap<String, PackageEntry> entriesForProfile = entries.get(userId);
206 if (entriesForProfile == null) {
207 continue;
208 }
209 final PackageEntry pe = entriesForProfile.get(packageOp.getPackageName());
210 if (pe == null) {
211 Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName()
212 + " of user " + userId +
213 " but package doesn't exist or did not request UsageStats access");
Adam Lesinski366e7a22014-07-25 10:20:26 -0700214 continue;
215 }
216
Adam Lesinski468d3912014-07-22 10:01:08 -0700217 if (packageOp.getOps().size() < 1) {
218 Log.w(TAG, "No AppOps permission exists for package "
219 + packageOp.getPackageName());
220 continue;
221 }
222
223 pe.appOpMode = packageOp.getOps().get(0).getMode();
224 }
225
226 return entries;
227 }
228
229 @Override
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000230 protected void onPostExecute(SparseArray<ArrayMap<String, PackageEntry>> newEntries) {
Adam Lesinski468d3912014-07-22 10:01:08 -0700231 mLastFetcherTask = null;
232
233 if (getActivity() == null) {
234 // We must have finished the Activity while we were processing in the background.
235 return;
236 }
237
238 if (newEntries == null) {
239 mPackageEntryMap.clear();
Adam Lesinski1813c622014-08-27 19:00:30 -0700240 mPreferenceScreen.removeAll();
Adam Lesinski468d3912014-07-22 10:01:08 -0700241 return;
242 }
243
244 // Find the deleted entries and remove them from the PreferenceScreen.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000245 final int oldProfileCount = mPackageEntryMap.size();
246 for (int profileIndex = 0; profileIndex < oldProfileCount; ++profileIndex) {
247 final int profileId = mPackageEntryMap.keyAt(profileIndex);
248 final ArrayMap<String, PackageEntry> oldEntriesForProfile = mPackageEntryMap
249 .valueAt(profileIndex);
250 final int oldPackageCount = oldEntriesForProfile.size();
251
252 final ArrayMap<String, PackageEntry> newEntriesForProfile = newEntries.get(
253 profileId);
254
255 for (int i = 0; i < oldPackageCount; i++) {
256 final PackageEntry oldPackageEntry = oldEntriesForProfile.valueAt(i);
257
258 PackageEntry newPackageEntry = null;
259 if (newEntriesForProfile != null) {
260 newPackageEntry = newEntriesForProfile.get(oldPackageEntry.packageName);
261 }
262 if (newPackageEntry == null) {
263 // This package has been removed.
264 mPreferenceScreen.removePreference(oldPackageEntry.preference);
265 } else {
266 // This package already exists in the preference hierarchy, so reuse that
267 // Preference.
268 newPackageEntry.preference = oldPackageEntry.preference;
269 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700270 }
271 }
272
273 // Now add new packages to the PreferenceScreen.
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000274 final int newProfileCount = newEntries.size();
275 for (int profileIndex = 0; profileIndex < newProfileCount; ++profileIndex) {
276 final int profileId = newEntries.keyAt(profileIndex);
277 final ArrayMap<String, PackageEntry> newEntriesForProfile = newEntries.get(
278 profileId);
279 final int packageCount = newEntriesForProfile.size();
280 for (int i = 0; i < packageCount; i++) {
281 final PackageEntry packageEntry = newEntriesForProfile.valueAt(i);
282 if (packageEntry.preference == null) {
283 packageEntry.preference = new SwitchPreference(mContext);
284 packageEntry.preference.setPersistent(false);
285 packageEntry.preference.setOnPreferenceChangeListener(
286 UsageAccessSettings.this);
287 mPreferenceScreen.addPreference(packageEntry.preference);
288 }
289 updatePreference(packageEntry);
Adam Lesinski468d3912014-07-22 10:01:08 -0700290 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700291 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700292 mPackageEntryMap.clear();
293 mPackageEntryMap = newEntries;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000294
295 // Add/remove headers if necessary. If there are package entries only for one user and
296 // that user is not the managed profile then do not show headers.
297 if (mPackageEntryMap.size() == 1 &&
298 mPackageEntryMap.keyAt(0) == UserHandle.myUserId()) {
299 for (int i = 0; i < mCategoryHeaders.length; ++i) {
300 if (mCategoryHeaders[i] != null) {
301 mPreferenceScreen.removePreference(mCategoryHeaders[i]);
302 }
303 mCategoryHeaders[i] = null;
304 }
305 } else {
306 for (int i = 0; i < mCategoryHeaders.length; ++i) {
307 if (mCategoryHeaders[i] == null) {
308 final Preference preference = new Preference(mContext, null,
309 com.android.internal.R.attr.preferenceCategoryStyle, 0);
310 mCategoryHeaders[i] = preference;
311 preference.setTitle(mCategoryHeaderTitleResIds[i]);
312 preference.setEnabled(false);
313 mPreferenceScreen.addPreference(preference);
314 }
315 }
316 }
317
318 // Sort preferences alphabetically within categories
319 int order = 0;
320 final int profileCount = mProfiles.size();
321 for (int i = 0; i < profileCount; ++i) {
322 Preference header = mCategoryHeaders[i];
323 if (header != null) {
324 header.setOrder(order++);
325 }
326 ArrayMap<String, PackageEntry> entriesForProfile =
327 mPackageEntryMap.get(mProfiles.get(i).getIdentifier());
328 if (entriesForProfile != null) {
329 List<PackageEntry> sortedEntries = Collections.list(
330 Collections.enumeration(entriesForProfile.values()));
331 Collections.sort(sortedEntries);
332 for (PackageEntry pe : sortedEntries) {
333 pe.preference.setOrder(order++);
334 }
335 }
336 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700337 }
338
339 private void updatePreference(PackageEntry pe) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000340 final int profileId = pe.userHandle.getIdentifier();
341 // Set something as default
342 pe.preference.setEnabled(false);
343 pe.preference.setTitle(pe.packageName);
344 pe.preference.setIcon(mUserManager.getBadgedIconForUser(mPackageManager
345 .getDefaultActivityIcon(), pe.userHandle));
346 try {
347 // Try setting real title and icon
348 final ApplicationInfo info = mIPackageManager.getApplicationInfo(pe.packageName,
349 0 /* no flags */, profileId);
350 if (info != null) {
351 pe.preference.setEnabled(true);
352 pe.preference.setTitle(info.loadLabel(mPackageManager).toString());
353 pe.preference.setIcon(mUserManager.getBadgedIconForUser(info.loadIcon(
354 mPackageManager), pe.userHandle));
355 }
356 } catch (RemoteException e) {
357 Log.w(TAG, "PackageManager is dead. Can't get app info for package " +
358 pe.packageName + " of user " + profileId);
359 // Keep going to update other parts of the preference
360 }
361
Adam Lesinski468d3912014-07-22 10:01:08 -0700362 pe.preference.setKey(pe.packageName);
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000363 Bundle extra = pe.preference.getExtras();
364 extra.putInt(BUNDLE_KEY_PROFILEID, profileId);
Adam Lesinski468d3912014-07-22 10:01:08 -0700365
366 boolean check = false;
367 if (pe.appOpMode == AppOpsManager.MODE_ALLOWED) {
368 check = true;
369 } else if (pe.appOpMode == AppOpsManager.MODE_DEFAULT) {
370 // If the default AppOps mode is set, then fall back to
371 // whether the app has been granted permission by PackageManager.
372 check = pe.permissionGranted;
373 }
374
375 if (check != pe.preference.isChecked()) {
376 pe.preference.setChecked(check);
377 }
378 }
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000379
380 private boolean isThisUserAProfileOfCurrentUser(final int userId) {
381 final int profilesMax = mProfiles.size();
382 for (int i = 0; i < profilesMax; ++i) {
383 if (mProfiles.get(i).getIdentifier() == userId) {
384 return true;
385 }
386 }
387 return false;
388 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700389 }
390
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700391 static boolean shouldIgnorePackage(String packageName) {
Adam Lesinski468d3912014-07-22 10:01:08 -0700392 return packageName.equals("android") || packageName.equals("com.android.settings");
393 }
394
Adam Lesinski468d3912014-07-22 10:01:08 -0700395 private AppsRequestingAccessFetcher mLastFetcherTask;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000396 SparseArray<ArrayMap<String, PackageEntry>> mPackageEntryMap = new SparseArray<>();
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700397 AppOpsManager mAppOpsManager;
Adam Lesinski1813c622014-08-27 19:00:30 -0700398 PreferenceScreen mPreferenceScreen;
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000399 private Preference[] mCategoryHeaders = new Preference[2];
400 private static int[] mCategoryHeaderTitleResIds = new int[] {
401 R.string.category_personal,
402 R.string.category_work
403 };
Adam Lesinski468d3912014-07-22 10:01:08 -0700404
405 @Override
406 public void onCreate(Bundle icicle) {
407 super.onCreate(icicle);
408
409 addPreferencesFromResource(R.xml.usage_access_settings);
Adam Lesinski1813c622014-08-27 19:00:30 -0700410 mPreferenceScreen = getPreferenceScreen();
411 mPreferenceScreen.setOrderingAsAdded(false);
Adam Lesinski468d3912014-07-22 10:01:08 -0700412 mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
413 }
414
415 @Override
416 public void onResume() {
417 super.onResume();
418
419 updateInterestedApps();
420 mPackageMonitor.register(getActivity(), Looper.getMainLooper(), false);
421 }
422
423 @Override
424 public void onPause() {
425 super.onPause();
426
427 mPackageMonitor.unregister();
428 if (mLastFetcherTask != null) {
429 mLastFetcherTask.cancel(true);
430 mLastFetcherTask = null;
431 }
432 }
433
434 private void updateInterestedApps() {
435 if (mLastFetcherTask != null) {
436 // Canceling can only fail for some obscure reason since mLastFetcherTask would be
437 // null if the task has already completed. So we ignore the result of cancel and
438 // spawn a new task to get fresh data. AsyncTask executes tasks serially anyways,
439 // so we are safe from running two tasks at the same time.
440 mLastFetcherTask.cancel(true);
441 }
442
443 mLastFetcherTask = new AppsRequestingAccessFetcher(getActivity());
444 mLastFetcherTask.execute();
445 }
446
447 @Override
448 public boolean onPreferenceChange(Preference preference, Object newValue) {
449 final String packageName = preference.getKey();
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000450 final int profileId = preference.getExtras().getInt(BUNDLE_KEY_PROFILEID);
451 final PackageEntry pe = getPackageEntry(packageName, profileId);
Adam Lesinski468d3912014-07-22 10:01:08 -0700452 if (pe == null) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000453 Log.w(TAG, "Preference change event handling failed");
Adam Lesinski468d3912014-07-22 10:01:08 -0700454 return false;
455 }
456
457 if (!(newValue instanceof Boolean)) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000458 Log.w(TAG, "Preference change event for package " + packageName + " of user " +
459 profileId + " had non boolean value of type " + newValue.getClass().getName());
Adam Lesinski468d3912014-07-22 10:01:08 -0700460 return false;
461 }
462
463 final int newMode = (Boolean) newValue ?
464 AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
Adam Lesinski366e7a22014-07-25 10:20:26 -0700465
466 // Check if we need to do any work.
467 if (pe.appOpMode != newMode) {
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700468 if (newMode != AppOpsManager.MODE_ALLOWED) {
469 // Turning off the setting has no warning.
470 setNewMode(pe, newMode);
471 return true;
472 }
473
474 // Turning on the setting has a Warning.
Adam Lesinski21dfa202014-09-09 11:34:55 -0700475 FragmentTransaction ft = getChildFragmentManager().beginTransaction();
476 Fragment prev = getChildFragmentManager().findFragmentByTag("warning");
477 if (prev != null) {
478 ft.remove(prev);
479 }
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000480 WarningDialogFragment.newInstance(pe).show(ft, "warning");
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700481 return false;
Adam Lesinski366e7a22014-07-25 10:20:26 -0700482 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700483 return true;
484 }
485
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700486 void setNewMode(PackageEntry pe, int newMode) {
487 mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS,
488 pe.packageInfo.applicationInfo.uid, pe.packageName, newMode);
489 pe.appOpMode = newMode;
490 }
491
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000492 void allowAccess(String packageName, int profileId) {
493 final PackageEntry entry = getPackageEntry(packageName, profileId);
Adam Lesinski21dfa202014-09-09 11:34:55 -0700494 if (entry == null) {
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000495 Log.w(TAG, "Unable to give access");
Adam Lesinski21dfa202014-09-09 11:34:55 -0700496 return;
497 }
498
499 setNewMode(entry, AppOpsManager.MODE_ALLOWED);
500 entry.preference.setChecked(true);
501 }
502
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000503 private PackageEntry getPackageEntry(String packageName, int profileId) {
504 ArrayMap<String, PackageEntry> entriesForProfile = mPackageEntryMap.get(profileId);
505 if (entriesForProfile == null) {
506 Log.w(TAG, "getPackageEntry fails for package " + packageName + " of user " +
507 profileId + ": user does not seem to be valid.");
508 return null;
509 }
510 final PackageEntry entry = entriesForProfile.get(packageName);
511 if (entry == null) {
512 Log.w(TAG, "getPackageEntry fails for package " + packageName + " of user " +
513 profileId + ": package does not exist.");
514 }
515 return entry;
516 }
517
Adam Lesinski468d3912014-07-22 10:01:08 -0700518 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
519 @Override
520 public void onPackageAdded(String packageName, int uid) {
521 updateInterestedApps();
522 }
523
524 @Override
525 public void onPackageRemoved(String packageName, int uid) {
526 updateInterestedApps();
527 }
528 };
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700529
Adam Lesinski21dfa202014-09-09 11:34:55 -0700530 public static class WarningDialogFragment extends DialogFragment
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700531 implements DialogInterface.OnClickListener {
Adam Lesinski21dfa202014-09-09 11:34:55 -0700532 private static final String ARG_PACKAGE_NAME = "package";
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000533 private static final String ARG_PROFILE_ID = "profileId";
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700534
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000535 public static WarningDialogFragment newInstance(PackageEntry pe) {
Adam Lesinski21dfa202014-09-09 11:34:55 -0700536 WarningDialogFragment dialog = new WarningDialogFragment();
537 Bundle args = new Bundle();
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000538 args.putString(ARG_PACKAGE_NAME, pe.packageName);
539 args.putInt(ARG_PROFILE_ID, pe.userHandle.getIdentifier());
Adam Lesinski21dfa202014-09-09 11:34:55 -0700540 dialog.setArguments(args);
541 return dialog;
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700542 }
543
544 @Override
545 public Dialog onCreateDialog(Bundle savedInstanceState) {
546 return new AlertDialog.Builder(getActivity())
547 .setTitle(R.string.allow_usage_access_title)
548 .setMessage(R.string.allow_usage_access_message)
549 .setIconAttribute(android.R.attr.alertDialogIcon)
550 .setNegativeButton(R.string.cancel, this)
Adam Lesinski1813c622014-08-27 19:00:30 -0700551 .setPositiveButton(android.R.string.ok, this)
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700552 .create();
553 }
554
555 @Override
556 public void onClick(DialogInterface dialog, int which) {
557 if (which == DialogInterface.BUTTON_POSITIVE) {
Adam Lesinski21dfa202014-09-09 11:34:55 -0700558 ((UsageAccessSettings) getParentFragment()).allowAccess(
Zoltan Szatmary-Ban2bfd97e2015-01-28 16:45:06 +0000559 getArguments().getString(ARG_PACKAGE_NAME),
560 getArguments().getInt(ARG_PROFILE_ID));
Adam Lesinskie01dfd12014-08-11 20:54:12 -0700561 } else {
562 dialog.cancel();
563 }
564 }
565 }
Adam Lesinski468d3912014-07-22 10:01:08 -0700566}