Home | History | Annotate | Download | only in handheld
      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 
     65     public static PermissionAppsFragment newInstance(String permissionName) {
     66         return setPermissionName(new PermissionAppsFragment(), permissionName);
     67     }
     68 
     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
     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
    113     public void onSaveInstanceState(Bundle outState) {
    114         super.onSaveInstanceState(outState);
    115 
    116         outState.putBoolean(SHOW_SYSTEM_KEY, mShowSystem);
    117     }
    118 
    119     @Override
    120     public void onResume() {
    121         super.onResume();
    122         mPermissionApps.refresh(true);
    123     }
    124 
    125     @Override
    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
    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 
    157     private void updateMenu() {
    158         mShowSystemMenu.setVisible(!mShowSystem);
    159         mHideSystemMenu.setVisible(mShowSystem);
    160     }
    161 
    162     @Override
    163     public void onViewCreated(View view, Bundle savedInstanceState) {
    164         super.onViewCreated(view, savedInstanceState);
    165         bindUi(this, mPermissionApps);
    166     }
    167 
    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 
    177     private void setOnPermissionsLoadedListener(Callback callback) {
    178         mOnPermissionsLoadedListener = callback;
    179     }
    180 
    181     @Override
    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
    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
    396     public void onPause() {
    397         super.onPause();
    398         logToggledGroups();
    399     }
    400 
    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 
    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
    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
    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
    450         public void onPermissionsLoaded(PermissionApps permissionApps) {
    451             setPreferenceScreen();
    452             mOuterFragment.setOnPermissionsLoadedListener(null);
    453         }
    454 
    455         private void setPreferenceScreen() {
    456             setPreferenceScreen(mOuterFragment.mExtraScreen);
    457             setLoading(false /* loading */, true /* animate */);
    458         }
    459     }
    460 }
    461