| /* |
| ** Copyright 2017, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| package com.android.server.accessibility; |
| |
| import android.accessibilityservice.AccessibilityServiceInfo; |
| import android.accessibilityservice.IAccessibilityServiceClient; |
| import android.app.UiAutomation; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.IBinder.DeathRecipient; |
| import android.os.RemoteException; |
| import android.util.Slog; |
| import android.view.accessibility.AccessibilityEvent; |
| |
| import com.android.server.wm.WindowManagerInternal; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| |
| /** |
| * Class to manage UiAutomation. |
| */ |
| class UiAutomationManager { |
| private static final ComponentName COMPONENT_NAME = |
| new ComponentName("com.android.server.accessibility", "UiAutomation"); |
| private static final String LOG_TAG = "UiAutomationManager"; |
| |
| private UiAutomationService mUiAutomationService; |
| |
| private AccessibilityServiceInfo mUiAutomationServiceInfo; |
| |
| private int mUiAutomationFlags; |
| |
| private IBinder mUiAutomationServiceOwner; |
| private final DeathRecipient mUiAutomationServiceOwnerDeathRecipient = |
| new DeathRecipient() { |
| @Override |
| public void binderDied() { |
| mUiAutomationServiceOwner.unlinkToDeath(this, 0); |
| mUiAutomationServiceOwner = null; |
| if (mUiAutomationService != null) { |
| destroyUiAutomationService(); |
| } |
| } |
| }; |
| |
| /** |
| * Register a UiAutomation. Only one may be registered at a time. |
| * |
| * @param owner A binder object owned by the process that owns the UiAutomation to be |
| * registered. |
| * @param serviceClient The UiAutomation's service interface. |
| * @param accessibilityServiceInfo The UiAutomation's service info |
| * @param flags The UiAutomation's flags |
| * @param id The id for the service connection |
| */ |
| void registerUiTestAutomationServiceLocked(IBinder owner, |
| IAccessibilityServiceClient serviceClient, |
| Context context, AccessibilityServiceInfo accessibilityServiceInfo, |
| int id, Handler mainHandler, Object lock, |
| AccessibilityManagerService.SecurityPolicy securityPolicy, |
| AccessibilityClientConnection.SystemSupport systemSupport, |
| WindowManagerInternal windowManagerInternal, |
| GlobalActionPerformer globalActionPerfomer, int flags) { |
| accessibilityServiceInfo.setComponentName(COMPONENT_NAME); |
| |
| if (mUiAutomationService != null) { |
| throw new IllegalStateException("UiAutomationService " + serviceClient |
| + "already registered!"); |
| } |
| |
| try { |
| owner.linkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0); |
| } catch (RemoteException re) { |
| Slog.e(LOG_TAG, "Couldn't register for the death of a UiTestAutomationService!", re); |
| return; |
| } |
| |
| mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id, |
| mainHandler, lock, securityPolicy, systemSupport, windowManagerInternal, |
| globalActionPerfomer); |
| mUiAutomationServiceOwner = owner; |
| mUiAutomationFlags = flags; |
| mUiAutomationServiceInfo = accessibilityServiceInfo; |
| mUiAutomationService.mServiceInterface = serviceClient; |
| mUiAutomationService.onAdded(); |
| try { |
| mUiAutomationService.mServiceInterface.asBinder().linkToDeath(mUiAutomationService, 0); |
| } catch (RemoteException re) { |
| Slog.e(LOG_TAG, "Failed registering death link: " + re); |
| destroyUiAutomationService(); |
| return; |
| } |
| |
| mUiAutomationService.connectServiceUnknownThread(); |
| } |
| |
| void unregisterUiTestAutomationServiceLocked(IAccessibilityServiceClient serviceClient) { |
| if ((mUiAutomationService == null) |
| || (serviceClient == null) |
| || (mUiAutomationService.mServiceInterface == null) |
| || (serviceClient.asBinder() |
| != mUiAutomationService.mServiceInterface.asBinder())) { |
| throw new IllegalStateException("UiAutomationService " + serviceClient |
| + " not registered!"); |
| } |
| |
| destroyUiAutomationService(); |
| } |
| |
| void sendAccessibilityEventLocked(AccessibilityEvent event) { |
| if (mUiAutomationService != null) { |
| mUiAutomationService.notifyAccessibilityEvent(event); |
| } |
| } |
| |
| boolean isUiAutomationRunningLocked() { |
| return (mUiAutomationService != null); |
| } |
| |
| boolean suppressingAccessibilityServicesLocked() { |
| return (mUiAutomationService != null) && ((mUiAutomationFlags |
| & UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES) == 0); |
| } |
| |
| boolean isTouchExplorationEnabledLocked() { |
| return (mUiAutomationService != null) |
| && mUiAutomationService.mRequestTouchExplorationMode; |
| } |
| |
| boolean canRetrieveInteractiveWindowsLocked() { |
| return (mUiAutomationService != null) && mUiAutomationService.mRetrieveInteractiveWindows; |
| } |
| |
| int getRequestedEventMaskLocked() { |
| if (mUiAutomationService == null) return 0; |
| return mUiAutomationService.mEventTypes; |
| } |
| |
| void dumpUiAutomationService(FileDescriptor fd, final PrintWriter pw, String[] args) { |
| if (mUiAutomationService != null) { |
| mUiAutomationService.dump(fd, pw, args); |
| } |
| } |
| |
| private void destroyUiAutomationService() { |
| mUiAutomationService.mServiceInterface.asBinder().unlinkToDeath(mUiAutomationService, 0); |
| mUiAutomationService.onRemoved(); |
| mUiAutomationService.resetLocked(); |
| mUiAutomationService = null; |
| mUiAutomationFlags = 0; |
| if (mUiAutomationServiceOwner != null) { |
| mUiAutomationServiceOwner.unlinkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0); |
| mUiAutomationServiceOwner = null; |
| } |
| } |
| |
| private class UiAutomationService extends AccessibilityClientConnection { |
| private final Handler mMainHandler; |
| |
| UiAutomationService(Context context, AccessibilityServiceInfo accessibilityServiceInfo, |
| int id, Handler mainHandler, Object lock, |
| AccessibilityManagerService.SecurityPolicy securityPolicy, |
| SystemSupport systemSupport, WindowManagerInternal windowManagerInternal, |
| GlobalActionPerformer globalActionPerfomer) { |
| super(context, COMPONENT_NAME, accessibilityServiceInfo, id, mainHandler, lock, |
| securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer); |
| mMainHandler = mainHandler; |
| } |
| |
| void connectServiceUnknownThread() { |
| // This needs to be done on the main thread |
| mMainHandler.post(() -> { |
| try { |
| final IAccessibilityServiceClient serviceInterface; |
| final IBinder service; |
| synchronized (mLock) { |
| serviceInterface = mServiceInterface; |
| mService = (serviceInterface == null) ? null : mServiceInterface.asBinder(); |
| service = mService; |
| } |
| // If the serviceInterface is null, the UiAutomation has been shut down on |
| // another thread. |
| if (serviceInterface != null) { |
| service.linkToDeath(this, 0); |
| serviceInterface.init(this, mId, mOverlayWindowToken); |
| } |
| } catch (RemoteException re) { |
| Slog.w(LOG_TAG, "Error initialized connection", re); |
| destroyUiAutomationService(); |
| } |
| }); |
| } |
| |
| @Override |
| public void binderDied() { |
| destroyUiAutomationService(); |
| } |
| |
| @Override |
| protected boolean isCalledForCurrentUserLocked() { |
| // Allow UiAutomation to work for any user |
| return true; |
| } |
| |
| @Override |
| protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) { |
| return true; |
| } |
| |
| // Since this isn't really an accessibility service, several methods are just stubbed here. |
| @Override |
| public boolean setSoftKeyboardShowMode(int mode) { |
| return false; |
| } |
| |
| @Override |
| public boolean isAccessibilityButtonAvailable() { |
| return false; |
| } |
| |
| @Override |
| public void disableSelf() {} |
| |
| @Override |
| public void onServiceConnected(ComponentName componentName, IBinder service) {} |
| |
| @Override |
| public void onServiceDisconnected(ComponentName componentName) {} |
| |
| @Override |
| public boolean isCapturingFingerprintGestures() { |
| return false; |
| } |
| |
| @Override |
| public void onFingerprintGestureDetectionActiveChanged(boolean active) {} |
| |
| @Override |
| public void onFingerprintGesture(int gesture) {} |
| } |
| } |