• 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 
17 package com.android.packageinstaller.permission.ui.handheld;
18 
19 import android.app.ActionBar;
20 import android.app.Activity;
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.content.pm.ApplicationInfo;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageManager;
30 import android.graphics.drawable.Drawable;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.preference.Preference;
34 import android.preference.Preference.OnPreferenceChangeListener;
35 import android.preference.Preference.OnPreferenceClickListener;
36 import android.preference.PreferenceScreen;
37 import android.preference.SwitchPreference;
38 import android.provider.Settings;
39 import android.util.Log;
40 import android.view.Menu;
41 import android.view.MenuInflater;
42 import android.view.MenuItem;
43 import android.view.View;
44 import android.view.ViewGroup;
45 import android.widget.ImageView;
46 import android.widget.TextView;
47 import android.widget.Toast;
48 import com.android.packageinstaller.R;
49 import com.android.packageinstaller.permission.model.AppPermissionGroup;
50 import com.android.packageinstaller.permission.model.AppPermissions;
51 import com.android.packageinstaller.permission.utils.LocationUtils;
52 import com.android.packageinstaller.permission.utils.SafetyNetLogger;
53 import com.android.packageinstaller.permission.utils.Utils;
54 import com.android.settingslib.HelpUtils;
55 import com.android.settingslib.RestrictedLockUtils;
56 
57 import java.util.ArrayList;
58 import java.util.List;
59 
60 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
61 
62 public final class AppPermissionsFragment extends SettingsWithHeader
63         implements OnPreferenceChangeListener {
64 
65     private static final String LOG_TAG = "ManagePermsFragment";
66 
67     static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
68 
69     private static final int MENU_ALL_PERMS = 0;
70 
71     private List<AppPermissionGroup> mToggledGroups;
72     private AppPermissions mAppPermissions;
73     private PreferenceScreen mExtraScreen;
74 
75     private boolean mHasConfirmedRevoke;
76 
newInstance(String packageName)77     public static AppPermissionsFragment newInstance(String packageName) {
78         return setPackageName(new AppPermissionsFragment(), packageName);
79     }
80 
setPackageName(T fragment, String packageName)81     private static <T extends Fragment> T setPackageName(T fragment, String packageName) {
82         Bundle arguments = new Bundle();
83         arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
84         fragment.setArguments(arguments);
85         return fragment;
86     }
87 
88     @Override
onCreate(Bundle savedInstanceState)89     public void onCreate(Bundle savedInstanceState) {
90         super.onCreate(savedInstanceState);
91         setLoading(true /* loading */, false /* animate */);
92         setHasOptionsMenu(true);
93         final ActionBar ab = getActivity().getActionBar();
94         if (ab != null) {
95             ab.setDisplayHomeAsUpEnabled(true);
96         }
97 
98         String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
99         Activity activity = getActivity();
100         PackageInfo packageInfo = getPackageInfo(activity, packageName);
101         if (packageInfo == null) {
102             Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
103             activity.finish();
104             return;
105         }
106 
107         mAppPermissions = new AppPermissions(activity, packageInfo, null, true, new Runnable() {
108             @Override
109             public void run() {
110                 getActivity().finish();
111             }
112         });
113         loadPreferences();
114     }
115 
116     @Override
onResume()117     public void onResume() {
118         super.onResume();
119         mAppPermissions.refresh();
120         loadPreferences();
121         setPreferencesCheckedState();
122     }
123 
124     @Override
onOptionsItemSelected(MenuItem item)125     public boolean onOptionsItemSelected(MenuItem item) {
126         switch (item.getItemId()) {
127             case android.R.id.home: {
128                 getActivity().finish();
129                 return true;
130             }
131 
132             case MENU_ALL_PERMS: {
133                 Fragment frag = AllAppPermissionsFragment.newInstance(
134                         getArguments().getString(Intent.EXTRA_PACKAGE_NAME));
135                 getFragmentManager().beginTransaction()
136                         .replace(android.R.id.content, frag)
137                         .addToBackStack("AllPerms")
138                         .commit();
139                 return true;
140             }
141         }
142         return super.onOptionsItemSelected(item);
143     }
144 
145     @Override
onViewCreated(View view, Bundle savedInstanceState)146     public void onViewCreated(View view, Bundle savedInstanceState) {
147         super.onViewCreated(view, savedInstanceState);
148         if (mAppPermissions != null) {
149             bindUi(this, mAppPermissions.getPackageInfo());
150         }
151     }
152 
153     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)154     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
155         super.onCreateOptionsMenu(menu, inflater);
156         menu.add(Menu.NONE, MENU_ALL_PERMS, Menu.NONE, R.string.all_permissions);
157         HelpUtils.prepareHelpMenuItem(getActivity(), menu, R.string.help_app_permissions,
158                 getClass().getName());
159     }
160 
bindUi(SettingsWithHeader fragment, PackageInfo packageInfo)161     private static void bindUi(SettingsWithHeader fragment, PackageInfo packageInfo) {
162         Activity activity = fragment.getActivity();
163         PackageManager pm = activity.getPackageManager();
164         ApplicationInfo appInfo = packageInfo.applicationInfo;
165         Intent infoIntent = null;
166         if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) {
167             infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
168                     .setData(Uri.fromParts("package", packageInfo.packageName, null));
169         }
170 
171         Drawable icon = appInfo.loadIcon(pm);
172         CharSequence label = appInfo.loadLabel(pm);
173         fragment.setHeader(icon, label, infoIntent);
174 
175         ActionBar ab = activity.getActionBar();
176         if (ab != null) {
177             ab.setTitle(R.string.app_permissions);
178         }
179     }
180 
loadPreferences()181     private void loadPreferences() {
182         Context context = getActivity();
183         if (context == null) {
184             return;
185         }
186 
187         PreferenceScreen screen = getPreferenceScreen();
188         if (screen == null) {
189             screen = getPreferenceManager().createPreferenceScreen(getActivity());
190             setPreferenceScreen(screen);
191         }
192 
193         screen.removeAll();
194 
195         if (mExtraScreen != null) {
196             mExtraScreen.removeAll();
197         }
198 
199         final Preference extraPerms = new Preference(context);
200         extraPerms.setIcon(R.drawable.ic_toc);
201         extraPerms.setTitle(R.string.additional_permissions);
202 
203         for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
204             if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName)) {
205                 continue;
206             }
207 
208             boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
209 
210             RestrictedSwitchPreference preference = new RestrictedSwitchPreference(context);
211             preference.setOnPreferenceChangeListener(this);
212             preference.setKey(group.getName());
213             Drawable icon = Utils.loadDrawable(context.getPackageManager(),
214                     group.getIconPkg(), group.getIconResId());
215             preference.setIcon(Utils.applyTint(getContext(), icon,
216                     android.R.attr.colorControlNormal));
217             preference.setTitle(group.getLabel());
218             if (group.isPolicyFixed()) {
219                 EnforcedAdmin admin = RestrictedLockUtils.getProfileOrDeviceOwner(getContext(),
220                         group.getUserId());
221                 if (admin != null) {
222                     preference.setDisabledByAdmin(admin);
223                     preference.setSummary(R.string.disabled_by_admin_summary_text);
224                 } else {
225                     preference.setSummary(R.string.permission_summary_enforced_by_policy);
226                     preference.setEnabled(false);
227                 }
228             }
229             preference.setPersistent(false);
230             preference.setChecked(group.areRuntimePermissionsGranted());
231 
232             if (isPlatform) {
233                 screen.addPreference(preference);
234             } else {
235                 if (mExtraScreen == null) {
236                     mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
237                 }
238                 mExtraScreen.addPreference(preference);
239             }
240         }
241 
242         if (mExtraScreen != null) {
243             extraPerms.setOnPreferenceClickListener(new OnPreferenceClickListener() {
244                 @Override
245                 public boolean onPreferenceClick(Preference preference) {
246                     AdditionalPermissionsFragment frag = new AdditionalPermissionsFragment();
247                     setPackageName(frag, getArguments().getString(Intent.EXTRA_PACKAGE_NAME));
248                     frag.setTargetFragment(AppPermissionsFragment.this, 0);
249                     getFragmentManager().beginTransaction()
250                             .replace(android.R.id.content, frag)
251                             .addToBackStack(null)
252                             .commit();
253                     return true;
254                 }
255             });
256             int count = mExtraScreen.getPreferenceCount();
257             extraPerms.setSummary(getResources().getQuantityString(
258                     R.plurals.additional_permissions_more, count, count));
259             screen.addPreference(extraPerms);
260         }
261 
262         setLoading(false /* loading */, true /* animate */);
263     }
264 
265     @Override
onPreferenceChange(final Preference preference, Object newValue)266     public boolean onPreferenceChange(final Preference preference, Object newValue) {
267         String groupName = preference.getKey();
268         final AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
269 
270         if (group == null) {
271             return false;
272         }
273 
274         addToggledGroup(group);
275 
276         if (LocationUtils.isLocationGroupAndProvider(group.getName(), group.getApp().packageName)) {
277             LocationUtils.showLocationDialog(getContext(), mAppPermissions.getAppLabel());
278             return false;
279         }
280         if (newValue == Boolean.TRUE) {
281             group.grantRuntimePermissions(false);
282         } else {
283             final boolean grantedByDefault = group.hasGrantedByDefaultPermission();
284             if (grantedByDefault || (!group.hasRuntimePermission() && !mHasConfirmedRevoke)) {
285                 new AlertDialog.Builder(getContext())
286                         .setMessage(grantedByDefault ? R.string.system_warning
287                                 : R.string.old_sdk_deny_warning)
288                         .setNegativeButton(R.string.cancel, null)
289                         .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
290                                 new OnClickListener() {
291                             @Override
292                             public void onClick(DialogInterface dialog, int which) {
293                                 ((SwitchPreference) preference).setChecked(false);
294                                 group.revokeRuntimePermissions(false);
295                                 if (!grantedByDefault) {
296                                     mHasConfirmedRevoke = true;
297                                 }
298                             }
299                         })
300                         .show();
301                 return false;
302             } else {
303                 group.revokeRuntimePermissions(false);
304             }
305         }
306 
307         return true;
308     }
309 
310     @Override
onPause()311     public void onPause() {
312         super.onPause();
313         logToggledGroups();
314     }
315 
addToggledGroup(AppPermissionGroup group)316     private void addToggledGroup(AppPermissionGroup group) {
317         if (mToggledGroups == null) {
318             mToggledGroups = new ArrayList<>();
319         }
320         // Double toggle is back to initial state.
321         if (mToggledGroups.contains(group)) {
322             mToggledGroups.remove(group);
323         } else {
324             mToggledGroups.add(group);
325         }
326     }
327 
logToggledGroups()328     private void logToggledGroups() {
329         if (mToggledGroups != null) {
330             String packageName = mAppPermissions.getPackageInfo().packageName;
331             SafetyNetLogger.logPermissionsToggled(packageName, mToggledGroups);
332             mToggledGroups = null;
333         }
334     }
335 
setPreferencesCheckedState()336     private void setPreferencesCheckedState() {
337         setPreferencesCheckedState(getPreferenceScreen());
338         if (mExtraScreen != null) {
339             setPreferencesCheckedState(mExtraScreen);
340         }
341     }
342 
setPreferencesCheckedState(PreferenceScreen screen)343     private void setPreferencesCheckedState(PreferenceScreen screen) {
344         int preferenceCount = screen.getPreferenceCount();
345         for (int i = 0; i < preferenceCount; i++) {
346             Preference preference = screen.getPreference(i);
347             if (preference instanceof SwitchPreference) {
348                 SwitchPreference switchPref = (SwitchPreference) preference;
349                 AppPermissionGroup group = mAppPermissions.getPermissionGroup(switchPref.getKey());
350                 if (group != null) {
351                     switchPref.setChecked(group.areRuntimePermissionsGranted());
352                 }
353             }
354         }
355     }
356 
getPackageInfo(Activity activity, String packageName)357     private static PackageInfo getPackageInfo(Activity activity, String packageName) {
358         try {
359             return activity.getPackageManager().getPackageInfo(
360                     packageName, PackageManager.GET_PERMISSIONS);
361         } catch (PackageManager.NameNotFoundException e) {
362             Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e);
363             return null;
364         }
365     }
366 
367     public static class AdditionalPermissionsFragment extends SettingsWithHeader {
368         AppPermissionsFragment mOuterFragment;
369 
370         @Override
onCreate(Bundle savedInstanceState)371         public void onCreate(Bundle savedInstanceState) {
372             mOuterFragment = (AppPermissionsFragment) getTargetFragment();
373             super.onCreate(savedInstanceState);
374             setHeader(mOuterFragment.mIcon, mOuterFragment.mLabel, mOuterFragment.mInfoIntent);
375             setHasOptionsMenu(true);
376             setPreferenceScreen(mOuterFragment.mExtraScreen);
377         }
378 
379         @Override
onViewCreated(View view, Bundle savedInstanceState)380         public void onViewCreated(View view, Bundle savedInstanceState) {
381             super.onViewCreated(view, savedInstanceState);
382             String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
383             bindUi(this, getPackageInfo(getActivity(), packageName));
384         }
385 
386         @Override
onOptionsItemSelected(MenuItem item)387         public boolean onOptionsItemSelected(MenuItem item) {
388             switch (item.getItemId()) {
389             case android.R.id.home:
390                 getFragmentManager().popBackStack();
391                 return true;
392             }
393             return super.onOptionsItemSelected(item);
394         }
395     }
396 }
397