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