1 /* 2 * Copyright (C) 2017 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.Activity; 20 import android.app.DialogFragment; 21 import android.content.Intent; 22 import android.content.IntentSender; 23 import android.content.pm.PackageInfo; 24 import android.graphics.drawable.Drawable; 25 import android.os.Bundle; 26 import android.os.RemoteCallback; 27 import android.preference.Preference; 28 import android.preference.PreferenceCategory; 29 import android.preference.PreferenceFragment; 30 import android.preference.PreferenceGroup; 31 import android.preference.PreferenceScreen; 32 import android.preference.SwitchPreference; 33 import android.preference.TwoStatePreference; 34 import android.text.Html; 35 import android.text.Spanned; 36 import android.view.View; 37 import android.widget.Button; 38 import android.widget.ImageView; 39 import android.widget.TextView; 40 41 import com.android.packageinstaller.R; 42 import com.android.packageinstaller.permission.model.AppPermissionGroup; 43 import com.android.packageinstaller.permission.model.AppPermissions; 44 import com.android.packageinstaller.permission.model.Permission; 45 import com.android.packageinstaller.permission.ui.ConfirmActionDialogFragment; 46 import com.android.packageinstaller.permission.ui.ManagePermissionsActivity; 47 import com.android.packageinstaller.permission.utils.ArrayUtils; 48 import com.android.packageinstaller.permission.utils.Utils; 49 50 import java.util.ArrayList; 51 import java.util.List; 52 53 public final class ReviewPermissionsFragment extends PreferenceFragment 54 implements View.OnClickListener, Preference.OnPreferenceChangeListener, 55 ConfirmActionDialogFragment.OnActionConfirmedListener { 56 57 public static final String EXTRA_PACKAGE_INFO = 58 "com.android.packageinstaller.permission.ui.extra.PACKAGE_INFO"; 59 60 private AppPermissions mAppPermissions; 61 62 private Button mContinueButton; 63 private Button mCancelButton; 64 private Button mMoreInfoButton; 65 66 private PreferenceCategory mNewPermissionsCategory; 67 private PreferenceCategory mCurrentPermissionsCategory; 68 69 private boolean mHasConfirmedRevoke; 70 newInstance(PackageInfo packageInfo)71 public static ReviewPermissionsFragment newInstance(PackageInfo packageInfo) { 72 Bundle arguments = new Bundle(); 73 arguments.putParcelable(ReviewPermissionsFragment.EXTRA_PACKAGE_INFO, packageInfo); 74 ReviewPermissionsFragment instance = new ReviewPermissionsFragment(); 75 instance.setArguments(arguments); 76 instance.setRetainInstance(true); 77 return instance; 78 } 79 80 @Override onCreate(Bundle savedInstanceState)81 public void onCreate(Bundle savedInstanceState) { 82 super.onCreate(savedInstanceState); 83 84 Activity activity = getActivity(); 85 if (activity == null) { 86 return; 87 } 88 89 PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO); 90 if (packageInfo == null) { 91 activity.finish(); 92 return; 93 } 94 95 mAppPermissions = new AppPermissions(activity, packageInfo, null, false, 96 new Runnable() { 97 @Override 98 public void run() { 99 getActivity().finish(); 100 } 101 }); 102 103 if (mAppPermissions.getPermissionGroups().isEmpty()) { 104 activity.finish(); 105 return; 106 } 107 108 boolean reviewRequired = false; 109 for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) { 110 if (group.isReviewRequired()) { 111 reviewRequired = true; 112 break; 113 } 114 } 115 116 if (!reviewRequired) { 117 activity.finish(); 118 } 119 } 120 121 @Override onViewCreated(View view, Bundle savedInstanceState)122 public void onViewCreated(View view, Bundle savedInstanceState) { 123 super.onViewCreated(view, savedInstanceState); 124 bindUi(); 125 } 126 127 @Override onResume()128 public void onResume() { 129 super.onResume(); 130 mAppPermissions.refresh(); 131 loadPreferences(); 132 } 133 134 @Override onClick(View view)135 public void onClick(View view) { 136 Activity activity = getActivity(); 137 if (activity == null) { 138 return; 139 } 140 if (view == mContinueButton) { 141 confirmPermissionsReview(); 142 executeCallback(true); 143 } else if (view == mCancelButton) { 144 executeCallback(false); 145 activity.setResult(Activity.RESULT_CANCELED); 146 } else if (view == mMoreInfoButton) { 147 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS); 148 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, 149 mAppPermissions.getPackageInfo().packageName); 150 intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true); 151 getActivity().startActivity(intent); 152 } 153 activity.finish(); 154 } 155 156 @Override onPreferenceChange(Preference preference, Object newValue)157 public boolean onPreferenceChange(Preference preference, Object newValue) { 158 if (mHasConfirmedRevoke) { 159 return true; 160 } 161 if (preference instanceof SwitchPreference) { 162 SwitchPreference switchPreference = (SwitchPreference) preference; 163 if (switchPreference.isChecked()) { 164 showWarnRevokeDialog(switchPreference.getKey()); 165 } else { 166 return true; 167 } 168 } 169 return false; 170 } 171 172 @Override onActionConfirmed(String action)173 public void onActionConfirmed(String action) { 174 Preference preference = getPreferenceManager().findPreference(action); 175 if (preference instanceof SwitchPreference) { 176 SwitchPreference switchPreference = (SwitchPreference) preference; 177 switchPreference.setChecked(false); 178 mHasConfirmedRevoke = true; 179 } 180 } 181 showWarnRevokeDialog(final String groupName)182 private void showWarnRevokeDialog(final String groupName) { 183 DialogFragment fragment = ConfirmActionDialogFragment.newInstance( 184 getString(R.string.old_sdk_deny_warning), groupName); 185 fragment.show(getFragmentManager(), fragment.getClass().getName()); 186 } 187 confirmPermissionsReview()188 private void confirmPermissionsReview() { 189 final List<PreferenceGroup> preferenceGroups = new ArrayList<>(); 190 if (mNewPermissionsCategory != null) { 191 preferenceGroups.add(mNewPermissionsCategory); 192 preferenceGroups.add(mCurrentPermissionsCategory); 193 } else { 194 preferenceGroups.add(getPreferenceScreen()); 195 } 196 197 final int preferenceGroupCount = preferenceGroups.size(); 198 for (int groupNum = 0; groupNum < preferenceGroupCount; groupNum++) { 199 final PreferenceGroup preferenceGroup = preferenceGroups.get(groupNum); 200 201 final int preferenceCount = preferenceGroup.getPreferenceCount(); 202 for (int prefNum = 0; prefNum < preferenceCount; prefNum++) { 203 Preference preference = preferenceGroup.getPreference(prefNum); 204 if (preference instanceof TwoStatePreference) { 205 TwoStatePreference twoStatePreference = (TwoStatePreference) preference; 206 String groupName = preference.getKey(); 207 AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName); 208 if (twoStatePreference.isChecked()) { 209 String[] permissionsToGrant = null; 210 final int permissionCount = group.getPermissions().size(); 211 for (int j = 0; j < permissionCount; j++) { 212 final Permission permission = group.getPermissions().get(j); 213 if (permission.isReviewRequired()) { 214 permissionsToGrant = ArrayUtils.appendString( 215 permissionsToGrant, permission.getName()); 216 } 217 } 218 if (permissionsToGrant != null) { 219 group.grantRuntimePermissions(false, permissionsToGrant); 220 } 221 } else { 222 group.revokeRuntimePermissions(false); 223 } 224 group.resetReviewRequired(); 225 } 226 } 227 } 228 } 229 bindUi()230 private void bindUi() { 231 Activity activity = getActivity(); 232 if (activity == null) { 233 return; 234 } 235 236 // Set icon 237 Drawable icon = mAppPermissions.getPackageInfo().applicationInfo.loadIcon( 238 activity.getPackageManager()); 239 ImageView iconView = (ImageView) activity.findViewById(R.id.app_icon); 240 iconView.setImageDrawable(icon); 241 242 // Set message 243 final int labelTemplateResId = isPackageUpdated() 244 ? R.string.permission_review_title_template_update 245 : R.string.permission_review_title_template_install; 246 Spanned message = Html.fromHtml(getString(labelTemplateResId, 247 mAppPermissions.getAppLabel()), 0); 248 249 // Set the permission message as the title so it can be announced. 250 activity.setTitle(message.toString()); 251 252 // Color the app name. 253 TextView permissionsMessageView = (TextView) activity.findViewById( 254 R.id.permissions_message); 255 permissionsMessageView.setText(message); 256 257 mContinueButton = (Button) getActivity().findViewById(R.id.continue_button); 258 mContinueButton.setOnClickListener(this); 259 260 mCancelButton = (Button) getActivity().findViewById(R.id.cancel_button); 261 mCancelButton.setOnClickListener(this); 262 263 mMoreInfoButton = (Button) getActivity().findViewById( 264 R.id.permission_more_info_button); 265 mMoreInfoButton.setOnClickListener(this); 266 } 267 loadPreferences()268 private void loadPreferences() { 269 Activity activity = getActivity(); 270 if (activity == null) { 271 return; 272 } 273 274 PreferenceScreen screen = getPreferenceScreen(); 275 if (screen == null) { 276 screen = getPreferenceManager().createPreferenceScreen(getActivity()); 277 setPreferenceScreen(screen); 278 } else { 279 screen.removeAll(); 280 } 281 282 mCurrentPermissionsCategory = null; 283 PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory; 284 mNewPermissionsCategory = null; 285 286 final boolean isPackageUpdated = isPackageUpdated(); 287 288 for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) { 289 if (!Utils.shouldShowPermission(group, mAppPermissions.getPackageInfo().packageName) 290 || !Utils.OS_PKG.equals(group.getDeclaringPackage())) { 291 continue; 292 } 293 294 final SwitchPreference preference; 295 Preference cachedPreference = oldNewPermissionsCategory != null 296 ? oldNewPermissionsCategory.findPreference(group.getName()) : null; 297 if (cachedPreference instanceof SwitchPreference) { 298 preference = (SwitchPreference) cachedPreference; 299 } else { 300 preference = new SwitchPreference(getActivity()); 301 302 preference.setKey(group.getName()); 303 Drawable icon = Utils.loadDrawable(activity.getPackageManager(), 304 group.getIconPkg(), group.getIconResId()); 305 preference.setIcon(Utils.applyTint(getContext(), icon, 306 android.R.attr.colorControlNormal)); 307 preference.setTitle(group.getLabel()); 308 preference.setSummary(group.getDescription()); 309 preference.setPersistent(false); 310 311 preference.setOnPreferenceChangeListener(this); 312 } 313 314 preference.setChecked(group.areRuntimePermissionsGranted() 315 || group.isReviewRequired()); 316 317 // Mutable state 318 if (group.isPolicyFixed()) { 319 preference.setEnabled(false); 320 preference.setSummary(getString( 321 R.string.permission_summary_enforced_by_policy)); 322 } else { 323 preference.setEnabled(true); 324 } 325 326 if (group.isReviewRequired()) { 327 if (!isPackageUpdated) { 328 screen.addPreference(preference); 329 } else { 330 if (mNewPermissionsCategory == null) { 331 mNewPermissionsCategory = new PreferenceCategory(activity); 332 mNewPermissionsCategory.setTitle(R.string.new_permissions_category); 333 mNewPermissionsCategory.setOrder(1); 334 screen.addPreference(mNewPermissionsCategory); 335 } 336 mNewPermissionsCategory.addPreference(preference); 337 } 338 } else { 339 if (mCurrentPermissionsCategory == null) { 340 mCurrentPermissionsCategory = new PreferenceCategory(activity); 341 mCurrentPermissionsCategory.setTitle(R.string.current_permissions_category); 342 mCurrentPermissionsCategory.setOrder(2); 343 screen.addPreference(mCurrentPermissionsCategory); 344 } 345 mCurrentPermissionsCategory.addPreference(preference); 346 } 347 } 348 } 349 isPackageUpdated()350 private boolean isPackageUpdated() { 351 List<AppPermissionGroup> groups = mAppPermissions.getPermissionGroups(); 352 final int groupCount = groups.size(); 353 for (int i = 0; i < groupCount; i++) { 354 AppPermissionGroup group = groups.get(i); 355 if (!group.isReviewRequired()) { 356 return true; 357 } 358 } 359 return false; 360 } 361 executeCallback(boolean success)362 private void executeCallback(boolean success) { 363 Activity activity = getActivity(); 364 if (activity == null) { 365 return; 366 } 367 if (success) { 368 IntentSender intent = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT); 369 if (intent != null) { 370 try { 371 int flagMask = 0; 372 int flagValues = 0; 373 if (activity.getIntent().getBooleanExtra( 374 Intent.EXTRA_RESULT_NEEDED, false)) { 375 flagMask = Intent.FLAG_ACTIVITY_FORWARD_RESULT; 376 flagValues = Intent.FLAG_ACTIVITY_FORWARD_RESULT; 377 } 378 activity.startIntentSenderForResult(intent, -1, null, 379 flagMask, flagValues, 0); 380 } catch (IntentSender.SendIntentException e) { 381 /* ignore */ 382 } 383 return; 384 } 385 } 386 RemoteCallback callback = activity.getIntent().getParcelableExtra( 387 Intent.EXTRA_REMOTE_CALLBACK); 388 if (callback != null) { 389 Bundle result = new Bundle(); 390 result.putBoolean(Intent.EXTRA_RETURN_RESULT, success); 391 callback.sendResult(result); 392 } 393 } 394 } 395