diff options
7 files changed, 79 insertions, 16 deletions
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index 0b10a35b5d5e..452225cd7da0 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -16,6 +16,8 @@ package android.accessibilityservice; +import static android.content.pm.PackageManager.FEATURE_FINGERPRINT; + import android.annotation.IntDef; import android.content.ComponentName; import android.content.Context; @@ -50,8 +52,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static android.content.pm.PackageManager.FEATURE_FINGERPRINT; - /** * This class describes an {@link AccessibilityService}. The system notifies an * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s @@ -410,6 +410,15 @@ public class AccessibilityServiceInfo implements Parcelable { public int flags; /** + * Whether or not the service has crashed and is awaiting restart. Only valid from {@link + * android.view.accessibility.AccessibilityManager#getEnabledAccessibilityServiceList(int)}, + * because that is populated from the internal list of running services. + * + * @hide + */ + public boolean crashed; + + /** * The component name the accessibility service. */ private ComponentName mComponentName; @@ -757,6 +766,7 @@ public class AccessibilityServiceInfo implements Parcelable { parcel.writeInt(feedbackType); parcel.writeLong(notificationTimeout); parcel.writeInt(flags); + parcel.writeInt(crashed ? 1 : 0); parcel.writeParcelable(mComponentName, flagz); parcel.writeParcelable(mResolveInfo, 0); parcel.writeString(mSettingsActivityName); @@ -773,6 +783,7 @@ public class AccessibilityServiceInfo implements Parcelable { feedbackType = parcel.readInt(); notificationTimeout = parcel.readLong(); flags = parcel.readInt(); + crashed = parcel.readInt() != 0; mComponentName = parcel.readParcelable(this.getClass().getClassLoader()); mResolveInfo = parcel.readParcelable(null); mSettingsActivityName = parcel.readString(); diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java index 350b64882ed5..8473c06c1ac7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java @@ -20,6 +20,7 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.content.ComponentName; import android.content.Context; import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.os.UserHandle; @@ -50,6 +51,19 @@ public class AccessibilityUtils { return getEnabledServicesFromSettings(context, UserHandle.myUserId()); } + public static boolean hasServiceCrashed(String packageName, String serviceName, + List<AccessibilityServiceInfo> enabledServiceInfos) { + for (int i = 0; i < enabledServiceInfos.size(); i++) { + AccessibilityServiceInfo accessibilityServiceInfo = enabledServiceInfos.get(i); + final ServiceInfo serviceInfo = enabledServiceInfos.get(i).getResolveInfo().serviceInfo; + if (TextUtils.equals(serviceInfo.packageName, packageName) + && TextUtils.equals(serviceInfo.name, serviceName)) { + return accessibilityServiceInfo.crashed; + } + } + return false; + } + /** * @return the set of enabled accessibility services for {@param userId}. If there are no * services, it returns the unmodifiable {@link Collections#emptySet()}. diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index ed068b931bad..5c5978ad1adc 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -88,7 +88,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int mId; - final AccessibilityServiceInfo mAccessibilityServiceInfo; + protected final AccessibilityServiceInfo mAccessibilityServiceInfo; // Lock must match the one used by AccessibilityManagerService protected final Object mLock; @@ -340,6 +340,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } + public int getCapabilities() { + return mAccessibilityServiceInfo.getCapabilities(); + } + int getRelevantEventTypes() { return (mUsesAccessibilityCache ? AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK : 0) | mEventTypes; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index ecd47e8dd39e..8941b4926584 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -621,7 +621,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub for (int i = 0; i < serviceCount; ++i) { final AccessibilityServiceConnection service = services.get(i); if ((service.mFeedbackType & feedbackType) != 0) { - result.add(service.mAccessibilityServiceInfo); + result.add(service.getServiceInfo()); } } return result; @@ -1874,7 +1874,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { AccessibilityServiceConnection service = userState.mBoundServices.get(i); - if ((service.mAccessibilityServiceInfo.getCapabilities() + if ((service.getCapabilities() & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) { userState.mIsPerformGesturesEnabled = true; return; @@ -1888,7 +1888,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub for (int i = 0; i < serviceCount; i++) { AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (service.mRequestFilterKeyEvents - && (service.mAccessibilityServiceInfo.getCapabilities() + && (service.getCapabilities() & AccessibilityServiceInfo .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) { userState.mIsFilterKeyEventsEnabled = true; @@ -2124,7 +2124,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Starting in JB-MR2 we request an accessibility service to declare // certain capabilities in its meta-data to allow it to enable the // corresponding features. - if ((service.mAccessibilityServiceInfo.getCapabilities() + if ((service.getCapabilities() & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION) != 0) { return true; } @@ -3446,22 +3446,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } public boolean canRetrieveWindowContentLocked(AbstractAccessibilityServiceConnection service) { - return (service.mAccessibilityServiceInfo.getCapabilities() + return (service.getCapabilities() & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0; } public boolean canControlMagnification(AbstractAccessibilityServiceConnection service) { - return (service.mAccessibilityServiceInfo.getCapabilities() + return (service.getCapabilities() & AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION) != 0; } public boolean canPerformGestures(AccessibilityServiceConnection service) { - return (service.mAccessibilityServiceInfo.getCapabilities() + return (service.getCapabilities() & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0; } public boolean canCaptureFingerprintGestures(AccessibilityServiceConnection service) { - return (service.mAccessibilityServiceInfo.getCapabilities() + return (service.getCapabilities() & AccessibilityServiceInfo.CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES) != 0; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 89bf82d6f065..eb18f06baae0 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -165,7 +165,14 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } - public void initializeService() { + @Override + public AccessibilityServiceInfo getServiceInfo() { + // Update crashed data + mAccessibilityServiceInfo.crashed = mWasConnectedAndDied; + return mAccessibilityServiceInfo; + } + + private void initializeService() { IAccessibilityServiceClient serviceInterface = null; synchronized (mLock) { UserState userState = mUserStateWeakReference.get(); diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java index 5b5d18f4aaf7..9f441978f3aa 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java @@ -55,7 +55,7 @@ import java.util.Locale; * magnification region. If a value is out of bounds, it will be adjusted to guarantee these * constraints. */ -class MagnificationController implements Handler.Callback { +public class MagnificationController implements Handler.Callback { private static final boolean DEBUG = false; private static final String LOG_TAG = "MagnificationController"; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index 0462b1430496..e5c6c6e51430 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -16,6 +16,9 @@ package com.android.server.accessibility; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -57,6 +60,7 @@ public class AccessibilityServiceConnectionTest { static final int SERVICE_ID = 42; AccessibilityServiceConnection mConnection; + @Mock AccessibilityManagerService.UserState mMockUserState; @Mock Context mMockContext; @Mock AccessibilityServiceInfo mMockServiceInfo; @@ -66,7 +70,9 @@ public class AccessibilityServiceConnectionTest { @Mock WindowManagerInternal mMockWindowManagerInternal; @Mock GlobalActionPerformer mMockGlobalActionPerformer; @Mock KeyEventDispatcher mMockKeyEventDispatcher; + @Mock MagnificationController mMockMagnificationController; + MessageCapturingHandler mHandler = new MessageCapturingHandler(null); @BeforeClass public static void oneTimeInitialization() { @@ -79,12 +85,15 @@ public class AccessibilityServiceConnectionTest { public void setup() { MockitoAnnotations.initMocks(this); when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher); + when(mMockSystemSupport.getMagnificationController()) + .thenReturn(mMockMagnificationController); + when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo); mMockResolveInfo.serviceInfo = mock(ServiceInfo.class); mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class); mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext, - COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, new Handler(), new Object(), + COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal, mMockGlobalActionPerformer); } @@ -106,12 +115,30 @@ public class AccessibilityServiceConnectionTest { @Test public void bindConnectUnbind_linksAndUnlinksToServiceDeath() throws RemoteException { IBinder mockBinder = mock(IBinder.class); - when(mMockUserState.getBindingServicesLocked()) - .thenReturn(new HashSet<>(Arrays.asList(COMPONENT_NAME))); + setServiceBinding(COMPONENT_NAME); mConnection.bindLocked(); mConnection.onServiceConnected(COMPONENT_NAME, mockBinder); verify(mockBinder).linkToDeath(eq(mConnection), anyInt()); mConnection.unbindLocked(); verify(mockBinder).unlinkToDeath(eq(mConnection), anyInt()); } + + @Test + public void connectedServiceCrashedAndRestarted_crashReportedInServiceInfo() { + IBinder mockBinder = mock(IBinder.class); + setServiceBinding(COMPONENT_NAME); + mConnection.bindLocked(); + mConnection.onServiceConnected(COMPONENT_NAME, mockBinder); + assertFalse(mConnection.getServiceInfo().crashed); + mConnection.binderDied(); + assertTrue(mConnection.getServiceInfo().crashed); + mConnection.onServiceConnected(COMPONENT_NAME, mockBinder); + mHandler.sendAllMessages(); + assertFalse(mConnection.getServiceInfo().crashed); + } + + private void setServiceBinding(ComponentName componentName) { + when(mMockUserState.getBindingServicesLocked()) + .thenReturn(new HashSet<>(Arrays.asList(componentName))); + } } |