1468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski/* 2468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * Copyright (C) 2014 The Android Open Source Project 3468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * 4468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License"); 5468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * you may not use this file except in compliance with the License. 6468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * You may obtain a copy of the License at 7468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * 8468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * http://www.apache.org/licenses/LICENSE-2.0 9468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * 10468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * Unless required by applicable law or agreed to in writing, software 11468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS, 12468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * See the License for the specific language governing permissions and 14468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * limitations under the License. 15468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski */ 16468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 17468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskipackage com.android.settings; 18468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 19468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport com.android.internal.content.PackageMonitor; 20468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 21468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.Manifest; 22468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.app.ActivityThread; 23e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinskiimport android.app.AlertDialog; 24468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.app.AppOpsManager; 25e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinskiimport android.app.Dialog; 26e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinskiimport android.app.DialogFragment; 2721dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinskiimport android.app.Fragment; 2821dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinskiimport android.app.FragmentTransaction; 29468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.content.Context; 30e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinskiimport android.content.DialogInterface; 31468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.content.pm.IPackageManager; 32468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.content.pm.PackageInfo; 33468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.content.pm.PackageManager; 34468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.os.AsyncTask; 35468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.os.Bundle; 36468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.os.Looper; 37468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.os.RemoteException; 38468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.preference.Preference; 391813c6297fd55d5640aad3eb614f0acd44c166d8Adam Lesinskiimport android.preference.PreferenceScreen; 40468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.preference.SwitchPreference; 41468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.util.ArrayMap; 42468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport android.util.Log; 43468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 44468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskiimport java.util.List; 45468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 46468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinskipublic class UsageAccessSettings extends SettingsPreferenceFragment implements 47468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski Preference.OnPreferenceChangeListener { 48468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 49468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private static final String TAG = "UsageAccessSettings"; 50468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 51468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private static final String[] PM_USAGE_STATS_PERMISSION = new String[] { 52468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski Manifest.permission.PACKAGE_USAGE_STATS 53468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski }; 54468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 55468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private static final int[] APP_OPS_OP_CODES = new int[] { 56468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski AppOpsManager.OP_GET_USAGE_STATS 57468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski }; 58468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 59468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private static class PackageEntry { 60468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski public PackageEntry(String packageName) { 61468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski this.packageName = packageName; 62468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski this.appOpMode = AppOpsManager.MODE_DEFAULT; 63468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 64468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 65468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final String packageName; 66468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski PackageInfo packageInfo; 67468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski boolean permissionGranted; 68468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski int appOpMode; 69468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 70468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski SwitchPreference preference; 71468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 72468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 73468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski /** 74468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * Fetches the list of Apps that are requesting access to the UsageStats API and updates 75468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski * the PreferenceScreen with the results when complete. 76468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski */ 77468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private class AppsRequestingAccessFetcher extends 78468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski AsyncTask<Void, Void, ArrayMap<String, PackageEntry>> { 79468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 80468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private final Context mContext; 81468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private final PackageManager mPackageManager; 82468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private final IPackageManager mIPackageManager; 83468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 84468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski public AppsRequestingAccessFetcher(Context context) { 85468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mContext = context; 86468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mPackageManager = context.getPackageManager(); 87468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mIPackageManager = ActivityThread.getPackageManager(); 88468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 89468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 90468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski @Override 91468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski protected ArrayMap<String, PackageEntry> doInBackground(Void... params) { 92468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final String[] packages; 93468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski try { 94468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski packages = mIPackageManager.getAppOpPermissionPackages( 95468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski Manifest.permission.PACKAGE_USAGE_STATS); 96468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } catch (RemoteException e) { 97468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting " 98468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski + Manifest.permission.PACKAGE_USAGE_STATS); 99468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski return null; 100468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 101468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 102468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (packages == null) { 103468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // No packages are requesting permission to use the UsageStats API. 104468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski return null; 105468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 106468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 107468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski ArrayMap<String, PackageEntry> entries = new ArrayMap<>(); 108468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski for (final String packageName : packages) { 109468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (!shouldIgnorePackage(packageName)) { 110468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski entries.put(packageName, new PackageEntry(packageName)); 111468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 112468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 113468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 114468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // Load the packages that have been granted the PACKAGE_USAGE_STATS permission. 115468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final List<PackageInfo> packageInfos = mPackageManager.getPackagesHoldingPermissions( 116468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski PM_USAGE_STATS_PERMISSION, 0); 117468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0; 118468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski for (int i = 0; i < packageInfoCount; i++) { 119468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final PackageInfo packageInfo = packageInfos.get(i); 120468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final PackageEntry pe = entries.get(packageInfo.packageName); 121468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (pe != null) { 122468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski pe.packageInfo = packageInfo; 123468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski pe.permissionGranted = true; 124468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 125468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 126468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 127468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // Load the remaining packages that have requested but don't have the 128468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // PACKAGE_USAGE_STATS permission. 129468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski int packageCount = entries.size(); 130468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski for (int i = 0; i < packageCount; i++) { 131468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final PackageEntry pe = entries.valueAt(i); 132468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (pe.packageInfo == null) { 133468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski try { 134468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski pe.packageInfo = mPackageManager.getPackageInfo(pe.packageName, 0); 135468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } catch (PackageManager.NameNotFoundException e) { 136468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // This package doesn't exist. This may occur when an app is uninstalled for 137468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // one user, but it is not removed from the system. 138468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski entries.removeAt(i); 139468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski i--; 140468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski packageCount--; 141468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 142468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 143468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 144468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 145468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // Find out which packages have been granted permission from AppOps. 146468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps( 147468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski APP_OPS_OP_CODES); 148468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final int packageOpsCount = packageOps != null ? packageOps.size() : 0; 149468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski for (int i = 0; i < packageOpsCount; i++) { 150468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final AppOpsManager.PackageOps packageOp = packageOps.get(i); 151468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final PackageEntry pe = entries.get(packageOp.getPackageName()); 152468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (pe == null) { 153468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName() 154468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski + " but package doesn't exist or did not request UsageStats access"); 155468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski continue; 156468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 157468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 158366e7a20c4d716c3d260faef929f0e8d6be267fbAdam Lesinski if (packageOp.getUid() != pe.packageInfo.applicationInfo.uid) { 159366e7a20c4d716c3d260faef929f0e8d6be267fbAdam Lesinski // This AppOp does not belong to this user. 160366e7a20c4d716c3d260faef929f0e8d6be267fbAdam Lesinski continue; 161366e7a20c4d716c3d260faef929f0e8d6be267fbAdam Lesinski } 162366e7a20c4d716c3d260faef929f0e8d6be267fbAdam Lesinski 163468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (packageOp.getOps().size() < 1) { 164468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski Log.w(TAG, "No AppOps permission exists for package " 165468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski + packageOp.getPackageName()); 166468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski continue; 167468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 168468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 169468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski pe.appOpMode = packageOp.getOps().get(0).getMode(); 170468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 171468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 172468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski return entries; 173468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 174468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 175468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski @Override 176468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski protected void onPostExecute(ArrayMap<String, PackageEntry> newEntries) { 177468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mLastFetcherTask = null; 178468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 179468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (getActivity() == null) { 180468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // We must have finished the Activity while we were processing in the background. 181468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski return; 182468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 183468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 184468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (newEntries == null) { 185468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mPackageEntryMap.clear(); 1861813c6297fd55d5640aad3eb614f0acd44c166d8Adam Lesinski mPreferenceScreen.removeAll(); 187468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski return; 188468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 189468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 190468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // Find the deleted entries and remove them from the PreferenceScreen. 191468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final int oldPackageCount = mPackageEntryMap.size(); 192468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski for (int i = 0; i < oldPackageCount; i++) { 193468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final PackageEntry oldPackageEntry = mPackageEntryMap.valueAt(i); 194468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final PackageEntry newPackageEntry = newEntries.get(oldPackageEntry.packageName); 195468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (newPackageEntry == null) { 196468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // This package has been removed. 1971813c6297fd55d5640aad3eb614f0acd44c166d8Adam Lesinski mPreferenceScreen.removePreference(oldPackageEntry.preference); 198468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } else { 199468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // This package already exists in the preference hierarchy, so reuse that 200468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // Preference. 201468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski newPackageEntry.preference = oldPackageEntry.preference; 202468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 203468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 204468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 205468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // Now add new packages to the PreferenceScreen. 206468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final int packageCount = newEntries.size(); 207468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski for (int i = 0; i < packageCount; i++) { 208468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final PackageEntry packageEntry = newEntries.valueAt(i); 209468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (packageEntry.preference == null) { 210468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski packageEntry.preference = new SwitchPreference(mContext); 211468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski packageEntry.preference.setPersistent(false); 212468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski packageEntry.preference.setOnPreferenceChangeListener(UsageAccessSettings.this); 2131813c6297fd55d5640aad3eb614f0acd44c166d8Adam Lesinski mPreferenceScreen.addPreference(packageEntry.preference); 214468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 215468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski updatePreference(packageEntry); 216468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 217468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 218468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mPackageEntryMap.clear(); 219468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mPackageEntryMap = newEntries; 220468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 221468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 222468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private void updatePreference(PackageEntry pe) { 223468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski pe.preference.setIcon(pe.packageInfo.applicationInfo.loadIcon(mPackageManager)); 224468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski pe.preference.setTitle(pe.packageInfo.applicationInfo.loadLabel(mPackageManager)); 225468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski pe.preference.setKey(pe.packageName); 226468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 227468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski boolean check = false; 228468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (pe.appOpMode == AppOpsManager.MODE_ALLOWED) { 229468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski check = true; 230468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } else if (pe.appOpMode == AppOpsManager.MODE_DEFAULT) { 231468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // If the default AppOps mode is set, then fall back to 232468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // whether the app has been granted permission by PackageManager. 233468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski check = pe.permissionGranted; 234468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 235468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 236468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (check != pe.preference.isChecked()) { 237468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski pe.preference.setChecked(check); 238468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 239468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 240468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 241468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 242e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski static boolean shouldIgnorePackage(String packageName) { 243468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski return packageName.equals("android") || packageName.equals("com.android.settings"); 244468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 245468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 246468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private AppsRequestingAccessFetcher mLastFetcherTask; 247e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski ArrayMap<String, PackageEntry> mPackageEntryMap = new ArrayMap<>(); 248e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski AppOpsManager mAppOpsManager; 2491813c6297fd55d5640aad3eb614f0acd44c166d8Adam Lesinski PreferenceScreen mPreferenceScreen; 250468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 251468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski @Override 252468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski public void onCreate(Bundle icicle) { 253468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski super.onCreate(icicle); 254468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 255468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski addPreferencesFromResource(R.xml.usage_access_settings); 2561813c6297fd55d5640aad3eb614f0acd44c166d8Adam Lesinski mPreferenceScreen = getPreferenceScreen(); 2571813c6297fd55d5640aad3eb614f0acd44c166d8Adam Lesinski mPreferenceScreen.setOrderingAsAdded(false); 258468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); 259468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 260468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 261468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski @Override 262468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski public void onResume() { 263468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski super.onResume(); 264468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 265468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski updateInterestedApps(); 266468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mPackageMonitor.register(getActivity(), Looper.getMainLooper(), false); 267468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 268468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 269468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski @Override 270468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski public void onPause() { 271468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski super.onPause(); 272468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 273468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mPackageMonitor.unregister(); 274468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (mLastFetcherTask != null) { 275468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mLastFetcherTask.cancel(true); 276468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mLastFetcherTask = null; 277468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 278468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 279468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 280468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private void updateInterestedApps() { 281468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (mLastFetcherTask != null) { 282468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // Canceling can only fail for some obscure reason since mLastFetcherTask would be 283468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // null if the task has already completed. So we ignore the result of cancel and 284468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // spawn a new task to get fresh data. AsyncTask executes tasks serially anyways, 285468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski // so we are safe from running two tasks at the same time. 286468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mLastFetcherTask.cancel(true); 287468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 288468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 289468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mLastFetcherTask = new AppsRequestingAccessFetcher(getActivity()); 290468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski mLastFetcherTask.execute(); 291468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 292468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 293468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski @Override 294468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski public boolean onPreferenceChange(Preference preference, Object newValue) { 295468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final String packageName = preference.getKey(); 296468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final PackageEntry pe = mPackageEntryMap.get(packageName); 297468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (pe == null) { 298468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski Log.w(TAG, "Preference change event for package " + packageName 299468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski + " but that package is no longer valid."); 300468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski return false; 301468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 302468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 303468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski if (!(newValue instanceof Boolean)) { 304468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski Log.w(TAG, "Preference change event for package " + packageName 305468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski + " had non boolean value of type " + newValue.getClass().getName()); 306468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski return false; 307468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 308468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 309468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski final int newMode = (Boolean) newValue ? 310468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED; 311366e7a20c4d716c3d260faef929f0e8d6be267fbAdam Lesinski 312366e7a20c4d716c3d260faef929f0e8d6be267fbAdam Lesinski // Check if we need to do any work. 313366e7a20c4d716c3d260faef929f0e8d6be267fbAdam Lesinski if (pe.appOpMode != newMode) { 314e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski if (newMode != AppOpsManager.MODE_ALLOWED) { 315e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski // Turning off the setting has no warning. 316e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski setNewMode(pe, newMode); 317e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski return true; 318e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski } 319e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski 320e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski // Turning on the setting has a Warning. 32121dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski FragmentTransaction ft = getChildFragmentManager().beginTransaction(); 32221dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski Fragment prev = getChildFragmentManager().findFragmentByTag("warning"); 32321dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski if (prev != null) { 32421dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski ft.remove(prev); 32521dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski } 32621dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski WarningDialogFragment.newInstance(pe.packageName).show(ft, "warning"); 327e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski return false; 328366e7a20c4d716c3d260faef929f0e8d6be267fbAdam Lesinski } 329468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski return true; 330468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 331468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 332e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski void setNewMode(PackageEntry pe, int newMode) { 333e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS, 334e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski pe.packageInfo.applicationInfo.uid, pe.packageName, newMode); 335e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski pe.appOpMode = newMode; 336e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski } 337e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski 33821dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski void allowAccess(String packageName) { 33921dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski final PackageEntry entry = mPackageEntryMap.get(packageName); 34021dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski if (entry == null) { 34121dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski Log.w(TAG, "Unable to give access to package " + packageName + ": it does not exist."); 34221dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski return; 34321dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski } 34421dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski 34521dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski setNewMode(entry, AppOpsManager.MODE_ALLOWED); 34621dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski entry.preference.setChecked(true); 34721dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski } 34821dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski 349468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski private final PackageMonitor mPackageMonitor = new PackageMonitor() { 350468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski @Override 351468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski public void onPackageAdded(String packageName, int uid) { 352468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski updateInterestedApps(); 353468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 354468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski 355468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski @Override 356468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski public void onPackageRemoved(String packageName, int uid) { 357468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski updateInterestedApps(); 358468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski } 359468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski }; 360e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski 36121dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski public static class WarningDialogFragment extends DialogFragment 362e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski implements DialogInterface.OnClickListener { 36321dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski private static final String ARG_PACKAGE_NAME = "package"; 36421dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski 36521dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski public static WarningDialogFragment newInstance(String packageName) { 36621dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski WarningDialogFragment dialog = new WarningDialogFragment(); 36721dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski Bundle args = new Bundle(); 36821dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski args.putString(ARG_PACKAGE_NAME, packageName); 36921dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski dialog.setArguments(args); 37021dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski return dialog; 371e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski } 372e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski 373e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski @Override 374e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski public Dialog onCreateDialog(Bundle savedInstanceState) { 375e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski return new AlertDialog.Builder(getActivity()) 376e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski .setTitle(R.string.allow_usage_access_title) 377e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski .setMessage(R.string.allow_usage_access_message) 378e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski .setIconAttribute(android.R.attr.alertDialogIcon) 379e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski .setNegativeButton(R.string.cancel, this) 3801813c6297fd55d5640aad3eb614f0acd44c166d8Adam Lesinski .setPositiveButton(android.R.string.ok, this) 381e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski .create(); 382e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski } 383e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski 384e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski @Override 385e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski public void onClick(DialogInterface dialog, int which) { 386e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski if (which == DialogInterface.BUTTON_POSITIVE) { 38721dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski ((UsageAccessSettings) getParentFragment()).allowAccess( 38821dfa201f8799a99ba216e8c4d57bf43719aa5b3Adam Lesinski getArguments().getString(ARG_PACKAGE_NAME)); 389e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski } else { 390e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski dialog.cancel(); 391e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski } 392e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski } 393e01dfd1a82b7e313e885aaa8f4de61233157c8e6Adam Lesinski } 394468d39160fd417cf23cba3d592acb349d3f59c55Adam Lesinski} 395