1 /*
2  * Copyright (C) 2022 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.car.settings.privacy;
18 
19 import android.Manifest;
20 import android.car.drivingstate.CarUxRestrictions;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.hardware.SensorPrivacyManager;
24 import android.os.UserHandle;
25 import android.os.UserManager;
26 import android.permission.PermissionControllerManager;
27 
28 import androidx.preference.Preference;
29 
30 import com.android.car.settings.R;
31 import com.android.car.settings.common.FragmentController;
32 import com.android.car.settings.common.Logger;
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.settingslib.utils.StringUtil;
35 
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.concurrent.atomic.AtomicInteger;
41 
42 /**
43  * This controller displays the number of non-system apps that have access to camera.
44  */
45 public class ManageCameraPermissionsPreferenceController extends
46         CameraPrivacyBasePreferenceController<Preference> {
47     private static final Logger LOG = new Logger(
48             ManageCameraPermissionsPreferenceController.class);
49 
50     private final AtomicInteger mLoadingInProgress = new AtomicInteger(0);
51     private int mNumTotal = 0;
52     private int mNumHasAccess = 0;
53 
54     private final UserManager mUserManager;
55 
ManageCameraPermissionsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)56     public ManageCameraPermissionsPreferenceController(Context context, String preferenceKey,
57             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
58         this(context, preferenceKey, fragmentController, uxRestrictions,
59                 SensorPrivacyManager.getInstance(context),
60                 context.getSystemService(UserManager.class));
61     }
62 
63     @VisibleForTesting
ManageCameraPermissionsPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions, SensorPrivacyManager sensorPrivacyManager, UserManager userManager)64     ManageCameraPermissionsPreferenceController(Context context, String preferenceKey,
65             FragmentController fragmentController, CarUxRestrictions uxRestrictions,
66             SensorPrivacyManager sensorPrivacyManager, UserManager userManager) {
67         super(context, preferenceKey, fragmentController, uxRestrictions, sensorPrivacyManager);
68         mUserManager = userManager;
69     }
70 
71     @Override
getPreferenceType()72     protected Class<Preference> getPreferenceType() {
73         return Preference.class;
74     }
75 
76     @Override
updateState(Preference preference)77     public void updateState(Preference preference) {
78         super.updateState(preference);
79         if (getSensorPrivacyManager().isSensorPrivacyEnabled(
80                 SensorPrivacyManager.Sensors.CAMERA)) {
81             getPreference().setSummary(getContext().getString(
82                     R.string.camera_app_permission_summary_camera_off));
83             return;
84         }
85         // Bail out if there's another loading request in progress.
86         if (mLoadingInProgress.get() != 0) {
87             return;
88         }
89         getPreference().setSummary(
90                 getContext().getString(R.string.camera_settings_loading_app_permission_stats));
91 
92         mNumTotal = 0;
93         mNumHasAccess = 0;
94         // Retrieve a list of users inside the current user profile group.
95         List<UserHandle> users = mUserManager.getUserProfiles();
96         // need to asynchronously load 2 callbacks for each profile
97         mLoadingInProgress.set(2 * users.size());
98         for (UserHandle user : users) {
99             Context userContext = createPackageContextAsUser(getContext(), user);
100             if (userContext == null) {
101                 // remove the two callbacks that we would normally expect for this user
102                 if (mLoadingInProgress.addAndGet(-2) == 0) {
103                     setAppCounts(mNumTotal, mNumHasAccess);
104                 }
105                 continue;
106             }
107             PermissionControllerManager permController =
108                     userContext.getSystemService(PermissionControllerManager.class);
109             permController.countPermissionApps(
110                     Collections.singletonList(Manifest.permission.CAMERA), /* flags= */ 0,
111                     (numApps) -> {
112                         mNumTotal += numApps;
113                         if (mLoadingInProgress.decrementAndGet() == 0) {
114                             setAppCounts(mNumTotal, mNumHasAccess);
115                         }
116                     }, /* handler= */ null);
117             permController.countPermissionApps(
118                     Collections.singletonList(Manifest.permission.CAMERA),
119                     PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED,
120                     (numApps) -> {
121                         mNumHasAccess += numApps;
122                         if (mLoadingInProgress.decrementAndGet() == 0) {
123                             setAppCounts(mNumTotal, mNumHasAccess);
124                         }
125                     }, /* handler= */  null);
126         }
127 
128     }
129 
setAppCounts(int numTotal, int numHasAccess)130     private void setAppCounts(int numTotal, int numHasAccess) {
131         Map<String, Object> arguments = new HashMap<>();
132         arguments.put("count", numHasAccess);
133         arguments.put("total_count", numTotal);
134         getPreference().setSummary(StringUtil.getIcuPluralsString(getContext(), arguments,
135                 R.string.camera_app_permission_summary_camera_on));
136     }
137 
138 
139     /**
140      * Returns a context created from the given context for the given user, or null if it fails.
141      */
createPackageContextAsUser(Context context, UserHandle userHandle)142     private Context createPackageContextAsUser(Context context, UserHandle userHandle) {
143         try {
144             return context.createPackageContextAsUser(
145                     context.getPackageName(), /* flags= */ 0, userHandle);
146         } catch (PackageManager.NameNotFoundException e) {
147             LOG.e("Failed to create user context", e);
148         }
149         return null;
150     }
151 }
152 
153