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