Merge "Eanble sending non-A11yTool notification dyanmically" into tm-dev

This commit is contained in:
Ryan Lin
2022-04-11 04:34:50 +00:00
committed by Android (Google) Code Review
5 changed files with 194 additions and 61 deletions

View File

@@ -94,6 +94,7 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.SettingsStringUtil.SettingStringHelper;
import android.safetycenter.SafetyCenterManager;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.ArraySet;
@@ -414,11 +415,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mMainHandler = new MainHandler(mContext.getMainLooper());
mActivityTaskManagerService = LocalServices.getService(ActivityTaskManagerInternal.class);
mPackageManager = mContext.getPackageManager();
PolicyWarningUIController policyWarningUIController;
if (AccessibilitySecurityPolicy.POLICY_WARNING_ENABLED) {
policyWarningUIController = new PolicyWarningUIController(mMainHandler, context,
new PolicyWarningUIController.NotificationController(context));
}
final PolicyWarningUIController policyWarningUIController = new PolicyWarningUIController(
mMainHandler, context,
new PolicyWarningUIController.NotificationController(context));
mSecurityPolicy = new AccessibilitySecurityPolicy(policyWarningUIController, mContext,
this);
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
@@ -467,6 +466,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
LocalServices.getService(AppWidgetManagerInternal.class));
}
}
// SafetyCenterService is ready after this phase.
if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
setNonA11yToolNotificationToMatchSafetyCenter();
}
}
private void setNonA11yToolNotificationToMatchSafetyCenter() {
final boolean sendNotification = !mContext.getSystemService(
SafetyCenterManager.class).isSafetyCenterEnabled();
synchronized (mLock) {
mSecurityPolicy.setSendingNonA11yToolNotificationLocked(sendNotification);
}
}
AccessibilityUserState getCurrentUserState() {
@@ -722,6 +734,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
}, UserHandle.ALL, intentFilter, null, null);
final IntentFilter filter = new IntentFilter();
filter.addAction(SafetyCenterManager.ACTION_SAFETY_CENTER_ENABLED_CHANGED);
final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
setNonA11yToolNotificationToMatchSafetyCenter();
}
};
mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, mMainHandler,
Context.RECEIVER_EXPORTED);
}
// Called only during settings restore; currently supports only the owner user

View File

