• 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         ArraySet<String> preferencesToRemove = new ArraySet<>();
181         for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) {
182             preferencesToRemove.add(screen.getPreference(i).getKey());
183         }
184         if (mExtraScreen != null) {
185             for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
186                 preferencesToRemove.add(mExtraScreen.getPreference(i).getKey());
187             }
188         }
189 
190         mHasSystemApps = false;
191         boolean menuOptionsInvalided = false;
192 
193         for (PermissionApp app : permissionApps.getApps()) {
194             if (!Utils.shouldShowPermission(app)) {
195                 continue;
196             }
197 
198             String key = app.getKey();
199             preferencesToRemove.remove(key);
200             Preference existingPref = screen.findPreference(key);
201             if (existingPref == null && mExtraScreen != null) {
202                 existingPref = mExtraScreen.findPreference(key);
203             }
204 
205             boolean isSystemApp = Utils.isSystem(app, mLauncherPkgs);
206 
207             if (isSystemApp && !menuOptionsInvalided) {
208                 mHasSystemApps = true;
209                 getActivity().invalidateOptionsMenu();
210                 menuOptionsInvalided = true;
211             }
212 
213             if (isSystemApp && !isTelevision && !mShowSystem) {
214                 if (existingPref != null) {
215                     screen.removePreference(existingPref);
216                 }
217                 continue;
218             }
219 
220             if (existingPref != null) {
221                 // If existing preference - only update its state.
222                 final boolean isPolicyFixed = app.isPolicyFixed();
223                 EnforcedAdmin enforcedAdmin = RestrictedLockUtils.getProfileOrDeviceOwner(
224                         getActivity(), app.getUserId());
225                 if (!isTelevision && (existingPref instanceof RestrictedSwitchPreference)) {
226                     ((RestrictedSwitchPreference) existingPref).setDisabledByAdmin(
227                             isPolicyFixed ? enforcedAdmin : null);
228                     existingPref.setSummary(isPolicyFixed ?
229                             getString(R.string.disabled_by_admin_summary_text) : null);
230                 } else {
231                     existingPref.setEnabled(!isPolicyFixed);
232                     existingPref.setSummary(isPolicyFixed ?
233                             getString(R.string.permission_summary_enforced_by_policy) : null);
234                 }
235                 existingPref.setPersistent(false);
236                 if (existingPref instanceof SwitchPreference) {
237                     ((SwitchPreference) existingPref)
238                             .setChecked(app.areRuntimePermissionsGranted());
239                 }
240                 continue;
241             }
242 
243             RestrictedSwitchPreference pref = new RestrictedSwitchPreference(context);
244             pref.setOnPreferenceChangeListener(this);
245             pref.setKey(app.getKey());
246             pref.setIcon(app.getIcon());
247             pref.setTitle(app.getLabel());
248             EnforcedAdmin enforcedAdmin = RestrictedLockUtils.getProfileOrDeviceOwner(
249                     getActivity(), app.getUserId());
250             if (app.isPolicyFixed()) {
251                 if (!isTelevision && enforcedAdmin != null) {
252                     pref.setDisabledByAdmin(enforcedAdmin);
253                     pref.setSummary(R.string.disabled_by_admin_summary_text);
254                 } else {
255                     pref.setEnabled(false);
256                     pref.setSummary(R.string.permission_summary_enforced_by_policy);
257                 }
258             }
259             pref.setPersistent(false);
260             pref.setChecked(app.areRuntimePermissionsGranted());
261 
262             if (isSystemApp && isTelevision) {
263                 if (mExtraScreen == null) {
264                     mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
265                 }
266                 mExtraScreen.addPreference(pref);
267             } else {
268                 screen.addPreference(pref);
269             }
270         }
271 
272         if (mExtraScreen != null) {
273             preferencesToRemove.remove(KEY_SHOW_SYSTEM_PREFS);
274             Preference pref = screen.findPreference(KEY_SHOW_SYSTEM_PREFS);
275 
276             if (pref == null) {
277                 pref = new Preference(context);
278                 pref.setKey(KEY_SHOW_SYSTEM_PREFS);
279                 pref.setIcon(Utils.applyTint(context, R.drawable.ic_toc,
280                         android.R.attr.colorControlNormal));
281                 pref.setTitle(R.string.preference_show_system_apps);
282                 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
283                     @Override
284                     public boolean onPreferenceClick(Preference preference) {
285                         SystemAppsFragment frag = new SystemAppsFragment();
286                         setPermissionName(frag, getArguments().getString(Intent.EXTRA_PERMISSION_NAME));
287                         frag.setTargetFragment(PermissionAppsFragment.this, 0);
288                         getFragmentManager().beginTransaction()
289                             .replace(android.R.id.content, frag)
290                             .addToBackStack("SystemApps")
291                             .commit();
292                         return true;
293                     }
294                 });
295                 screen.addPreference(pref);
296             }
297 
298             int grantedCount = 0;
299             for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
300                 if (((SwitchPreference) mExtraScreen.getPreference(i)).isChecked()) {
301                     grantedCount++;
302                 }
303             }
304             pref.setSummary(getString(R.string.app_permissions_group_summary,
305                     grantedCount, mExtraScreen.getPreferenceCount()));
306         }
307 
308         for (String key : preferencesToRemove) {
309             Preference pref = screen.findPreference(key);
310             if (pref != null) {
311                 screen.removePreference(pref);
312             } else if (mExtraScreen != null) {
313                 pref = mExtraScreen.findPreference(key);
314                 if (pref != null) {
315                     mExtraScreen.removePreference(pref);
316                 }
317             }
318         }
319 
320         setLoading(false /* loading */, true /* animate */);
321 
322         if (mOnPermissionsLoadedListener != null) {
323             mOnPermissionsLoadedListener.onPermissionsLoaded(permissionApps);
324         }
325     }
326 
327     @Override
onPreferenceChange(final Preference preference, Object newValue)328     public boolean onPreferenceChange(final Preference preference, Object newValue) {
329         String pkg = preference.getKey();
330         final PermissionApp app = mPermissionApps.getApp(pkg);
331 
332         if (app == null) {
333             return false;
334         }
335 
336         addToggledGroup(app.getPackageName(), app.getPermissionGroup());
337 
338         if (LocationUtils.isLocationGroupAndProvider(mPermissionApps.getGroupName(),
339                 app.getPackageName())) {
340             LocationUtils.showLocationDialog(getContext(), app.getLabel());
341             return false;
342         }
343         if (newValue == Boolean.TRUE) {
344             app.grantRuntimePermissions();
345         } else {
346             final boolean grantedByDefault = app.hasGrantedByDefaultPermissions();
347             if (grantedByDefault || (!app.hasRuntimePermissions() && !mHasConfirmedRevoke)) {
348                 new AlertDialog.Builder(getContext())
349                         .setMessage(grantedByDefault ? R.string.system_warning
350                                 : R.string.old_sdk_deny_warning)
351                         .setNegativeButton(R.string.cancel, null)
352                         .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
353                                 new OnClickListener() {
354                             @Override
355                             public void onClick(DialogInterface dialog, int which) {
356                                 ((SwitchPreference) preference).setChecked(false);
357                                 app.revokeRuntimePermissions();
358                                 if (!grantedByDefault) {
359                                     mHasConfirmedRevoke = true;
360                                 }
361                             }
362                         })
363                         .show();
364                 return false;
365             } else {
366                 app.revokeRuntimePermissions();
367             }
368         }
369         return true;
370     }
371 
372     @Override
onPause()373     public void onPause() {
374         super.onPause();
375         logToggledGroups();
376     }
377 
addToggledGroup(String packageName, AppPermissionGroup group)378     private void addToggledGroup(String packageName, AppPermissionGroup group) {
379         if (mToggledGroups == null) {
380             mToggledGroups = new ArrayMap<>();
381         }
382         // Double toggle is back to initial state.
383         if (mToggledGroups.containsKey(packageName)) {
384             mToggledGroups.remove(packageName);
385         } else {
386             mToggledGroups.put(packageName, group);
387         }
388     }
389 
logToggledGroups()390     private void logToggledGroups() {
391         if (mToggledGroups != null) {
392             final int groupCount = mToggledGroups.size();
393             for (int i = 0; i < groupCount; i++) {
394                 String packageName = mToggledGroups.keyAt(i);
395                 List<AppPermissionGroup> groups = new ArrayList<>();
396                 groups.add(mToggledGroups.valueAt(i));
397                 SafetyNetLogger.logPermissionsToggled(packageName, groups);
398             }
399             mToggledGroups = null;
400         }
401     }
402 
403     public static class SystemAppsFragment extends PermissionsFrameFragment implements Callback {
404         PermissionAppsFragment mOuterFragment;
405 
406         @Override
onCreate(Bundle savedInstanceState)407         public void onCreate(Bundle savedInstanceState) {
408             mOuterFragment = (PermissionAppsFragment) getTargetFragment();
409             setLoading(true /* loading */, false /* animate */);
410             super.onCreate(savedInstanceState);
411             if (mOuterFragment.mExtraScreen != null) {
412                 setPreferenceScreen();
413             } else {
414                 mOuterFragment.setOnPermissionsLoadedListener(this);
415             }
416         }
417 
418         @Override
onViewCreated(View view, Bundle savedInstanceState)419         public void onViewCreated(View view, Bundle savedInstanceState) {
420             super.onViewCreated(view, savedInstanceState);
421             String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
422             PermissionApps permissionApps = new PermissionApps(getActivity(), groupName, null);
423             bindUi(this, permissionApps);
424         }
425 
426         @Override
onPermissionsLoaded(PermissionApps permissionApps)427         public void onPermissionsLoaded(PermissionApps permissionApps) {
428             setPreferenceScreen();
429             mOuterFragment.setOnPermissionsLoadedListener(null);
430         }
431 
setPreferenceScreen()432         private void setPreferenceScreen() {
433             setPreferenceScreen(mOuterFragment.mExtraScreen);
434             setLoading(false /* loading */, true /* animate */);
435         }
436     }
437 }
438