• 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.television;
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.Intent;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.PackageInfo;
27 import android.content.pm.PackageManager;
28 import android.graphics.drawable.Drawable;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.provider.Settings;
32 import android.support.v14.preference.SwitchPreference;
33 import android.support.v7.preference.Preference;
34 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
35 import android.support.v7.preference.PreferenceScreen;
36 import android.support.v7.preference.PreferenceViewHolder;
37 import android.util.Log;
38 import android.view.Menu;
39 import android.view.MenuInflater;
40 import android.view.MenuItem;
41 import android.view.View;
42 import android.widget.Toast;
43 
44 import com.android.packageinstaller.R;
45 import com.android.packageinstaller.permission.model.AppPermissionGroup;
46 import com.android.packageinstaller.permission.model.AppPermissions;
47 import com.android.packageinstaller.permission.ui.ReviewPermissionsActivity;
48 import com.android.packageinstaller.permission.utils.LocationUtils;
49 import com.android.packageinstaller.permission.utils.SafetyNetLogger;
50 import com.android.packageinstaller.permission.utils.Utils;
51 
52 import java.util.ArrayList;
53 import java.util.List;
54 
55 public final class AppPermissionsFragment extends SettingsWithHeader
56         implements OnPreferenceChangeListener {
57 
58     private static final String LOG_TAG = "ManagePermsFragment";
59 
60     static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
61 
62     private static final int MENU_ALL_PERMS = 0;
63 
64     private List<AppPermissionGroup> mToggledGroups;
65     private AppPermissions mAppPermissions;
66     private PreferenceScreen mExtraScreen;
67 
68     private boolean mHasConfirmedRevoke;
69 
newInstance(String packageName)70     public static AppPermissionsFragment newInstance(String packageName) {
71         return setPackageName(new AppPermissionsFragment(), packageName);
72     }
73 
setPackageName(T fragment, String packageName)74     private static <T extends Fragment> T setPackageName(T fragment, String packageName) {
75         Bundle arguments = new Bundle();
76         arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
77         fragment.setArguments(arguments);
78         return fragment;
79     }
80 
81     @Override
onCreate(Bundle savedInstanceState)82     public void onCreate(Bundle savedInstanceState) {
83         super.onCreate(savedInstanceState);
84         setLoading(true /* loading */, false /* animate */);
85         setHasOptionsMenu(true);
86         final ActionBar ab = getActivity().getActionBar();
87         if (ab != null) {
88             ab.setDisplayHomeAsUpEnabled(true);
89         }
90 
91         String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
92         Activity activity = getActivity();
93         PackageInfo packageInfo = getPackageInfo(activity, packageName);
94         if (packageInfo == null) {
95             Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
96             activity.finish();
97             return;
98         }
99 
100 
101         mAppPermissions = new AppPermissions(activity, packageInfo, null, true,
102                 () -> getActivity().finish());
103 
104         if (mAppPermissions.isReviewRequired()) {
105             Intent intent = new Intent(getActivity(), ReviewPermissionsActivity.class);
106             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
107             startActivity(intent);
108             getActivity().finish();
109             return;
110         }
111 
112         loadPreferences();
113     }
114 
115     @Override
onResume()116     public void onResume() {
117         super.onResume();
118         mAppPermissions.refresh();
119         loadPreferences();
120         setPreferencesCheckedState();
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             }
130 
131             case MENU_ALL_PERMS: {
132                 Fragment frag = AllAppPermissionsFragment.newInstance(
133                         getArguments().getString(Intent.EXTRA_PACKAGE_NAME));
134                 getFragmentManager().beginTransaction()
135                         .replace(android.R.id.content, frag)
136                         .addToBackStack("AllPerms")
137                         .commit();
138                 return true;
139             }
140         }
141         return super.onOptionsItemSelected(item);
142     }
143 
144     @Override
onViewCreated(View view, Bundle savedInstanceState)145     public void onViewCreated(View view, Bundle savedInstanceState) {
146         super.onViewCreated(view, savedInstanceState);
147         if (mAppPermissions != null) {
148             bindUi(this, mAppPermissions.getPackageInfo());
149         }
150     }
151 
152     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)153     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
154         super.onCreateOptionsMenu(menu, inflater);
155         menu.add(Menu.NONE, MENU_ALL_PERMS, Menu.NONE, R.string.all_permissions);
156     }
157 
bindUi(SettingsWithHeader fragment, PackageInfo packageInfo)158     private static void bindUi(SettingsWithHeader fragment, PackageInfo packageInfo) {
159         Activity activity = fragment.getActivity();
160         PackageManager pm = activity.getPackageManager();
161         ApplicationInfo appInfo = packageInfo.applicationInfo;
162         Intent infoIntent = null;
163         if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) {
164             infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
165                     .setData(Uri.fromParts("package", packageInfo.packageName, null));
166         }
167 
168         Drawable icon = appInfo.loadIcon(pm);
169         CharSequence label = appInfo.loadLabel(pm);
170         fragment.setHeader(icon, label, infoIntent, fragment.getString(
171                 R.string.app_permissions_decor_title));
172     }
173 
loadPreferences()174     private void loadPreferences() {
175         Context context = getPreferenceManager().getContext();
176         if (context == null) {
177             return;
178         }
179 
180         PreferenceScreen screen = getPreferenceScreen();
181         screen.removeAll();
182         screen.addPreference(createHeaderLineTwoPreference(context));
183 
184         if (mExtraScreen != null) {
185             mExtraScreen.removeAll();
186             mExtraScreen = null;
187         }
188 
189         final Preference extraPerms = new Preference(context);
190         extraPerms.setIcon(R.drawable.ic_toc);
191         extraPerms.setTitle(R.string.additional_permissions);
192 
193         for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
194             if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName)) {
195                 continue;
196             }
197 
198             boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
199 
200             SwitchPreference preference = new SwitchPreference(context);
201             preference.setOnPreferenceChangeListener(this);
202             preference.setKey(group.getName());
203             Drawable icon = Utils.loadDrawable(context.getPackageManager(),
204                     group.getIconPkg(), group.getIconResId());
205             preference.setIcon(Utils.applyTint(getContext(), icon,
206                     android.R.attr.colorControlNormal));
207             preference.setTitle(group.getLabel());
208             if (group.isPolicyFixed()) {
209                 preference.setSummary(getString(R.string.permission_summary_enforced_by_policy));
210             }
211             preference.setPersistent(false);
212             preference.setEnabled(!group.isPolicyFixed());
213             preference.setChecked(group.areRuntimePermissionsGranted());
214 
215             if (isPlatform) {
216                 screen.addPreference(preference);
217             } else {
218                 if (mExtraScreen == null) {
219                     mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
220                     mExtraScreen.addPreference(createHeaderLineTwoPreference(context));
221                 }
222                 mExtraScreen.addPreference(preference);
223             }
224         }
225 
226         if (mExtraScreen != null) {
227             extraPerms.setOnPreferenceClickListener(preference -> {
228                 AdditionalPermissionsFragment frag = new AdditionalPermissionsFragment();
229                 setPackageName(frag, getArguments().getString(Intent.EXTRA_PACKAGE_NAME));
230                 frag.setTargetFragment(AppPermissionsFragment.this, 0);
231                 getFragmentManager().beginTransaction()
232                         .replace(android.R.id.content, frag)
233                         .addToBackStack(null)
234                         .commit();
235                 return true;
236             });
237             int count = mExtraScreen.getPreferenceCount() - 1;
238             extraPerms.setSummary(getResources().getQuantityString(
239                     R.plurals.additional_permissions_more, count, count));
240             screen.addPreference(extraPerms);
241         }
242 
243         setLoading(false /* loading */, true /* animate */);
244     }
245 
246     /**
247      * Creates a heading below decor_title and above the rest of the preferences. This heading
248      * displays the app name and banner icon. It's used in both system and additional permissions
249      * fragments for each app. The styling used is the same as a leanback preference with a
250      * customized background color
251      * @param context The context the preferences created on
252      * @return The preference header to be inserted as the first preference in the list.
253      */
createHeaderLineTwoPreference(Context context)254     private Preference createHeaderLineTwoPreference(Context context) {
255         Preference headerLineTwo = new Preference(context) {
256             @Override
257             public void onBindViewHolder(PreferenceViewHolder holder) {
258                 super.onBindViewHolder(holder);
259                 holder.itemView.setBackgroundColor(
260                         getResources().getColor(R.color.lb_header_banner_color));
261             }
262         };
263         headerLineTwo.setKey(HEADER_PREFERENCE_KEY);
264         headerLineTwo.setSelectable(false);
265         headerLineTwo.setTitle(mLabel);
266         headerLineTwo.setIcon(mIcon);
267         return headerLineTwo;
268     }
269 
270     @Override
onPreferenceChange(final Preference preference, Object newValue)271     public boolean onPreferenceChange(final Preference preference, Object newValue) {
272         String groupName = preference.getKey();
273         final AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
274 
275         if (group == null) {
276             return false;
277         }
278 
279         addToggledGroup(group);
280 
281         if (LocationUtils.isLocationGroupAndProvider(group.getName(), group.getApp().packageName)) {
282             LocationUtils.showLocationDialog(getContext(), mAppPermissions.getAppLabel());
283             return false;
284         }
285         if (newValue == Boolean.TRUE) {
286             group.grantRuntimePermissions(false);
287         } else {
288             final boolean grantedByDefault = group.hasGrantedByDefaultPermission();
289             if (grantedByDefault || (!group.doesSupportRuntimePermissions()
290                     && !mHasConfirmedRevoke)) {
291                 new AlertDialog.Builder(getContext())
292                         .setMessage(grantedByDefault ? R.string.system_warning
293                                 : R.string.old_sdk_deny_warning)
294                         .setNegativeButton(R.string.cancel, null)
295                         .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
296                                 (dialog, which) -> {
297                                     ((SwitchPreference) preference).setChecked(false);
298                                     group.revokeRuntimePermissions(false);
299                                     if (!grantedByDefault) {
300                                         mHasConfirmedRevoke = true;
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             setHasOptionsMenu(true);
378         }
379 
380         @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)381         public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
382             setPreferenceScreen(mOuterFragment.mExtraScreen);
383         }
384 
385         @Override
onViewCreated(View view, Bundle savedInstanceState)386         public void onViewCreated(View view, Bundle savedInstanceState) {
387             super.onViewCreated(view, savedInstanceState);
388             String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
389             bindUi(this, getPackageInfo(getActivity(), packageName));
390         }
391 
bindUi(SettingsWithHeader fragment, PackageInfo packageInfo)392         private static void bindUi(SettingsWithHeader fragment, PackageInfo packageInfo) {
393             Activity activity = fragment.getActivity();
394             PackageManager pm = activity.getPackageManager();
395             ApplicationInfo appInfo = packageInfo.applicationInfo;
396             Intent infoIntent = null;
397             if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) {
398                 infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
399                         .setData(Uri.fromParts("package", packageInfo.packageName, null));
400             }
401 
402             Drawable icon = appInfo.loadIcon(pm);
403             CharSequence label = appInfo.loadLabel(pm);
404             fragment.setHeader(icon, label, infoIntent, fragment.getString(
405                     R.string.additional_permissions_decor_title));
406         }
407 
408         @Override
onOptionsItemSelected(MenuItem item)409         public boolean onOptionsItemSelected(MenuItem item) {
410             switch (item.getItemId()) {
411             case android.R.id.home:
412                 getFragmentManager().popBackStack();
413                 return true;
414             }
415             return super.onOptionsItemSelected(item);
416         }
417     }
418 }
419