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