@@ -80,8 +80,6 @@ public class AccessibilitySecurityPolicy {
| AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
| AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
public static final boolean POLICY_WARNING_ENABLED = true;
/**
* Methods that should find their way into separate modules, but are still in AMS
* TODO (b/111889696): Refactoring UserState to AccessibilityUserManager.
@@ -106,6 +104,7 @@ public class AccessibilitySecurityPolicy {
private AppWidgetManagerInternal mAppWidgetService;
private AccessibilityWindowManager mAccessibilityWindowManager;
private int mCurrentUserId = UserHandle.USER_NULL;
private boolean mSendNonA11yToolNotificationEnabled = false;
/**
* Constructor for AccessibilityManagerService.
@@ -119,7 +118,25 @@ public class AccessibilitySecurityPolicy {
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mPolicyWarningUIController = policyWarningUIController;
mPolicyWarningUIController.setAccessibilityPolicyManager(this);
}
/**
* Enables sending the notification for non-AccessibilityTool services with the given state.
*
*/
public void setSendingNonA11yToolNotificationLocked(boolean enable) {
if (enable == mSendNonA11yToolNotificationEnabled) {
return;
}
mSendNonA11yToolNotificationEnabled = enable;
mPolicyWarningUIController.enableSendingNonA11yToolNotification(enable);
if (enable) {
for (int i = 0; i < mNonA11yCategoryServices.size(); i++) {
final ComponentName service = mNonA11yCategoryServices.valueAt(i);
mPolicyWarningUIController.onNonA11yCategoryServiceBound(mCurrentUserId, service);
}
}
}
/**
@@ -725,9 +742,6 @@ public class AccessibilitySecurityPolicy {
*/
public void onBoundServicesChangedLocked(int userId,
ArrayList<AccessibilityServiceConnection> boundServices) {
if (!POLICY_WARNING_ENABLED) {
return;
}
if (mAccessibilityUserManager.getCurrentUserIdLocked() != userId) {
return;
}
@@ -742,7 +756,9 @@ public class AccessibilitySecurityPolicy {
if (mNonA11yCategoryServices.contains(service)) {
mNonA11yCategoryServices.remove(service);
} else {
mPolicyWarningUIController.onNonA11yCategoryServiceBound(userId, service);
if (mSendNonA11yToolNotificationEnabled) {
mPolicyWarningUIController.onNonA11yCategoryServiceBound(userId, service);
}
}
}
}
@@ -763,14 +779,11 @@ public class AccessibilitySecurityPolicy {
* @param enabledServices The enabled services
*/
public void onSwitchUserLocked(int userId, Set<ComponentName> enabledServices) {
if (!POLICY_WARNING_ENABLED) {
return;
}
if (mCurrentUserId == userId) {
return;
}
mPolicyWarningUIController.onSwitchUserLocked(userId, enabledServices);
mPolicyWarningUIController.onSwitchUser(userId,
new ArraySet<>(enabledServices));
for (int i = 0; i < mNonA11yCategoryServices.size(); i++) {
mPolicyWarningUIController.onNonA11yCategoryServiceUnbound(mCurrentUserId,
@@ -786,15 +799,11 @@ public class AccessibilitySecurityPolicy {
* @param userId The user id
* @param enabledServices The enabled services
*/
public void onEnabledServicesChangedLocked(int userId,
Set<ComponentName> enabledServices) {
if (!POLICY_WARNING_ENABLED) {
return;
}
public void onEnabledServicesChangedLocked(int userId, Set<ComponentName> enabledServices) {
if (mAccessibilityUserManager.getCurrentUserIdLocked() != userId) {
return;
}
mPolicyWarningUIController.onEnabledServicesChangedLocked(userId, enabledServices);
mPolicyWarningUIController.onEnabledServicesChanged(userId,
new ArraySet<>(enabledServices));
}
}

View File

@@ -53,6 +53,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ImageUtils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
@@ -96,24 +97,23 @@ public class PolicyWarningUIController {
filter.addAction(ACTION_DISMISS_NOTIFICATION);
mContext.registerReceiver(mNotificationController, filter,
Manifest.permission.MANAGE_ACCESSIBILITY, mMainHandler, Context.RECEIVER_EXPORTED);
}
protected void setAccessibilityPolicyManager(
AccessibilitySecurityPolicy accessibilitySecurityPolicy) {
mNotificationController.setAccessibilityPolicyManager(accessibilitySecurityPolicy);
}
/**
* Updates enabled accessibility services and notified accessibility services after switching
* to another user.
*
* @param enabledServices The current enabled services
* @param enabledServices the current enabled services
*/
public void onSwitchUserLocked(int userId, Set<ComponentName> enabledServices) {
public void onSwitchUser(int userId, Set<ComponentName> enabledServices) {
mMainHandler.sendMessage(
obtainMessage(this::onSwitchUserInternal, userId, enabledServices));
}
private void onSwitchUserInternal(int userId, Set<ComponentName> enabledServices) {
mEnabledA11yServices.clear();
mEnabledA11yServices.addAll(enabledServices);
mMainHandler.sendMessage(obtainMessage(mNotificationController::onSwitchUser, userId));
mNotificationController.onSwitchUser(userId);
}
/**
@@ -122,10 +122,14 @@ public class PolicyWarningUIController {
* setting {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} changed.
*
* @param userId The user id
* @param enabledServices The enabled services
* @param enabledServices The enabled services set
*/
public void onEnabledServicesChangedLocked(int userId,
Set<ComponentName> enabledServices) {
public void onEnabledServicesChanged(int userId, Set<ComponentName> enabledServices) {
mMainHandler.sendMessage(
obtainMessage(this::onEnabledServicesChangedInternal, userId, enabledServices));
}
void onEnabledServicesChangedInternal(int userId, Set<ComponentName> enabledServices) {
final ArraySet<ComponentName> disabledServices = new ArraySet<>(mEnabledA11yServices);
disabledServices.removeAll(enabledServices);
mEnabledA11yServices.clear();
@@ -187,6 +191,18 @@ public class PolicyWarningUIController {
return intent;
}
/**
* Enables to send the notification for non-Accessibility services.
*/
public void enableSendingNonA11yToolNotification(boolean enable) {
mMainHandler.sendMessage(
obtainMessage(this::enableSendingNonA11yToolNotificationInternal, enable));
}
private void enableSendingNonA11yToolNotificationInternal(boolean enable) {
mNotificationController.setSendingNotification(enable);
}
/** A sub class to handle notifications and settings on the main thread. */
@MainThread
public static class NotificationController extends BroadcastReceiver {
@@ -194,22 +210,19 @@ public class PolicyWarningUIController {
/** All accessibility services which are notified to the user by the policy warning rule. */
private final ArraySet<ComponentName> mNotifiedA11yServices = new ArraySet<>();
/** The component name of sent notifications. */
private final List<ComponentName> mSentA11yServiceNotification = new ArrayList<>();
private final NotificationManager mNotificationManager;
private final Context mContext;
private int mCurrentUserId;
private AccessibilitySecurityPolicy mAccessibilitySecurityPolicy;
private boolean mSendNotification;
public NotificationController(Context context) {
mContext = context;
mNotificationManager = mContext.getSystemService(NotificationManager.class);
}
protected void setAccessibilityPolicyManager(
AccessibilitySecurityPolicy accessibilitySecurityPolicy) {
mAccessibilitySecurityPolicy = accessibilitySecurityPolicy;
}
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
@@ -238,15 +251,18 @@ public class PolicyWarningUIController {
}
mNotificationManager.cancel(componentName.flattenToShortString(),
NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
mSentA11yServiceNotification.remove(componentName);
onNotificationCanceled(userId, componentName);
} else if (ACTION_DISMISS_NOTIFICATION.equals(action)) {
mSentA11yServiceNotification.remove(componentName);
onNotificationCanceled(userId, componentName);
}
}
protected void onSwitchUser(int userId) {
mCurrentUserId = userId;
cancelSentNotifications();
mNotifiedA11yServices.clear();
mCurrentUserId = userId;
mNotifiedA11yServices.addAll(readNotifiedServiceList(userId));
}
@@ -258,10 +274,11 @@ public class PolicyWarningUIController {
}
private boolean trySendNotification(int userId, ComponentName componentName) {
if (!AccessibilitySecurityPolicy.POLICY_WARNING_ENABLED) {
if (userId != mCurrentUserId) {
return false;
}
if (userId != mCurrentUserId) {
if (!mSendNotification) {
return false;
}
@@ -344,6 +361,7 @@ public class PolicyWarningUIController {
mNotificationManager.notify(serviceComponentName.flattenToShortString(),
NOTE_A11Y_VIEW_AND_CONTROL_ACCESS,
notificationBuilder.build());
mSentA11yServiceNotification.add(serviceComponentName);
}
private ArraySet<ComponentName> readNotifiedServiceList(int userId) {
@@ -393,5 +411,15 @@ public class PolicyWarningUIController {
return accessibilityManager.getEnabledAccessibilityServiceList(
AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
}
private void cancelSentNotifications() {
mSentA11yServiceNotification.forEach(componentName -> mNotificationManager.cancel(
componentName.flattenToShortString(), NOTE_A11Y_VIEW_AND_CONTROL_ACCESS));
mSentA11yServiceNotification.clear();
}
void setSendingNotification(boolean enable) {
mSendNotification = enable;
}
}
}

View File

@@ -158,6 +158,7 @@ public class AccessibilitySecurityPolicyTest {
mA11ySecurityPolicy = new AccessibilitySecurityPolicy(
mPolicyWarningUIController, mContext, mMockA11yUserManager);
mA11ySecurityPolicy.setSendingNonA11yToolNotificationLocked(true);
mA11ySecurityPolicy.setAccessibilityWindowManager(mMockA11yWindowManager);
mA11ySecurityPolicy.setAppWidgetManager(mMockAppWidgetManager);
mA11ySecurityPolicy.onSwitchUserLocked(TEST_USER_ID, new HashSet<>());
@@ -653,11 +654,18 @@ public class AccessibilitySecurityPolicyTest {
mA11ySecurityPolicy.onSwitchUserLocked(newUserId, new HashSet<>());
verify(mPolicyWarningUIController).onSwitchUserLocked(eq(newUserId), eq(new HashSet<>()));
verify(mPolicyWarningUIController).onSwitchUser(eq(newUserId), eq(new HashSet<>()));
verify(mPolicyWarningUIController).onNonA11yCategoryServiceUnbound(eq(TEST_USER_ID),
eq(TEST_COMPONENT_NAME));
}
@Test
public void enableSendingNonA11yToolNotificationLocked_propagateToPolicyWarningController() {
mA11ySecurityPolicy.setSendingNonA11yToolNotificationLocked(true);
verify(mPolicyWarningUIController).enableSendingNonA11yToolNotification(true);
}
private void initServiceInfoAndConnection(ComponentName componentName,
AccessibilityServiceConnection connection,
boolean isAccessibilityTool) {

View File

@@ -29,6 +29,7 @@ import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -46,12 +47,16 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.testing.TestableContext;
import android.util.ArraySet;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -67,6 +72,8 @@ public class PolicyWarningUIControllerTest {
private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
"com.android.server.accessibility", "PolicyWarningUIControllerTest");
private static final ComponentName TEST_COMPONENT_NAME2 = new ComponentName(
"com.android.server.accessibility", "nonAccessibilityToolService");
private final List<AccessibilityServiceInfo> mEnabledServiceList = new ArrayList<>();
@Rule
@@ -79,15 +86,9 @@ public class PolicyWarningUIControllerTest {
@Mock
private StatusBarManager mStatusBarManager;
@Mock
private AccessibilityServiceInfo mMockA11yServiceInfo;
@Mock
private ResolveInfo mMockResolveInfo;
@Mock
private ServiceInfo mMockServiceInfo;
@Mock
private Context mSpyContext;
@Mock
private AccessibilitySecurityPolicy mAccessibilitySecurityPolicy;
private PolicyWarningUIController mPolicyWarningUIController;
private FakeNotificationController mFakeNotificationController;
@@ -102,21 +103,18 @@ public class PolicyWarningUIControllerTest {
mPolicyWarningUIController = new PolicyWarningUIController(
getInstrumentation().getTargetContext().getMainThreadHandler(), mContext,
mFakeNotificationController);
mPolicyWarningUIController.setAccessibilityPolicyManager(mAccessibilitySecurityPolicy);
mPolicyWarningUIController.onSwitchUserLocked(TEST_USER_ID, new HashSet<>());
mEnabledServiceList.clear();
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES,
"", TEST_USER_ID);
mPolicyWarningUIController.enableSendingNonA11yToolNotification(true);
mPolicyWarningUIController.onSwitchUser(TEST_USER_ID, new HashSet<>());
getInstrumentation().waitForIdleSync();
}
@Test
public void receiveActionSendNotification_isNonA11yCategoryService_sendNotification() {
mEnabledServiceList.add(mMockA11yServiceInfo);
mMockResolveInfo.serviceInfo = mMockServiceInfo;
when(mMockA11yServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo);
when(mMockA11yServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME);
when(mMockA11yServiceInfo.isAccessibilityTool()).thenReturn(false);
addEnabledServiceInfo(TEST_COMPONENT_NAME, false);
mFakeNotificationController.onReceive(mContext,
PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
@@ -127,6 +125,39 @@ public class PolicyWarningUIControllerTest {
eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(Notification.class));
}
@Test
public void receiveActionSendNotification_sendNotificationDisabled_doNothing() {
mPolicyWarningUIController.enableSendingNonA11yToolNotification(false);
addEnabledServiceInfo(TEST_COMPONENT_NAME, false);
mFakeNotificationController.onReceive(mContext,
PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
TEST_COMPONENT_NAME));
verify(mNotificationManager, never()).notify(eq(TEST_COMPONENT_NAME.flattenToShortString()),
eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(Notification.class));
}
@Test
public void receiveActionSendNotificationWithNotifiedService_doNothing() {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.NOTIFIED_NON_ACCESSIBILITY_CATEGORY_SERVICES,
TEST_COMPONENT_NAME.flattenToShortString(), TEST_USER_ID);
mEnabledServiceList.clear();
mPolicyWarningUIController.onSwitchUser(TEST_USER_ID, new HashSet<>());
getInstrumentation().waitForIdleSync();
addEnabledServiceInfo(TEST_COMPONENT_NAME, false);
mFakeNotificationController.onReceive(mContext,
PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
TEST_COMPONENT_NAME));
verify(mNotificationManager, never()).notify(eq(TEST_COMPONENT_NAME.flattenToShortString()),
eq(NOTE_A11Y_VIEW_AND_CONTROL_ACCESS), any(Notification.class));
}
@Test
public void receiveActionA11ySettings_launchA11ySettingsAndDismissNotification() {
mFakeNotificationController.onReceive(mContext,
@@ -154,11 +185,11 @@ public class PolicyWarningUIControllerTest {
public void onEnabledServicesChangedLocked_serviceDisabled_removedFromNotifiedSettings() {
final Set<ComponentName> enabledServices = new HashSet<>();
enabledServices.add(TEST_COMPONENT_NAME);
mPolicyWarningUIController.onEnabledServicesChangedLocked(TEST_USER_ID, enabledServices);
mPolicyWarningUIController.onEnabledServicesChanged(TEST_USER_ID, enabledServices);
getInstrumentation().waitForIdleSync();
receiveActionDismissNotification_addToNotifiedSettings();
mPolicyWarningUIController.onEnabledServicesChangedLocked(TEST_USER_ID, new HashSet<>());
mPolicyWarningUIController.onEnabledServicesChanged(TEST_USER_ID, new HashSet<>());
getInstrumentation().waitForIdleSync();
assertNotifiedSettingsEqual(TEST_USER_ID, "");
@@ -185,6 +216,29 @@ public class PolicyWarningUIControllerTest {
PolicyWarningUIController.ACTION_SEND_NOTIFICATION, TEST_COMPONENT_NAME)));
}
@Test
public void onSwitchUserLocked_hasAlarmAndSentNotification_cancelNotification() {
addEnabledServiceInfo(TEST_COMPONENT_NAME2, false);
final Set<ComponentName> enabledNonA11yServices = new ArraySet<>();
enabledNonA11yServices.add(TEST_COMPONENT_NAME);
enabledNonA11yServices.add(TEST_COMPONENT_NAME2);
mPolicyWarningUIController.onEnabledServicesChanged(TEST_USER_ID,
enabledNonA11yServices);
mPolicyWarningUIController.onNonA11yCategoryServiceBound(TEST_USER_ID, TEST_COMPONENT_NAME);
mFakeNotificationController.onReceive(mContext,
PolicyWarningUIController.createIntent(mContext, TEST_USER_ID,
PolicyWarningUIController.ACTION_SEND_NOTIFICATION,
TEST_COMPONENT_NAME2));
getInstrumentation().waitForIdleSync();
mPolicyWarningUIController.onSwitchUser(TEST_USER_ID,
ImmutableSet.copyOf(new ArraySet<>()));
getInstrumentation().waitForIdleSync();
verify(mNotificationManager).cancel(TEST_COMPONENT_NAME2.flattenToShortString(),
NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
}
private void assertNotifiedSettingsEqual(int userId, String settingString) {
final String notifiedServicesSetting = Settings.Secure.getStringForUser(
mContext.getContentResolver(),
@@ -205,6 +259,17 @@ public class PolicyWarningUIControllerTest {
verify(mStatusBarManager).collapsePanels();
}
private void addEnabledServiceInfo(ComponentName componentName, boolean isAccessibilityTool) {
final AccessibilityServiceInfo a11yServiceInfo = Mockito.mock(
AccessibilityServiceInfo.class);
when(a11yServiceInfo.getComponentName()).thenReturn(componentName);
when(a11yServiceInfo.isAccessibilityTool()).thenReturn(isAccessibilityTool);
final ResolveInfo resolveInfo = Mockito.mock(ResolveInfo.class);
when(a11yServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
resolveInfo.serviceInfo = mMockServiceInfo;
mEnabledServiceList.add(a11yServiceInfo);
}
private class A11yTestableContext extends TestableContext {
A11yTestableContext(Context base) {
super(base);