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