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