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