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