blob: ed3b3e770338ceb82e1cdd099b5221cfa1f7f062 [file] [log] [blame]
/*
** 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.annotation.Nullable;
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.internal.util.DumpUtils;
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 AbstractAccessibilityServiceConnection.SystemSupport mSystemSupport;
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,
AbstractAccessibilityServiceConnection.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;
}
mSystemSupport = systemSupport;
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;
}
int getRelevantEventTypes() {
if (mUiAutomationService == null) return 0;
return mUiAutomationService.getRelevantEventTypes();
}
@Nullable
AccessibilityServiceInfo getServiceInfo() {
if (mUiAutomationService == null) return null;
return mUiAutomationService.getServiceInfo();
}
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;
}
mSystemSupport.onClientChange(false);
}
private class UiAutomationService extends AbstractAccessibilityServiceConnection {
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;
}
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
synchronized (mLock) {
pw.append("Ui Automation[eventTypes="
+ AccessibilityEvent.eventTypeToString(mEventTypes));
pw.append(", notificationTimeout=" + mNotificationTimeout);
pw.append("]");
}
}
// 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) {}
}
}