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