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.permissioncontroller.permission.ui.handheld; 18 19 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; 20 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; 21 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED; 22 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED; 23 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND; 24 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED; 25 import static com.android.permissioncontroller.hibernation.HibernationPolicyKt.isHibernationEnabled; 26 import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; 27 28 import static java.util.concurrent.TimeUnit.DAYS; 29 30 import android.app.ActionBar; 31 import android.app.Activity; 32 import android.content.ActivityNotFoundException; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.pm.PackageManager; 37 import android.content.pm.ResolveInfo; 38 import android.graphics.drawable.Drawable; 39 import android.icu.text.ListFormatter; 40 import android.net.Uri; 41 import android.os.Build; 42 import android.os.Bundle; 43 import android.os.UserHandle; 44 import android.provider.Settings; 45 import android.util.Log; 46 import android.view.LayoutInflater; 47 import android.view.Menu; 48 import android.view.MenuInflater; 49 import android.view.MenuItem; 50 import android.view.View; 51 import android.view.ViewGroup; 52 import android.widget.Toast; 53 54 import androidx.annotation.NonNull; 55 import androidx.annotation.RequiresApi; 56 import androidx.annotation.StringRes; 57 import androidx.lifecycle.ViewModelProvider; 58 import androidx.preference.Preference; 59 import androidx.preference.PreferenceCategory; 60 import androidx.preference.PreferenceScreen; 61 import androidx.preference.SwitchPreference; 62 63 import com.android.modules.utils.build.SdkLevel; 64 import com.android.permissioncontroller.PermissionControllerStatsLog; 65 import com.android.permissioncontroller.R; 66 import com.android.permissioncontroller.permission.model.AppPermissionUsage; 67 import com.android.permissioncontroller.permission.model.PermissionUsages; 68 import com.android.permissioncontroller.permission.model.livedatatypes.HibernationSettingState; 69 import com.android.permissioncontroller.permission.ui.Category; 70 import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel; 71 import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModel.GroupUiInfo; 72 import com.android.permissioncontroller.permission.ui.model.AppPermissionGroupsViewModelFactory; 73 import com.android.permissioncontroller.permission.utils.KotlinUtils; 74 import com.android.permissioncontroller.permission.utils.Utils; 75 import com.android.settingslib.HelpUtils; 76 import com.android.settingslib.widget.FooterPreference; 77 78 import java.text.Collator; 79 import java.time.Instant; 80 import java.util.ArrayList; 81 import java.util.HashMap; 82 import java.util.List; 83 import java.util.Map; 84 import java.util.Objects; 85 import java.util.Random; 86 87 import kotlin.Pair; 88 89 /** 90 * Show and manage permission groups for an app. 91 * 92 * <p>Shows the list of permission groups the app has requested at one permission for. 93 */ 94 public final class AppPermissionGroupsFragment extends SettingsWithLargeHeader implements 95 PermissionUsages.PermissionsUsagesChangeCallback { 96 97 private static final String LOG_TAG = AppPermissionGroupsFragment.class.getSimpleName(); 98 private static final String IS_SYSTEM_PERMS_SCREEN = "_is_system_screen"; 99 private static final String AUTO_REVOKE_CATEGORY_KEY = "_AUTO_REVOKE_KEY"; 100 private static final String AUTO_REVOKE_SWITCH_KEY = "_AUTO_REVOKE_SWITCH_KEY"; 101 private static final String AUTO_REVOKE_SUMMARY_KEY = "_AUTO_REVOKE_SUMMARY_KEY"; 102 private static final String ASSISTANT_MIC_CATEGORY_KEY = "_ASSISTANT_MIC_KEY"; 103 private static final String ASSISTANT_MIC_SWITCH_KEY = "_ASSISTANT_MIC_SWITCH_KEY"; 104 private static final String ASSISTANT_MIC_SUMMARY_KEY = "_ASSISTANT_MIC_SUMMARY_KEY"; 105 106 static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton"; 107 108 private AppPermissionGroupsViewModel mViewModel; 109 private boolean mIsSystemPermsScreen; 110 private boolean mIsFirstLoad; 111 private String mPackageName; 112 private UserHandle mUser; 113 private PermissionUsages mPermissionUsages; 114 private List<AppPermissionUsage> mAppPermissionUsages = new ArrayList<>(); 115 116 private Collator mCollator; 117 118 /** 119 * Create a bundle with the arguments needed by this fragment 120 * 121 * @param packageName The name of the package 122 * @param userHandle The user of this package 123 * @param sessionId The current session ID 124 * @param isSystemPermsScreen Whether or not this screen is the system permission screen, or 125 * the extra permissions screen 126 * 127 * @return A bundle with all of the args placed 128 */ createArgs(@onNull String packageName, @NonNull UserHandle userHandle, long sessionId, boolean isSystemPermsScreen)129 public static Bundle createArgs(@NonNull String packageName, @NonNull UserHandle userHandle, 130 long sessionId, boolean isSystemPermsScreen) { 131 Bundle arguments = new Bundle(); 132 arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName); 133 arguments.putParcelable(Intent.EXTRA_USER, userHandle); 134 arguments.putLong(EXTRA_SESSION_ID, sessionId); 135 arguments.putBoolean(IS_SYSTEM_PERMS_SCREEN, isSystemPermsScreen); 136 return arguments; 137 } 138 139 /** 140 * Create a bundle for a system permissions fragment 141 * 142 * @param packageName The name of the package 143 * @param userHandle The user of this package 144 * @param sessionId The current session ID 145 * 146 * @return A bundle with all of the args placed 147 */ createArgs(@onNull String packageName, @NonNull UserHandle userHandle, long sessionId)148 public static Bundle createArgs(@NonNull String packageName, @NonNull UserHandle userHandle, 149 long sessionId) { 150 return createArgs(packageName, userHandle, sessionId, true); 151 } 152 153 @Override onCreate(Bundle savedInstanceState)154 public void onCreate(Bundle savedInstanceState) { 155 super.onCreate(savedInstanceState); 156 setHasOptionsMenu(true); 157 mIsFirstLoad = true; 158 final ActionBar ab = getActivity().getActionBar(); 159 if (ab != null) { 160 ab.setDisplayHomeAsUpEnabled(true); 161 } 162 163 mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME); 164 mUser = getArguments().getParcelable(Intent.EXTRA_USER); 165 mIsSystemPermsScreen = getArguments().getBoolean(IS_SYSTEM_PERMS_SCREEN, true); 166 167 AppPermissionGroupsViewModelFactory factory = 168 new AppPermissionGroupsViewModelFactory(mPackageName, mUser, 169 getArguments().getLong(EXTRA_SESSION_ID, 0)); 170 171 mViewModel = new ViewModelProvider(this, factory).get(AppPermissionGroupsViewModel.class); 172 mViewModel.getPackagePermGroupsLiveData().observe(this, this::updatePreferences); 173 mViewModel.getAutoRevokeLiveData().observe(this, this::setAutoRevokeToggleState); 174 175 mCollator = Collator.getInstance( 176 getContext().getResources().getConfiguration().getLocales().get(0)); 177 178 // If the build type is below S, the app ops for permission usage can't be found. Thus, we 179 // shouldn't load permission usages, for them. 180 if (SdkLevel.isAtLeastS()) { 181 Context context = getPreferenceManager().getContext(); 182 mPermissionUsages = new PermissionUsages(context); 183 184 long filterTimeBeginMillis = Math.max(System.currentTimeMillis() 185 - DAYS.toMillis( 186 AppPermissionGroupsViewModel.AGGREGATE_DATA_FILTER_BEGIN_DAYS), 187 Instant.EPOCH.toEpochMilli()); 188 mPermissionUsages.load(null, null, filterTimeBeginMillis, Long.MAX_VALUE, 189 PermissionUsages.USAGE_FLAG_LAST, getActivity().getLoaderManager(), 190 false, false, this, false); 191 } 192 193 updatePreferences(mViewModel.getPackagePermGroupsLiveData().getValue()); 194 } 195 196 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)197 public View onCreateView(LayoutInflater inflater, ViewGroup container, 198 Bundle savedInstanceState) { 199 getActivity().setTitle(R.string.app_permissions); 200 return super.onCreateView(inflater, container, savedInstanceState); 201 } 202 203 @Override 204 @RequiresApi(Build.VERSION_CODES.S) onPermissionUsagesChanged()205 public void onPermissionUsagesChanged() { 206 if (mPermissionUsages.getUsages().isEmpty()) { 207 return; 208 } 209 if (getContext() == null) { 210 // Async result has come in after our context is gone. 211 return; 212 } 213 214 mAppPermissionUsages = new ArrayList<>(mPermissionUsages.getUsages()); 215 updatePreferences(mViewModel.getPackagePermGroupsLiveData().getValue()); 216 } 217 218 @Override onOptionsItemSelected(MenuItem item)219 public boolean onOptionsItemSelected(MenuItem item) { 220 switch (item.getItemId()) { 221 case android.R.id.home: { 222 pressBack(this); 223 return true; 224 } 225 226 case MENU_ALL_PERMS: { 227 mViewModel.showAllPermissions(this, AllAppPermissionsFragment.createArgs( 228 mPackageName, mUser)); 229 return true; 230 } 231 } 232 return super.onOptionsItemSelected(item); 233 } 234 235 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)236 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 237 super.onCreateOptionsMenu(menu, inflater); 238 if (mIsSystemPermsScreen) { 239 menu.add(Menu.NONE, MENU_ALL_PERMS, Menu.NONE, R.string.all_permissions); 240 if (!SdkLevel.isAtLeastS()) { 241 HelpUtils.prepareHelpMenuItem(getActivity(), menu, R.string.help_app_permissions, 242 getClass().getName()); 243 } 244 } 245 } 246 bindUi(SettingsWithLargeHeader fragment, String packageName, UserHandle user)247 private static void bindUi(SettingsWithLargeHeader fragment, String packageName, 248 UserHandle user) { 249 Activity activity = fragment.getActivity(); 250 Intent infoIntent = null; 251 if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) { 252 infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) 253 .setData(Uri.fromParts("package", packageName, null)); 254 } 255 256 Drawable icon = KotlinUtils.INSTANCE.getBadgedPackageIcon(activity.getApplication(), 257 packageName, user); 258 fragment.setHeader(icon, KotlinUtils.INSTANCE.getPackageLabel(activity.getApplication(), 259 packageName, user), infoIntent, user, false); 260 261 } 262 createPreferenceScreenIfNeeded()263 private void createPreferenceScreenIfNeeded() { 264 if (getPreferenceScreen() == null) { 265 addPreferencesFromResource(R.xml.allowed_denied); 266 addAutoRevokePreferences(getPreferenceScreen()); 267 bindUi(this, mPackageName, mUser); 268 } 269 } 270 updatePreferences(Map<Category, List<GroupUiInfo>> groupMap)271 private void updatePreferences(Map<Category, List<GroupUiInfo>> groupMap) { 272 if (groupMap == null) { 273 return; 274 } 275 276 createPreferenceScreenIfNeeded(); 277 278 Context context = getPreferenceManager().getContext(); 279 if (context == null) { 280 return; 281 } 282 283 if (groupMap == null && mViewModel.getPackagePermGroupsLiveData().isInitialized()) { 284 Toast.makeText( 285 getActivity(), R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show(); 286 Log.w(LOG_TAG, "invalid package " + mPackageName); 287 288 pressBack(this); 289 290 return; 291 } 292 293 Map<String, Long> groupUsageLastAccessTime = new HashMap<>(); 294 mViewModel.extractGroupUsageLastAccessTime(groupUsageLastAccessTime, mAppPermissionUsages, 295 mPackageName); 296 297 findPreference(Category.ALLOWED_FOREGROUND.getCategoryName()).setVisible(false); 298 299 long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID); 300 301 for (Category grantCategory : groupMap.keySet()) { 302 PreferenceCategory category = findPreference(grantCategory.getCategoryName()); 303 int numExtraPerms = 0; 304 305 category.removeAll(); 306 307 if (grantCategory.equals(Category.ALLOWED_FOREGROUND)) { 308 category.setVisible(false); 309 category = findPreference(Category.ALLOWED.getCategoryName()); 310 } 311 312 if (grantCategory.equals(Category.ASK)) { 313 if (groupMap.get(grantCategory).size() == 0) { 314 category.setVisible(false); 315 } else { 316 category.setVisible(true); 317 } 318 } 319 320 for (GroupUiInfo groupInfo : groupMap.get(grantCategory)) { 321 String groupName = groupInfo.getGroupName(); 322 Long lastAccessTime = groupUsageLastAccessTime.get(groupName); 323 Pair<String, Integer> summaryTimestamp = Utils 324 .getPermissionLastAccessSummaryTimestamp( 325 lastAccessTime, context, groupName); 326 @Utils.AppPermsLastAccessType int lastAccessType = summaryTimestamp.getSecond(); 327 328 PermissionControlPreference preference = new PermissionControlPreference(context, 329 mPackageName, groupName, mUser, AppPermissionGroupsFragment.class.getName(), 330 sessionId, grantCategory.getCategoryName(), true); 331 preference.setTitle(KotlinUtils.INSTANCE.getPermGroupLabel(context, groupName)); 332 preference.setIcon(KotlinUtils.INSTANCE.getPermGroupIcon(context, groupName)); 333 preference.setKey(groupName); 334 String summary = mViewModel.getPreferenceSummary(groupInfo, context, 335 groupUsageLastAccessTime.get(groupName)); 336 if (!summary.isEmpty()) { 337 preference.setSummary(summary); 338 } 339 // Add an info icon if the package handles ACTION_VIEW_PERMISSION_USAGE. 340 PackageManager packageManager = requireActivity().getPackageManager(); 341 Intent viewUsageIntent = new Intent() 342 .setPackage(mPackageName) 343 .setAction(Intent.ACTION_VIEW_PERMISSION_USAGE) 344 .putExtra(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName); 345 ResolveInfo resolveInfo = packageManager.resolveActivity(viewUsageIntent, 346 PackageManager.MATCH_INSTANT); 347 if (resolveInfo != null && resolveInfo.activityInfo != null && Objects.equals( 348 resolveInfo.activityInfo.permission, 349 android.Manifest.permission.START_VIEW_PERMISSION_USAGE)) { 350 // Make the intent explicit to not require CATEGORY_DEFAULT. 351 viewUsageIntent.setComponent(new ComponentName( 352 resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)); 353 preference.setRightIcon( 354 context.getDrawable(R.drawable.ic_info_outline), 355 v -> { 356 try { 357 startActivity(viewUsageIntent); 358 } catch (ActivityNotFoundException e) { 359 Log.e(LOG_TAG, "No activity found for viewing permission " 360 + "usage."); 361 } 362 }); 363 } 364 if (groupInfo.isSystem() == mIsSystemPermsScreen) { 365 category.addPreference(preference); 366 } else if (!groupInfo.isSystem()) { 367 numExtraPerms++; 368 } 369 } 370 371 int noPermsStringRes = grantCategory.equals(Category.DENIED) 372 ? R.string.no_permissions_denied : R.string.no_permissions_allowed; 373 374 if (numExtraPerms > 0) { 375 final Preference extraPerms = setUpCustomPermissionsScreen(context, numExtraPerms, 376 grantCategory.getCategoryName()); 377 category.addPreference(extraPerms); 378 } 379 380 if (category.getPreferenceCount() == 0) { 381 setNoPermissionPreference(category, noPermsStringRes, context); 382 } 383 384 KotlinUtils.INSTANCE.sortPreferenceGroup(category, this::comparePreferences, false); 385 } 386 387 setAutoRevokeToggleState(mViewModel.getAutoRevokeLiveData().getValue()); 388 389 if (mIsFirstLoad) { 390 logAppPermissionGroupsFragmentView(); 391 mIsFirstLoad = false; 392 } 393 } 394 addAutoRevokePreferences(PreferenceScreen screen)395 private void addAutoRevokePreferences(PreferenceScreen screen) { 396 Context context = screen.getPreferenceManager().getContext(); 397 398 PreferenceCategory autoRevokeCategory = new PreferenceCategory(context); 399 autoRevokeCategory.setKey(AUTO_REVOKE_CATEGORY_KEY); 400 screen.addPreference(autoRevokeCategory); 401 402 SwitchPreference autoRevokeSwitch = new SwitchPreference(context); 403 autoRevokeSwitch.setOnPreferenceClickListener((preference) -> { 404 mViewModel.setAutoRevoke(autoRevokeSwitch.isChecked()); 405 return true; 406 }); 407 autoRevokeSwitch.setTitle(isHibernationEnabled() ? R.string.unused_apps_label 408 : R.string.auto_revoke_label); 409 autoRevokeSwitch.setKey(AUTO_REVOKE_SWITCH_KEY); 410 autoRevokeCategory.addPreference(autoRevokeSwitch); 411 412 Preference autoRevokeSummary = SdkLevel.isAtLeastS() ? new FooterPreference(context) 413 : new Preference(context); 414 autoRevokeSummary.setIcon(Utils.applyTint(getActivity(), R.drawable.ic_info_outline, 415 android.R.attr.colorControlNormal)); 416 autoRevokeSummary.setKey(AUTO_REVOKE_SUMMARY_KEY); 417 if (isHibernationEnabled()) { 418 autoRevokeCategory.setTitle(R.string.unused_apps); 419 } 420 autoRevokeCategory.addPreference(autoRevokeSummary); 421 } 422 setAutoRevokeToggleState(HibernationSettingState state)423 private void setAutoRevokeToggleState(HibernationSettingState state) { 424 if (state == null || !mViewModel.getPackagePermGroupsLiveData().isInitialized() 425 || getPreferenceScreen() == null || getListView() == null || getView() == null) { 426 return; 427 } 428 429 PreferenceCategory autoRevokeCategory = getPreferenceScreen() 430 .findPreference(AUTO_REVOKE_CATEGORY_KEY); 431 SwitchPreference autoRevokeSwitch = autoRevokeCategory.findPreference( 432 AUTO_REVOKE_SWITCH_KEY); 433 Preference autoRevokeSummary = autoRevokeCategory.findPreference( 434 AUTO_REVOKE_SUMMARY_KEY); 435 436 if (!state.isEnabledGlobal() || state.getRevocableGroupNames().isEmpty()) { 437 autoRevokeCategory.setVisible(false); 438 autoRevokeSwitch.setVisible(false); 439 autoRevokeSummary.setVisible(false); 440 return; 441 } 442 autoRevokeCategory.setVisible(true); 443 autoRevokeSwitch.setVisible(true); 444 autoRevokeSummary.setVisible(true); 445 autoRevokeSwitch.setChecked(state.isEnabledForApp()); 446 447 List<String> groupLabels = new ArrayList<>(); 448 for (String groupName : state.getRevocableGroupNames()) { 449 PreferenceCategory category = getPreferenceScreen().findPreference( 450 Category.ALLOWED.getCategoryName()); 451 Preference pref = category.findPreference(groupName); 452 if (pref != null) { 453 groupLabels.add(pref.getTitle().toString()); 454 } 455 } 456 457 groupLabels.sort(mCollator); 458 if (groupLabels.isEmpty()) { 459 autoRevokeSummary.setSummary(R.string.auto_revoke_summary); 460 } else { 461 autoRevokeSummary.setSummary(getString(R.string.auto_revoke_summary_with_permissions, 462 ListFormatter.getInstance().format(groupLabels))); 463 } 464 } 465 comparePreferences(Preference lhs, Preference rhs)466 private int comparePreferences(Preference lhs, Preference rhs) { 467 String additionalTitle = lhs.getContext().getString(R.string.additional_permissions); 468 if (lhs.getTitle().equals(additionalTitle)) { 469 return 1; 470 } else if (rhs.getTitle().equals(additionalTitle)) { 471 return -1; 472 } 473 return mCollator.compare(lhs.getTitle().toString(), 474 rhs.getTitle().toString()); 475 } 476 setUpCustomPermissionsScreen(Context context, int count, String category)477 private Preference setUpCustomPermissionsScreen(Context context, int count, String category) { 478 final Preference extraPerms = new Preference(context); 479 extraPerms.setIcon(Utils.applyTint(getActivity(), R.drawable.ic_toc, 480 android.R.attr.colorControlNormal)); 481 extraPerms.setTitle(R.string.additional_permissions); 482 extraPerms.setKey(extraPerms.getTitle() + category); 483 extraPerms.setOnPreferenceClickListener(preference -> { 484 mViewModel.showExtraPerms(this, AppPermissionGroupsFragment.createArgs( 485 mPackageName, mUser, getArguments().getLong(EXTRA_SESSION_ID), false)); 486 return true; 487 }); 488 extraPerms.setSummary(getResources().getQuantityString( 489 R.plurals.additional_permissions_more, count, count)); 490 return extraPerms; 491 } 492 setNoPermissionPreference(PreferenceCategory category, @StringRes int stringId, Context context)493 private void setNoPermissionPreference(PreferenceCategory category, @StringRes int stringId, 494 Context context) { 495 Preference empty = new Preference(context); 496 empty.setKey(getString(stringId)); 497 empty.setTitle(empty.getKey()); 498 empty.setSelectable(false); 499 category.addPreference(empty); 500 } 501 logAppPermissionGroupsFragmentView()502 private void logAppPermissionGroupsFragmentView() { 503 Context context = getPreferenceManager().getContext(); 504 if (context == null) { 505 return; 506 } 507 String permissionSubtitleOnlyInForeground = 508 context.getString(R.string.permission_subtitle_only_in_foreground); 509 510 511 long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID); 512 long viewId = new Random().nextLong(); 513 514 PreferenceCategory allowed = findPreference(Category.ALLOWED.getCategoryName()); 515 516 int numAllowed = allowed.getPreferenceCount(); 517 for (int i = 0; i < numAllowed; i++) { 518 Preference preference = allowed.getPreference(i); 519 520 if (preference.getTitle().equals(getString(R.string.no_permissions_allowed))) { 521 // R.string.no_permission_allowed was added to PreferenceCategory 522 continue; 523 } 524 525 int category = APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED; 526 if (preference.getSummary() != null 527 && permissionSubtitleOnlyInForeground.contentEquals(preference.getSummary())) { 528 category = APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__ALLOWED_FOREGROUND; 529 } 530 531 logAppPermissionsFragmentViewEntry(sessionId, viewId, preference.getKey(), 532 category); 533 } 534 535 PreferenceCategory denied = findPreference(Category.DENIED.getCategoryName()); 536 537 int numDenied = denied.getPreferenceCount(); 538 for (int i = 0; i < numDenied; i++) { 539 Preference preference = denied.getPreference(i); 540 if (preference.getTitle().equals(getString(R.string.no_permissions_denied))) { 541 // R.string.no_permission_denied was added to PreferenceCategory 542 continue; 543 } 544 logAppPermissionsFragmentViewEntry(sessionId, viewId, preference.getKey(), 545 APP_PERMISSIONS_FRAGMENT_VIEWED__CATEGORY__DENIED); 546 } 547 } 548 logAppPermissionsFragmentViewEntry( long sessionId, long viewId, String permissionGroupName, int category)549 private void logAppPermissionsFragmentViewEntry( 550 long sessionId, long viewId, String permissionGroupName, int category) { 551 552 Integer uid = KotlinUtils.INSTANCE.getPackageUid(getActivity().getApplication(), 553 mPackageName, mUser); 554 if (uid == null) { 555 return; 556 } 557 PermissionControllerStatsLog.write(APP_PERMISSIONS_FRAGMENT_VIEWED, sessionId, viewId, 558 permissionGroupName, uid, mPackageName, category); 559 Log.v(LOG_TAG, "AppPermissionFragment view logged with sessionId=" + sessionId + " viewId=" 560 + viewId + " permissionGroupName=" + permissionGroupName + " uid=" 561 + uid + " packageName=" 562 + mPackageName + " category=" + category); 563 } 564 } 565