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.server.accessibility; 18 19 import static android.app.AlarmManager.RTC_WAKEUP; 20 21 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 22 23 import static com.android.internal.messages.nano.SystemMessageProto.SystemMessage.NOTE_A11Y_VIEW_AND_CONTROL_ACCESS; 24 25 import static com.google.common.truth.Truth.assertThat; 26 27 import static junit.framework.Assert.assertEquals; 28 29 import static org.mockito.ArgumentMatchers.any; 30 import static org.mockito.ArgumentMatchers.anyLong; 31 import static org.mockito.ArgumentMatchers.eq; 32 import static org.mockito.Mockito.never; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.when; 35 36 import android.accessibilityservice.AccessibilityServiceInfo; 37 import android.app.AlarmManager; 38 import android.app.Notification; 39 import android.app.NotificationManager; 40 import android.app.StatusBarManager; 41 import android.content.ComponentName; 42 import android.content.Context; 43 import android.content.Intent; 44 import android.content.pm.ResolveInfo; 45 import android.content.pm.ServiceInfo; 46 import android.os.Bundle; 47 import android.os.UserHandle; 48 import android.provider.Settings; 49 import android.testing.TestableContext; 50 import android.util.ArraySet; 51 52 import com.google.common.collect.ImmutableSet; 53 54 import org.junit.Before; 55 import org.junit.Rule; 56 import org.junit.Test; 57 import org.mockito.ArgumentCaptor; 58 import org.mockito.Mock; 59 import org.mockito.Mockito; 60 import org.mockito.MockitoAnnotations; 61 62 import java.util.ArrayList; 63 import java.util.HashSet; 64 import java.util.List; 65 import java.util.Set; 66 67 /** 68 * Tests for the {@link PolicyWarningUIController}. 69 */ 70 public class PolicyWarningUIControllerTest { 71 private static final int TEST_USER_ID = UserHandle.USER_SYSTEM; 72 private static final ComponentName TEST_COMPONENT_NAME = new ComponentName( 73 "com.android.server.accessibility", "PolicyWarningUIControllerTest"); 74 75 private static final ComponentName TEST_COMPONENT_NAME2 = new ComponentName( 76 "com.android.server.accessibility", "nonAccessibilityToolService"); 77 private final List<AccessibilityServiceInfo> mEnabledServiceList = new ArrayList<>(); 78 79 @Rule 80 public final A11yTestableContext mContext = new A11yTestableContext( 81 getInstrumentation().getTargetContext()); 82 @Mock 83 private AlarmManager mAlarmManager; 84 @Mock 85 private NotificationManager mNotificationManager; 86 @Mock 87 private StatusBarManager mStatusBarManager; 88 @Mock 89 private ServiceInfo mMockServiceInfo; 90 @Mock 91 private Context mSpyContext; 92 93 private PolicyWarningUIController mPolicyWarningUIController; 94 private FakeNotificationController mFakeNotificationController; 95 96 @Before setUp()97 public void setUp() { 98 MockitoAnnotations.initMocks(this); 99 mContext.addMockSystemService(AlarmManager.class, mAlarmManager); 100 mContext.addMockSystemService(NotificationManager.class, mNotificationManager); 101 mContext.addMockSystemService(StatusBarManager.class, mStatusBarManager); 102 mFakeNotificationController = new FakeNotificationController(mContext); 103 mPolicyWarningUIController = new PolicyWarningUIController( 104 getInstrumentation().getTargetContext().getMainThreadHandler(), mContext, 105 mFakeNotificationController); 106 mEnabledServiceList.clear(); 107 Settings.Secure.putStringForUser(mContext.getContentResolver(), 108 Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES, 109 "", TEST_USER_ID); 110 mPolicyWarningUIController.enableSendingNonA11yToolNotification(true); 111 mPolicyWarningUIController.onSwitchUser(TEST_USER_ID, new HashSet<>()); 112 getInstrumentation().waitForIdleSync(); 113 } 114 115 @Test receiveActionSendNotification_isNonA11yCategoryService_sendNotification()116 public void receiveActionSendNotification_isNonA11yCategoryService_sendNotification() { 117 addEnabledServiceInfo(TEST_COMPONENT_NAME, false); 118 119 mFakeNotificationController.onReceive(mContext, 120 PolicyWarningUIController.createIntent(mContext, TEST_USER_ID, 121 PolicyWarningUIController.ACTION_SEND_NOTIFICATION, 122 TEST_COMPONENT_NAME)); 123 124 verify(mNotificationManager).notify(eq(TEST_COMPONENT_NAME.flattenToShortString()), 125 eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(Notification.class)); 126 } 127 128 @Test receiveActionSendNotification_sendNotificationDisabled_doNothing()129 public void receiveActionSendNotification_sendNotificationDisabled_doNothing() { 130 mPolicyWarningUIController.enableSendingNonA11yToolNotification(false); 131 addEnabledServiceInfo(TEST_COMPONENT_NAME, false); 132 133 mFakeNotificationController.onReceive(mContext, 134 PolicyWarningUIController.createIntent(mContext, TEST_USER_ID, 135 PolicyWarningUIController.ACTION_SEND_NOTIFICATION, 136 TEST_COMPONENT_NAME)); 137 138 verify(mNotificationManager, never()).notify(eq(TEST_COMPONENT_NAME.flattenToShortString()), 139 eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(Notification.class)); 140 } 141 142 @Test receiveActionSendNotificationWithNotifiedService_doNothing()143 public void receiveActionSendNotificationWithNotifiedService_doNothing() { 144 Settings.Secure.putStringForUser(mContext.getContentResolver(), 145 Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES, 146 TEST_COMPONENT_NAME.flattenToShortString(), TEST_USER_ID); 147 mEnabledServiceList.clear(); 148 mPolicyWarningUIController.onSwitchUser(TEST_USER_ID, new HashSet<>()); 149 getInstrumentation().waitForIdleSync(); 150 addEnabledServiceInfo(TEST_COMPONENT_NAME, false); 151 152 mFakeNotificationController.onReceive(mContext, 153 PolicyWarningUIController.createIntent(mContext, TEST_USER_ID, 154 PolicyWarningUIController.ACTION_SEND_NOTIFICATION, 155 TEST_COMPONENT_NAME)); 156 157 verify(mNotificationManager, never()).notify(eq(TEST_COMPONENT_NAME.flattenToShortString()), 158 eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(Notification.class)); 159 } 160 161 @Test receiveActionA11ySettings_launchA11ySettingsAndDismissNotification()162 public void receiveActionA11ySettings_launchA11ySettingsAndDismissNotification() { 163 mFakeNotificationController.onReceive(mContext, 164 PolicyWarningUIController.createIntent(mContext, TEST_USER_ID, 165 PolicyWarningUIController.ACTION_A11Y_SETTINGS, 166 TEST_COMPONENT_NAME)); 167 168 verifyLaunchA11ySettings(); 169 verify(mNotificationManager).cancel(TEST_COMPONENT_NAME.flattenToShortString(), 170 NOTE_A11Y_VIEW_AND_CONTROL_ACCESS); 171 assertNotifiedSettingsEqual(TEST_USER_ID, TEST_COMPONENT_NAME.flattenToShortString()); 172 } 173 174 @Test receiveActionDismissNotification_addToNotifiedSettings()175 public void receiveActionDismissNotification_addToNotifiedSettings() { 176 mFakeNotificationController.onReceive(mContext, 177 PolicyWarningUIController.createIntent(mContext, TEST_USER_ID, 178 PolicyWarningUIController.ACTION_DISMISS_NOTIFICATION, 179 TEST_COMPONENT_NAME)); 180 181 assertNotifiedSettingsEqual(TEST_USER_ID, TEST_COMPONENT_NAME.flattenToShortString()); 182 } 183 184 @Test onEnabledServicesChangedLocked_serviceDisabled_removedFromNotifiedSettings()185 public void onEnabledServicesChangedLocked_serviceDisabled_removedFromNotifiedSettings() { 186 final Set<ComponentName> enabledServices = new HashSet<>(); 187 enabledServices.add(TEST_COMPONENT_NAME); 188 mPolicyWarningUIController.onEnabledServicesChanged(TEST_USER_ID, enabledServices); 189 getInstrumentation().waitForIdleSync(); 190 receiveActionDismissNotification_addToNotifiedSettings(); 191 192 mPolicyWarningUIController.onEnabledServicesChanged(TEST_USER_ID, new HashSet<>()); 193 getInstrumentation().waitForIdleSync(); 194 195 assertNotifiedSettingsEqual(TEST_USER_ID, ""); 196 } 197 198 @Test onNonA11yCategoryServiceBound_setAlarm()199 public void onNonA11yCategoryServiceBound_setAlarm() { 200 mPolicyWarningUIController.onNonA11yCategoryServiceBound(TEST_USER_ID, TEST_COMPONENT_NAME); 201 getInstrumentation().waitForIdleSync(); 202 203 verify(mAlarmManager).set(eq(RTC_WAKEUP), anyLong(), 204 eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID, 205 PolicyWarningUIController.ACTION_SEND_NOTIFICATION, TEST_COMPONENT_NAME))); 206 } 207 208 @Test onNonA11yCategoryServiceUnbound_cancelAlarm()209 public void onNonA11yCategoryServiceUnbound_cancelAlarm() { 210 mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(TEST_USER_ID, 211 TEST_COMPONENT_NAME); 212 getInstrumentation().waitForIdleSync(); 213 214 verify(mAlarmManager).cancel( 215 eq(PolicyWarningUIController.createPendingIntent(mContext, TEST_USER_ID, 216 PolicyWarningUIController.ACTION_SEND_NOTIFICATION, TEST_COMPONENT_NAME))); 217 } 218 219 @Test onSwitchUserLocked_hasAlarmAndSentNotification_cancelNotification()220 public void onSwitchUserLocked_hasAlarmAndSentNotification_cancelNotification() { 221 addEnabledServiceInfo(TEST_COMPONENT_NAME2, false); 222 final Set<ComponentName> enabledNonA11yServices = new ArraySet<>(); 223 enabledNonA11yServices.add(TEST_COMPONENT_NAME); 224 enabledNonA11yServices.add(TEST_COMPONENT_NAME2); 225 mPolicyWarningUIController.onEnabledServicesChanged(TEST_USER_ID, 226 enabledNonA11yServices); 227 mPolicyWarningUIController.onNonA11yCategoryServiceBound(TEST_USER_ID, TEST_COMPONENT_NAME); 228 mFakeNotificationController.onReceive(mContext, 229 PolicyWarningUIController.createIntent(mContext, TEST_USER_ID, 230 PolicyWarningUIController.ACTION_SEND_NOTIFICATION, 231 TEST_COMPONENT_NAME2)); 232 getInstrumentation().waitForIdleSync(); 233 234 mPolicyWarningUIController.onSwitchUser(TEST_USER_ID, 235 ImmutableSet.copyOf(new ArraySet<>())); 236 getInstrumentation().waitForIdleSync(); 237 238 verify(mNotificationManager).cancel(TEST_COMPONENT_NAME2.flattenToShortString(), 239 NOTE_A11Y_VIEW_AND_CONTROL_ACCESS); 240 } 241 assertNotifiedSettingsEqual(int userId, String settingString)242 private void assertNotifiedSettingsEqual(int userId, String settingString) { 243 final String notifiedServicesSetting = Settings.Secure.getStringForUser( 244 mContext.getContentResolver(), 245 Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES, 246 userId); 247 assertEquals(settingString, notifiedServicesSetting); 248 } 249 verifyLaunchA11ySettings()250 private void verifyLaunchA11ySettings() { 251 final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); 252 final ArgumentCaptor<UserHandle> userHandleCaptor = ArgumentCaptor.forClass( 253 UserHandle.class); 254 verify(mSpyContext).startActivityAsUser(intentCaptor.capture(), 255 any(), userHandleCaptor.capture()); 256 assertThat(intentCaptor.getValue().getAction()).isEqualTo( 257 Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS); 258 assertThat(userHandleCaptor.getValue().getIdentifier()).isEqualTo(TEST_USER_ID); 259 verify(mStatusBarManager).collapsePanels(); 260 } 261 addEnabledServiceInfo(ComponentName componentName, boolean isAccessibilityTool)262 private void addEnabledServiceInfo(ComponentName componentName, boolean isAccessibilityTool) { 263 final AccessibilityServiceInfo a11yServiceInfo = Mockito.mock( 264 AccessibilityServiceInfo.class); 265 when(a11yServiceInfo.getComponentName()).thenReturn(componentName); 266 when(a11yServiceInfo.isAccessibilityTool()).thenReturn(isAccessibilityTool); 267 final ResolveInfo resolveInfo = Mockito.mock(ResolveInfo.class); 268 when(a11yServiceInfo.getResolveInfo()).thenReturn(resolveInfo); 269 resolveInfo.serviceInfo = mMockServiceInfo; 270 mEnabledServiceList.add(a11yServiceInfo); 271 } 272 273 private class A11yTestableContext extends TestableContext { A11yTestableContext(Context base)274 A11yTestableContext(Context base) { 275 super(base); 276 } 277 278 @Override startActivityAsUser(Intent intent, Bundle options, UserHandle user)279 public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) { 280 mSpyContext.startActivityAsUser(intent, options, user); 281 } 282 } 283 284 private class FakeNotificationController extends 285 PolicyWarningUIController.NotificationController { FakeNotificationController(Context context)286 FakeNotificationController(Context context) { 287 super(context); 288 } 289 290 @Override getEnabledServiceInfos()291 protected List<AccessibilityServiceInfo> getEnabledServiceInfos() { 292 return mEnabledServiceList; 293 } 294 } 295 } 296