1 /*
2  * Copyright (C) 2021 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 static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.ArgumentMatchers.anyInt;
22 import static org.mockito.ArgumentMatchers.anyString;
23 import static org.mockito.ArgumentMatchers.eq;
24 import static org.mockito.Mockito.doReturn;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.Manifest;
30 import android.car.drivingstate.CarUxRestrictions;
31 import android.content.Context;
32 import android.hardware.SensorPrivacyManager;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.permission.PermissionControllerManager;
36 
37 import androidx.lifecycle.LifecycleOwner;
38 import androidx.preference.Preference;
39 import androidx.preference.PreferenceManager;
40 import androidx.preference.PreferenceScreen;
41 import androidx.test.annotation.UiThreadTest;
42 import androidx.test.core.app.ApplicationProvider;
43 import androidx.test.ext.junit.runners.AndroidJUnit4;
44 
45 import com.android.car.settings.R;
46 import com.android.car.settings.common.FragmentController;
47 import com.android.car.settings.common.PreferenceControllerTestUtil;
48 import com.android.car.settings.testutils.TestLifecycleOwner;
49 import com.android.settingslib.utils.StringUtil;
50 
51 import org.junit.Before;
52 import org.junit.Test;
53 import org.junit.runner.RunWith;
54 import org.mockito.ArgumentCaptor;
55 import org.mockito.Captor;
56 import org.mockito.Mock;
57 import org.mockito.Mockito;
58 import org.mockito.MockitoAnnotations;
59 
60 import java.util.Collections;
61 import java.util.HashMap;
62 import java.util.List;
63 import java.util.Map;
64 
65 @RunWith(AndroidJUnit4.class)
66 public class ManageMicPermissionsPreferenceControllerTest {
67 
68     private final Context mContext = Mockito.spy(ApplicationProvider.getApplicationContext());
69 
70     @Mock
71     private FragmentController mFragmentController;
72     @Mock
73     private SensorPrivacyManager mMockSensorPrivacyManager;
74     @Mock
75     private UserManager mUserManager;
76     @Mock
77     private UserHandle mUserHandle1;
78     @Mock
79     private UserHandle mUserHandle2;
80     @Captor
81     private ArgumentCaptor<SensorPrivacyManager.OnSensorPrivacyChangedListener> mListener;
82 
83     private ManageMicPermissionsPreferenceController mPreferenceController;
84     private LifecycleOwner mLifecycleOwner;
85     private Preference mPreference;
86 
87     @Before
88     @UiThreadTest
setUp()89     public void setUp() {
90         MockitoAnnotations.initMocks(this);
91         mLifecycleOwner = new TestLifecycleOwner();
92         CarUxRestrictions carUxRestrictions = new CarUxRestrictions.Builder(/* reqOpt= */ true,
93                 CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build();
94 
95         PreferenceManager preferenceManager = new PreferenceManager(mContext);
96         PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
97         mPreference = new Preference(mContext);
98         screen.addPreference(mPreference);
99         mPreferenceController = new ManageMicPermissionsPreferenceController(mContext,
100                 "key", mFragmentController, carUxRestrictions,
101                 mMockSensorPrivacyManager, mUserManager);
102         PreferenceControllerTestUtil.assignPreference(mPreferenceController, mPreference);
103         when(mContext.getPackageName()).thenReturn("test.package");
104         when(mUserHandle1.getIdentifier()).thenReturn(1);
105         when(mUserHandle1.getIdentifier()).thenReturn(2);
106     }
107 
108     @Test
getSummary_whenMicrophoneIsOff_shouldReturnStringForOff()109     public void getSummary_whenMicrophoneIsOff_shouldReturnStringForOff() throws Exception {
110         setIsSensorPrivacyEnabled(true);
111         initializePreference();
112 
113         assertThat(mPreference.getSummary()).isEqualTo(
114                 mContext.getString(R.string.microphone_app_permission_summary_microphone_off));
115     }
116 
117     @Test
getSummary_whenLocationIsOn_shouldReturnLoadingString()118     public void getSummary_whenLocationIsOn_shouldReturnLoadingString() throws Exception {
119         setIsSensorPrivacyEnabled(false);
120         initializePreference();
121 
122         assertThat(mPreference.getSummary()).isEqualTo(
123                 mContext.getString(R.string.microphone_settings_loading_app_permission_stats));
124     }
125 
126     @Test
getSummary_whenLocationAppCountIsOne_shouldShowSingularString()127     public void getSummary_whenLocationAppCountIsOne_shouldShowSingularString() throws Exception {
128         setIsSensorPrivacyEnabled(false);
129 
130         when(mUserManager.getUserProfiles()).thenReturn(List.of(mUserHandle1));
131         PermissionControllerManager user1PermissionControllerManager = setupUser(mUserHandle1);
132         initializePreference();
133         setAppsForUser(1, 1, user1PermissionControllerManager);
134 
135         Map<String, Object> arguments = new HashMap<>();
136         arguments.put("count", 1);
137         arguments.put("total_count", 1);
138 
139         assertThat(mPreference.getSummary()).isEqualTo(StringUtil.getIcuPluralsString(mContext,
140                 arguments, R.string.microphone_app_permission_summary_microphone_on));
141     }
142 
143     @Test
getSummary_whenLocationAppCountIsGreaterThanOne_shouldShowPluralString()144     public void getSummary_whenLocationAppCountIsGreaterThanOne_shouldShowPluralString()
145             throws Exception {
146         setIsSensorPrivacyEnabled(false);
147 
148         when(mUserManager.getUserProfiles()).thenReturn(List.of(mUserHandle1));
149         PermissionControllerManager user1PermissionControllerManager = setupUser(mUserHandle1);
150         initializePreference();
151         setAppsForUser(5, 10, user1PermissionControllerManager);
152 
153         Map<String, Object> arguments = new HashMap<>();
154         arguments.put("count", 5);
155         arguments.put("total_count", 10);
156 
157         assertThat(mPreference.getSummary()).isEqualTo(StringUtil.getIcuPluralsString(mContext,
158                 arguments, R.string.microphone_app_permission_summary_microphone_on));
159     }
160 
161     @Test
getSummary_multipleUsers_addsAppCounts()162     public void getSummary_multipleUsers_addsAppCounts()
163             throws Exception {
164         setIsSensorPrivacyEnabled(false);
165 
166         when(mUserManager.getUserProfiles()).thenReturn(List.of(mUserHandle1, mUserHandle2));
167         PermissionControllerManager user1PermissionControllerManager = setupUser(mUserHandle1);
168         PermissionControllerManager user2PermissionControllerManager = setupUser(mUserHandle2);
169         initializePreference();
170         setAppsForUser(2, 4, user1PermissionControllerManager);
171 
172         // verify that loading state persists until all callbacks have returned
173         assertThat(mPreference.getSummary()).isEqualTo(
174                 mContext.getString(R.string.microphone_settings_loading_app_permission_stats));
175 
176         setAppsForUser(1, 1, user2PermissionControllerManager);
177 
178         Map<String, Object> arguments = new HashMap<>();
179         arguments.put("count", 3);
180         arguments.put("total_count", 5);
181 
182         assertThat(mPreference.getSummary()).isEqualTo(StringUtil.getIcuPluralsString(mContext,
183                 arguments, R.string.microphone_app_permission_summary_microphone_on));
184     }
185 
186     @Test
getSummary_initializeTwiceBeforeCallbacksReturn_callbacksOnlyCalledOnce()187     public void getSummary_initializeTwiceBeforeCallbacksReturn_callbacksOnlyCalledOnce()
188             throws Exception {
189         setIsSensorPrivacyEnabled(false);
190 
191         when(mUserManager.getUserProfiles()).thenReturn(List.of(mUserHandle1));
192         PermissionControllerManager user1PermissionControllerManager = setupUser(mUserHandle1);
193         mPreferenceController.onCreate(mLifecycleOwner);
194         mPreferenceController.onCreate(mLifecycleOwner);
195 
196         // verifies that callbacks are only called once due to the loading state
197         setAppsForUser(2, 4, user1PermissionControllerManager);
198     }
199 
setupUser(UserHandle userHandle)200     private PermissionControllerManager setupUser(UserHandle userHandle)
201             throws Exception {
202         Context userContext = mock(Context.class);
203         doReturn(userContext).when(mContext).createPackageContextAsUser(anyString(),
204                 anyInt(), eq(userHandle));
205         PermissionControllerManager permissionControllerManager = mock(
206                 PermissionControllerManager.class);
207         when(userContext.getSystemService(PermissionControllerManager.class)).thenReturn(
208                 permissionControllerManager);
209         return permissionControllerManager;
210     }
211 
setAppsForUser(int grantedCount, int totalCount, PermissionControllerManager permissionControllerManagerForUser)212     private void setAppsForUser(int grantedCount, int totalCount,
213             PermissionControllerManager permissionControllerManagerForUser) {
214         ArgumentCaptor<PermissionControllerManager.OnCountPermissionAppsResultCallback>
215                 permissionAllAppsCountCallback = ArgumentCaptor.forClass(
216                 PermissionControllerManager.OnCountPermissionAppsResultCallback.class);
217         ArgumentCaptor<PermissionControllerManager.OnCountPermissionAppsResultCallback>
218                 permissionAccessCountCallback = ArgumentCaptor.forClass(
219                 PermissionControllerManager.OnCountPermissionAppsResultCallback.class);
220         verify(permissionControllerManagerForUser).countPermissionApps(
221                 eq(Collections.singletonList(Manifest.permission.RECORD_AUDIO)), eq(/* flags= */ 0),
222                 permissionAllAppsCountCallback.capture(), eq(null));
223         verify(permissionControllerManagerForUser).countPermissionApps(
224                 eq(Collections.singletonList(Manifest.permission.RECORD_AUDIO)),
225                 eq(PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED),
226                 permissionAccessCountCallback.capture(), eq(null));
227         permissionAllAppsCountCallback.getValue().onCountPermissionApps(totalCount);
228         permissionAccessCountCallback.getValue().onCountPermissionApps(grantedCount);
229     }
230 
initializePreference()231     private void initializePreference() {
232         mPreferenceController.onCreate(mLifecycleOwner);
233         mPreferenceController.onStart(mLifecycleOwner);
234         verify(mMockSensorPrivacyManager).addSensorPrivacyListener(
235                 eq(SensorPrivacyManager.Sensors.MICROPHONE), mListener.capture());
236     }
237 
setIsSensorPrivacyEnabled(boolean isMuted)238     private void setIsSensorPrivacyEnabled(boolean isMuted) {
239         when(mMockSensorPrivacyManager.isSensorPrivacyEnabled(
240                 eq(SensorPrivacyManager.Sensors.MICROPHONE))).thenReturn(isMuted);
241     }
242 }
243