• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package com.android.packageinstaller.permission.ui.handheld;
17 
18 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
19 
20 import android.app.ActionBar;
21 import android.app.AlertDialog;
22 import android.app.Fragment;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.DialogInterface.OnClickListener;
26 import android.content.Intent;
27 import android.graphics.drawable.Drawable;
28 import android.os.Bundle;
29 import android.preference.Preference;
30 import android.preference.Preference.OnPreferenceClickListener;
31 import android.preference.PreferenceScreen;
32 import android.preference.SwitchPreference;
33 import android.util.ArrayMap;
34 import android.util.ArraySet;
35 import android.view.Menu;
36 import android.view.MenuInflater;
37 import android.view.MenuItem;
38 import android.view.View;
39 
40 import com.android.packageinstaller.DeviceUtils;
41 import com.android.packageinstaller.R;
42 import com.android.packageinstaller.permission.model.AppPermissionGroup;
43 import com.android.packageinstaller.permission.model.PermissionApps;
44 import com.android.packageinstaller.permission.model.PermissionApps.Callback;
45 import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp;
46 import com.android.packageinstaller.permission.utils.LocationUtils;
47 import com.android.packageinstaller.permission.utils.SafetyNetLogger;
48 import com.android.packageinstaller.permission.utils.Utils;
49 import com.android.settingslib.HelpUtils;
50 import com.android.settingslib.RestrictedLockUtils;
51 
52 import java.util.ArrayList;
53 import java.util.List;
54 
55 public final class PermissionAppsFragment extends PermissionsFrameFragment implements Callback,
56         Preference.OnPreferenceChangeListener {
57 
58     private static final int MENU_SHOW_SYSTEM = Menu.FIRST;
59     private static final int MENU_HIDE_SYSTEM = Menu.FIRST + 1;
60     private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem";
61 
62     private static final String SHOW_SYSTEM_KEY = PermissionAppsFragment.class.getName()
63             + KEY_SHOW_SYSTEM_PREFS;
64 
newInstance(String permissionName)65     public static PermissionAppsFragment newInstance(String permissionName) {
66         return setPermissionName(new PermissionAppsFragment(), permissionName);
67     }
68 
setPermissionName(T fragment, String permissionName)69     private static <T extends Fragment> T setPermissionName(T fragment, String permissionName) {
70         Bundle arguments = new Bundle();
71         arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName);
72         fragment.setArguments(arguments);
73         return fragment;
74     }
75 
76     private PermissionApps mPermissionApps;
77 
78     private PreferenceScreen mExtraScreen;
79 
80     private ArrayMap<String, AppPermissionGroup> mToggledGroups;
81     private ArraySet<String> mLauncherPkgs;
82     private boolean mHasConfirmedRevoke;
83 
84     private boolean mShowSystem;
85     private boolean mHasSystemApps;
86     private MenuItem mShowSystemMenu;
87     private MenuItem mHideSystemMenu;
88 
89     private Callback mOnPermissionsLoadedListener;
90 
91     @Override
onCreate(Bundle savedInstanceState)92     public void onCreate(Bundle savedInstanceState) {
93         super.onCreate(savedInstanceState);
94 
95         if (savedInstanceState != null) {
96             mShowSystem = savedInstanceState.getBoolean(SHOW_SYSTEM_KEY);
97         }
98 
99         setLoading(true /* loading */, false /* animate */);
100         setHasOptionsMenu(true);
101         final ActionBar ab = getActivity().getActionBar();
102         if (ab != null) {
103             ab.setDisplayHomeAsUpEnabled(true);
104         }
105         mLauncherPkgs = Utils.getLauncherPackages(getContext());
106 
107         String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
108         mPermissionApps = new PermissionApps(getActivity(), groupName, this);
109         mPermissionApps.refresh(true);
110     }
111 
112     @Override
onSaveInstanceState(Bundle outState)113     public void onSaveInstanceState(Bundle outState) {
114         super.onSaveInstanceState(outState);
115 
116         outState.putBoolean(SHOW_SYSTEM_KEY, mShowSystem);
117     }
118 
119     @Override
onResume()120     public void onResume() {
121         super.onResume();
122         mPermissionApps.refresh(true);
123     }
124 
125     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)126     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
127         if (mHasSystemApps) {
128             mShowSystemMenu = menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE,
129                     R.string.menu_show_system);
130             mHideSystemMenu = menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE,
131                     R.string.menu_hide_system);
132             updateMenu();
133         }
134 
135         HelpUtils.prepareHelpMenuItem(getActivity(), menu, R.string.help_app_permissions,
136                 getClass().getName());
137     }
138 
139     @Override
onOptionsItemSelected(MenuItem item)140     public boolean onOptionsItemSelected(MenuItem item) {
141         switch (item.getItemId()) {
142             case android.R.id.home:
143                 getActivity().finish();
144                 return true;
145             case MENU_SHOW_SYSTEM:
146             case MENU_HIDE_SYSTEM:
147                 mShowSystem = item.getItemId() == MENU_SHOW_SYSTEM;
148                 if (mPermissionApps.getApps() != null) {
149                     onPermissionsLoaded(mPermissionApps);
150                 }
151                 updateMenu();
152                 break;
153         }
154         return super.onOptionsItemSelected(item);
155     }
156 
updateMenu()157     private void updateMenu() {
158         mShowSystemMenu.setVisible(!mShowSystem);
159         mHideSystemMenu.setVisible(mShowSystem);
160     }
161 
162     @Override
onViewCreated(View view, Bundle savedInstanceState)163     public void onViewCreated(View view, Bundle savedInstanceState) {
164         super.onViewCreated(view, savedInstanceState);
165         bindUi(this, mPermissionApps);
166     }
167 
bindUi(Fragment fragment, PermissionApps permissionApps)168     private static void bindUi(Fragment fragment, PermissionApps permissionApps) {
169         final Drawable icon = permissionApps.getIcon();
170         final CharSequence label = permissionApps.getLabel();
171         final ActionBar ab = fragment.getActivity().getActionBar();
172         if (ab != null) {
173             ab.setTitle(fragment.getString(R.string.permission_title, label));
174         }
175     }
176 
setOnPermissionsLoadedListener(Callback callback)177     private void setOnPermissionsLoadedListener(Callback callback) {
178         mOnPermissionsLoadedListener = callback;
179     }
180 
181     @Override
onPermissionsLoaded(PermissionApps permissionApps)182     public void onPermissionsLoaded(PermissionApps permissionApps) {
183         Context context = getActivity();
184 
185         if (context == null) {
186             return;
187         }
188 
189         boolean isTelevision = DeviceUtils.isTelevision(context);
190         PreferenceScreen screen = getPreferenceScreen();
191         if (screen == null) {
192             screen = getPreferenceManager().createPreferenceScreen(getActivity());
193             setPreferenceScreen(screen);
194         }
195 
196         screen.setOrderingAsAdded(false);
197 
198         ArraySet<String> preferencesToRemove = new ArraySet<>();
199         for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) {
200             preferencesToRemove.add(screen.getPreference(i).getKey());
201         }
202         if (mExtraScreen != null) {
203             for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
204                 preferencesToRemove.add(mExtraScreen.getPreference(i).getKey());
205             }
206         }
207 
208         mHasSystemApps = false;
209         boolean menuOptionsInvalided = false;
210 
211         for (PermissionApp app : permissionApps.getApps()) {
212             if (!Utils.shouldShowPermission(app)) {
213                 continue;
214             }
215 
216             if (!app.getAppInfo().enabled) {
217                 continue;
218             }
219 
220             String key = app.getKey();
221             preferencesToRemove.remove(key);
222             Preference existingPref = screen.findPreference(key);
223             if (existingPref == null && mExtraScreen != null) {
224                 existingPref = mExtraScreen.findPreference(key);
225             }
226 
227             boolean isSystemApp = Utils.isSystem(app, mLauncherPkgs);
228 
229             if (isSystemApp && !menuOptionsInvalided) {
230                 mHasSystemApps = true;
231                 getActivity().invalidateOptionsMenu();
232                 menuOptionsInvalided = true;
233             }
234 
235             if (isSystemApp && !isTelevision && !mShowSystem) {
236                 if (existingPref != null) {
237                     screen.removePreference(existingPref);
238                 }
239                 continue;
240             }
241 
242             if (existingPref != null) {
243                 // If existing preference - only update its state.
244                 final boolean isPolicyFixed = app.isPolicyFixed();
245                 EnforcedAdmin enforcedAdmin = RestrictedLockUtils.getProfileOrDeviceOwner(
246                         getActivity(), app.getUserId());
247                 if (!isTelevision && (existingPref instanceof RestrictedSwitchPreference)) {
248                     ((RestrictedSwitchPreference) existingPref).setDisabledByAdmin(
249                             isPolicyFixed ? enforcedAdmin : null);
250                     existingPref.setSummary(isPolicyFixed ?
251                             getString(R.string.disabled_by_admin_summary_text) : null);
252                 } else {
253                     existingPref.setEnabled(!isPolicyFixed);
254                     existingPref.setSummary(isPolicyFixed ?
255                             getString(R.string.permission_summary_enforced_by_policy) : null);
256                 }
257                 existingPref.setPersistent(false);
258                 if (existingPref instanceof SwitchPreference) {
259                     ((SwitchPreference) existingPref)
260                             .setChecked(app.areRuntimePermissionsGranted());
261                 }
262                 continue;
263             }
264 
265             RestrictedSwitchPreference pref = new RestrictedSwitchPreference(context);
266             pref.setOnPreferenceChangeListener(this);
267             pref.setKey(app.getKey());
268             pref.setIcon(app.getIcon());
269             pref.setTitle(app.getLabel());
270             EnforcedAdmin enforcedAdmin = RestrictedLockUtils.getProfileOrDeviceOwner(
271                     getActivity(), app.getUserId());
272             if (app.isPolicyFixed()) {
273                 if (!isTelevision && enforcedAdmin != null) {
274                     pref.setDisabledByAdmin(enforcedAdmin);
275                     pref.setSummary(R.string.disabled_by_admin_summary_text);
276                 } else {
277                     pref.setEnabled(false);
278                     pref.setSummary(R.string.permission_summary_enforced_by_policy);
279                 }
280             }
281             pref.setPersistent(false);
282             pref.setChecked(app.areRuntimePermissionsGranted());
283 
284             if (isSystemApp && isTelevision) {
285                 if (mExtraScreen == null) {
286                     mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
287                 }
288                 mExtraScreen.addPreference(pref);
289             } else {
290                 screen.addPreference(pref);
291             }
292         }
293 
294         if (mExtraScreen != null) {
295             preferencesToRemove.remove(KEY_SHOW_SYSTEM_PREFS);
296             Preference pref = screen.findPreference(KEY_SHOW_SYSTEM_PREFS);
297 
298             if (pref == null) {
299                 pref = new Preference(context);
300                 pref.setKey(KEY_SHOW_SYSTEM_PREFS);
301                 pref.setIcon(Utils.applyTint(context, R.drawable.ic_toc,
302                         android.R.attr.colorControlNormal));
303                 pref.setTitle(R.string.preference_show_system_apps);
304                 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
305                     @Override
306                     public boolean onPreferenceClick(Preference preference) {
307                         SystemAppsFragment frag = new SystemAppsFragment();
308                         setPermissionName(frag, getArguments().getString(Intent.EXTRA_PERMISSION_NAME));
309                         frag.setTargetFragment(PermissionAppsFragment.this, 0);
310                         getFragmentManager().beginTransaction()
311                             .replace(android.R.id.content, frag)
312                             .addToBackStack("SystemApps")
313                             .commit();
314                         return true;
315                     }
316                 });
317                 screen.addPreference(pref);
318             }
319 
320             int grantedCount = 0;
321             for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
322                 if (((SwitchPreference) mExtraScreen.getPreference(i)).isChecked()) {
323                     grantedCount++;
324                 }
325             }
326             pref.setSummary(getString(R.string.app_permissions_group_summary,
327                     grantedCount, mExtraScreen.getPreferenceCount()));
328         }
329 
330         for (String key : preferencesToRemove) {
331             Preference pref = screen.findPreference(key);
332             if (pref != null) {
333                 screen.removePreference(pref);
334             } else if (mExtraScreen != null) {
335                 pref = mExtraScreen.findPreference(key);
336                 if (pref != null) {
337                     mExtraScreen.removePreference(pref);
338                 }
339             }
340         }
341 
342         setLoading(false /* loading */, true /* animate */);
343 
344         if (mOnPermissionsLoadedListener != null) {
345             mOnPermissionsLoadedListener.onPermissionsLoaded(permissionApps);
346         }
347     }
348 
349     @Override
onPreferenceChange(final Preference preference, Object newValue)350     public boolean onPreferenceChange(final Preference preference, Object newValue) {
351         String pkg = preference.getKey();
352         final PermissionApp app = mPermissionApps.getApp(pkg);
353 
354         if (app == null) {
355             return false;
356         }
357 
358         addToggledGroup(app.getPackageName(), app.getPermissionGroup());
359 
360         if (LocationUtils.isLocationGroupAndProvider(mPermissionApps.getGroupName(),
361                 app.getPackageName())) {
362             LocationUtils.showLocationDialog(getContext(), app.getLabel());
363             return false;
364         }
365         if (newValue == Boolean.TRUE) {
366             app.grantRuntimePermissions();
367         } else {
368             final boolean grantedByDefault = app.hasGrantedByDefaultPermissions();
369             if (grantedByDefault || (!app.doesSupportRuntimePermissions()
370                     && !mHasConfirmedRevoke)) {
371                 new AlertDialog.Builder(getContext())
372                         .setMessage(grantedByDefault ? R.string.system_warning
373                                 : R.string.old_sdk_deny_warning)
374                         .setNegativeButton(R.string.cancel, null)
375                         .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
376                                 new OnClickListener() {
377                             @Override
378                             public void onClick(DialogInterface dialog, int which) {
379                                 ((SwitchPreference) preference).setChecked(false);
380                                 app.revokeRuntimePermissions();
381                                 if (!grantedByDefault) {
382                                     mHasConfirmedRevoke = true;
383                                 }
384                             }
385                         })
386                         .show();
387                 return false;
388             } else {
389                 app.revokeRuntimePermissions();
390             }
391         }
392         return true;
393     }
394 
395     @Override
onPause()396     public void onPause() {
397         super.onPause();
398         logToggledGroups();
399     }
400 
addToggledGroup(String packageName, AppPermissionGroup group)401     private void addToggledGroup(String packageName, AppPermissionGroup group) {
402         if (mToggledGroups == null) {
403             mToggledGroups = new ArrayMap<>();
404         }
405         // Double toggle is back to initial state.
406         if (mToggledGroups.containsKey(packageName)) {
407             mToggledGroups.remove(packageName);
408         } else {
409             mToggledGroups.put(packageName, group);
410         }
411     }
412 
logToggledGroups()413     private void logToggledGroups() {
414         if (mToggledGroups != null) {
415             final int groupCount = mToggledGroups.size();
416             for (int i = 0; i < groupCount; i++) {
417                 String packageName = mToggledGroups.keyAt(i);
418                 List<AppPermissionGroup> groups = new ArrayList<>();
419                 groups.add(mToggledGroups.valueAt(i));
420                 SafetyNetLogger.logPermissionsToggled(packageName, groups);
421             }
422             mToggledGroups = null;
423         }
424     }
425 
426     public static class SystemAppsFragment extends PermissionsFrameFragment implements Callback {
427         PermissionAppsFragment mOuterFragment;
428 
429         @Override
onCreate(Bundle savedInstanceState)430         public void onCreate(Bundle savedInstanceState) {
431             mOuterFragment = (PermissionAppsFragment) getTargetFragment();
432             setLoading(true /* loading */, false /* animate */);
433             super.onCreate(savedInstanceState);
434             if (mOuterFragment.mExtraScreen != null) {
435                 setPreferenceScreen();
436             } else {
437                 mOuterFragment.setOnPermissionsLoadedListener(this);
438             }
439         }
440 
441         @Override
onViewCreated(View view, Bundle savedInstanceState)442         public void onViewCreated(View view, Bundle savedInstanceState) {
443             super.onViewCreated(view, savedInstanceState);
444             String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
445             PermissionApps permissionApps = new PermissionApps(getActivity(), groupName, null);
446             bindUi(this, permissionApps);
447         }
448 
449         @Override
onPermissionsLoaded(PermissionApps permissionApps)450         public void onPermissionsLoaded(PermissionApps permissionApps) {
451             setPreferenceScreen();
452             mOuterFragment.setOnPermissionsLoadedListener(null);
453         }
454 
setPreferenceScreen()455         private void setPreferenceScreen() {
456             setPreferenceScreen(mOuterFragment.mExtraScreen);
457             setLoading(false /* loading */, true /* animate */);
458         }
459     }
460 }
461