Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f3f7a85 authored by Phil Weaver's avatar Phil Weaver Committed by Android (Google) Code Review
Browse files

Merge "Refactoring A11yService and UiAutomation handling"

parents 0c8030df 015847aa
Loading
Loading
Loading
Loading
+1325 −0

File added.

Preview size limit exceeded, changes collapsed.

+344 −1968

File changed.

Preview size limit exceeded, changes collapsed.

+364 −0
Original line number Diff line number Diff line
/*
 ** 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 static android.provider.Settings.Secure.SHOW_MODE_AUTO;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import android.view.WindowManagerInternal;

import com.android.server.accessibility.AccessibilityManagerService.SecurityPolicy;
import com.android.server.accessibility.AccessibilityManagerService.UserState;

import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Set;

/**
 * This class represents an accessibility service. It stores all per service
 * data required for the service management, provides API for starting/stopping the
 * service and is responsible for adding/removing the service in the data structures
 * for service management. The class also exposes configuration interface that is
 * passed to the service it represents as soon it is bound. It also serves as the
 * connection for the service.
 */
class AccessibilityServiceConnection extends AccessibilityClientConnection {
    private static final String LOG_TAG = "AccessibilityServiceConnection";
    /*
     Holding a weak reference so there isn't a loop of references. UserState keeps lists of bound
     and binding services. These are freed on user changes, but just in case it somehow gets lost
     the weak reference will let the memory get GCed.

     Having the reference be null when being called is a very bad sign, but we check the condition.
    */
    final WeakReference<UserState> mUserStateWeakReference;
    final Intent mIntent;

    private final Handler mMainHandler;

    private boolean mWasConnectedAndDied;


    public AccessibilityServiceConnection(UserState userState, Context context,
            ComponentName componentName,
            AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
            Object lock, SecurityPolicy securityPolicy, SystemSupport systemSupport,
            WindowManagerInternal windowManagerInternal,
            GlobalActionPerformer globalActionPerfomer) {
        super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
                securityPolicy, systemSupport, windowManagerInternal, globalActionPerfomer);
        mUserStateWeakReference = new WeakReference<UserState>(userState);
        mIntent = new Intent().setComponent(mComponentName);
        mMainHandler = mainHandler;
        mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
                com.android.internal.R.string.accessibility_binding_label);
        final long identity = Binder.clearCallingIdentity();
        try {
            mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, mSystemSupport.getPendingIntentActivity(
                    mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    public void bindLocked() {
        UserState userState = mUserStateWeakReference.get();
        if (userState == null) return;
        final long identity = Binder.clearCallingIdentity();
        try {
            if (mService == null && mContext.bindServiceAsUser(
                    mIntent, this,
                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                    new UserHandle(userState.mUserId))) {
                userState.getBindingServicesLocked().add(mComponentName);
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    public void unbindLocked() {
        mContext.unbindService(this);
        UserState userState = mUserStateWeakReference.get();
        if (userState == null) return;
        userState.removeServiceLocked(this);
        resetLocked();
    }

    public boolean canRetrieveInteractiveWindowsLocked() {
        return mSecurityPolicy.canRetrieveWindowContentLocked(this) && mRetrieveInteractiveWindows;
    }

    @Override
    public void disableSelf() {
        synchronized (mLock) {
            UserState userState = mUserStateWeakReference.get();
            if (userState == null) return;
            if (userState.mEnabledServices.remove(mComponentName)) {
                final long identity = Binder.clearCallingIdentity();
                try {
                    mSystemSupport.persistComponentNamesToSettingLocked(
                            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                            userState.mEnabledServices, userState.mUserId);
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
                mSystemSupport.onClientChange(false);
            }
        }
    }

    @Override
    public void onServiceConnected(ComponentName componentName, IBinder service) {
        synchronized (mLock) {
            if (mService != service) {
                if (mService != null) {
                    mService.unlinkToDeath(this, 0);
                }
                mService = service;
                try {
                    mService.linkToDeath(this, 0);
                } catch (RemoteException re) {
                    Slog.e(LOG_TAG, "Failed registering death link");
                    binderDied();
                    return;
                }
            }
            mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
            UserState userState = mUserStateWeakReference.get();
            if (userState == null) return;
            userState.addServiceLocked(this);
            mSystemSupport.onClientChange(false);
            // Initialize the service on the main handler after we're done setting up for
            // the new configuration (for example, initializing the input filter).
            mMainHandler.obtainMessage(
                    AccessibilityManagerService.MainHandler.MSG_INIT_SERVICE, this).sendToTarget();
        }
    }

    public void initializeService() {
        IAccessibilityServiceClient serviceInterface = null;
        synchronized (mLock) {
            UserState userState = mUserStateWeakReference.get();
            if (userState == null) return;
            Set<ComponentName> bindingServices = userState.getBindingServicesLocked();
            if (bindingServices.contains(mComponentName) || mWasConnectedAndDied) {
                bindingServices.remove(mComponentName);
                mWasConnectedAndDied = false;
                serviceInterface = mServiceInterface;
            }
        }
        if (serviceInterface == null) {
            binderDied();
            return;
        }
        try {
            serviceInterface.init(this, mId, mOverlayWindowToken);
        } catch (RemoteException re) {
            Slog.w(LOG_TAG, "Error while setting connection for service: "
                    + serviceInterface, re);
            binderDied();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName componentName) {
        binderDied();
    }

    @Override
    protected boolean isCalledForCurrentUserLocked() {
        // We treat calls from a profile as if made by its parent as profiles
        // share the accessibility state of the parent. The call below
        // performs the current profile parent resolution.
        final int resolvedUserId = mSecurityPolicy
                .resolveCallingUserIdEnforcingPermissionsLocked(UserHandle.USER_CURRENT);
        return resolvedUserId == mSystemSupport.getCurrentUserIdLocked();
    }

    @Override
    public boolean setSoftKeyboardShowMode(int showMode) {
        synchronized (mLock) {
            if (!isCalledForCurrentUserLocked()) {
                return false;
            }
        }
        UserState userState = mUserStateWeakReference.get();
        if (userState == null) return false;
        final long identity = Binder.clearCallingIdentity();
        try {
            // Keep track of the last service to request a non-default show mode. The show mode
            // should be restored to default should this service be disabled.
            userState.mServiceChangingSoftKeyboardMode = (showMode == SHOW_MODE_AUTO)
                    ? null : mComponentName;

            Settings.Secure.putIntForUser(mContext.getContentResolver(),
                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, showMode,
                    userState.mUserId);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
        return true;
    }

    @Override
    public boolean isAccessibilityButtonAvailable() {
        synchronized (mLock) {
            if (!isCalledForCurrentUserLocked()) {
                return false;
            }
            UserState userState = mUserStateWeakReference.get();
            return (userState != null) && isAccessibilityButtonAvailableLocked(userState);
        }
    }

    public void binderDied() {
        synchronized (mLock) {
            // It is possible that this service's package was force stopped during
            // whose handling the death recipient is unlinked and still get a call
            // on binderDied since the call was made before we unlink but was
            // waiting on the lock we held during the force stop handling.
            if (!isConnectedLocked()) {
                return;
            }
            mWasConnectedAndDied = true;
            resetLocked();
            if (mId == mSystemSupport.getMagnificationController().getIdOfLastServiceToMagnify()) {
                mSystemSupport.getMagnificationController().resetIfNeeded(true);
            }
            mSystemSupport.onClientChange(false);
        }
    }

    public boolean isAccessibilityButtonAvailableLocked(UserState userState) {
        // If the service does not request the accessibility button, it isn't available
        if (!mRequestAccessibilityButton) {
            return false;
        }

        // If the accessibility button isn't currently shown, it cannot be available to services
        if (!mSystemSupport.isAccessibilityButtonShown()) {
            return false;
        }

        // If magnification is on and assigned to the accessibility button, services cannot be
        if (userState.mIsNavBarMagnificationEnabled
                && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
            return false;
        }

        int requestingServices = 0;
        for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
            final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
            if (service.mRequestAccessibilityButton) {
                requestingServices++;
            }
        }

        if (requestingServices == 1) {
            // If only a single service is requesting, it must be this service, and the
            // accessibility button is available to it
            return true;
        } else {
            // With more than one active service, we derive the target from the user's settings
            if (userState.mServiceAssignedToAccessibilityButton == null) {
                // If the user has not made an assignment, we treat the button as available to
                // all services until the user interacts with the button to make an assignment
                return true;
            } else {
                // If an assignment was made, it defines availability
                return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton);
            }
        }
    }

    @Override
    public boolean isCapturingFingerprintGestures() {
        return (mServiceInterface != null)
                && mSecurityPolicy.canCaptureFingerprintGestures(this)
                && mCaptureFingerprintGestures;
    }

    @Override
    public void onFingerprintGestureDetectionActiveChanged(boolean active) {
        if (!isCapturingFingerprintGestures()) {
            return;
        }
        IAccessibilityServiceClient serviceInterface;
        synchronized (mLock) {
            serviceInterface = mServiceInterface;
        }
        if (serviceInterface != null) {
            try {
                mServiceInterface.onFingerprintCapturingGesturesChanged(active);
            } catch (RemoteException e) {
            }
        }
    }

    @Override
    public void onFingerprintGesture(int gesture) {
        if (!isCapturingFingerprintGestures()) {
            return;
        }
        IAccessibilityServiceClient serviceInterface;
        synchronized (mLock) {
            serviceInterface = mServiceInterface;
        }
        if (serviceInterface != null) {
            try {
                mServiceInterface.onFingerprintGesture(gesture);
            } catch (RemoteException e) {
            }
        }
    }

    @Override
    public void sendGesture(int sequence, ParceledListSlice gestureSteps) {
        synchronized (mLock) {
            if (mSecurityPolicy.canPerformGestures(this)) {
                MotionEventInjector motionEventInjector =
                        mSystemSupport.getMotionEventInjectorLocked();
                if (motionEventInjector != null) {
                    motionEventInjector.injectEvents(
                            gestureSteps.getList(), mServiceInterface, sequence);
                } else {
                    try {
                        mServiceInterface.onPerformGestureResult(sequence, false);
                    } catch (RemoteException re) {
                        Slog.e(LOG_TAG, "Error sending motion event injection failure to "
                                + mServiceInterface, re);
                    }
                }
            }
        }
    }
}
+156 −0
Original line number Diff line number Diff line
/*
 ** 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.AccessibilityService;
import android.app.StatusBarManager;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Binder;
import android.os.SystemClock;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.WindowManagerInternal;

import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;

/**
 * Handle the back-end of AccessibilityService#performGlobalAction
 */
public class GlobalActionPerformer {
    private final WindowManagerInternal mWindowManagerService;
    private final Context mContext;

    public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
        mContext = context;
        mWindowManagerService = windowManagerInternal;
    }

    public boolean performGlobalAction(int action) {
        final long identity = Binder.clearCallingIdentity();
        try {
            switch (action) {
                case AccessibilityService.GLOBAL_ACTION_BACK: {
                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
                }
                return true;
                case AccessibilityService.GLOBAL_ACTION_HOME: {
                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
                }
                return true;
                case AccessibilityService.GLOBAL_ACTION_RECENTS: {
                    return openRecents();
                }
                case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
                    expandNotifications();
                }
                return true;
                case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: {
                    expandQuickSettings();
                }
                return true;
                case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
                    showGlobalActions();
                }
                return true;
                case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: {
                    return toggleSplitScreen();
                }
            }
            return false;
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    private void sendDownAndUpKeyEvents(int keyCode) {
        final long token = Binder.clearCallingIdentity();

        // Inject down.
        final long downTime = SystemClock.uptimeMillis();
        sendKeyEventIdentityCleared(keyCode, KeyEvent.ACTION_DOWN, downTime, downTime);
        sendKeyEventIdentityCleared(
                keyCode, KeyEvent.ACTION_UP, downTime, SystemClock.uptimeMillis());

        Binder.restoreCallingIdentity(token);
    }

    private void sendKeyEventIdentityCleared(int keyCode, int action, long downTime, long time) {
        KeyEvent event = KeyEvent.obtain(downTime, time, action, keyCode, 0, 0,
                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FROM_SYSTEM,
                InputDevice.SOURCE_KEYBOARD, null);
        InputManager.getInstance()
                .injectInputEvent(event, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
        event.recycle();
    }

    private void expandNotifications() {
        final long token = Binder.clearCallingIdentity();

        StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
                android.app.Service.STATUS_BAR_SERVICE);
        statusBarManager.expandNotificationsPanel();

        Binder.restoreCallingIdentity(token);
    }

    private void expandQuickSettings() {
        final long token = Binder.clearCallingIdentity();

        StatusBarManager statusBarManager = (StatusBarManager) mContext.getSystemService(
                android.app.Service.STATUS_BAR_SERVICE);
        statusBarManager.expandSettingsPanel();

        Binder.restoreCallingIdentity(token);
    }

    private boolean openRecents() {
        final long token = Binder.clearCallingIdentity();
        try {
            StatusBarManagerInternal statusBarService = LocalServices.getService(
                    StatusBarManagerInternal.class);
            if (statusBarService == null) {
                return false;
            }
            statusBarService.toggleRecentApps();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        return true;
    }

    private void showGlobalActions() {
        mWindowManagerService.showGlobalActions();
    }

    private boolean toggleSplitScreen() {
        final long token = Binder.clearCallingIdentity();
        try {
            StatusBarManagerInternal statusBarService = LocalServices.getService(
                    StatusBarManagerInternal.class);
            if (statusBarService == null) {
                return false;
            }
            statusBarService.toggleSplitScreen();
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        return true;
    }
}
+244 −0
Original line number Diff line number Diff line
/*
 ** 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.WindowManagerInternal;
import android.view.accessibility.AccessibilityEvent;

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 mUiAutomationSerivceOwnerDeathRecipient =
            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(mUiAutomationSerivceOwnerDeathRecipient, 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(mUiAutomationSerivceOwnerDeathRecipient, 0);
            mUiAutomationServiceOwner = null;
        }
    }

    private class UiAutomationService extends AccessibilityClientConnection {
        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);
        }

        void connectServiceUnknownThread() {
            // This needs to be done on the main thread
            mEventDispatchHandler.post(() -> {
                try {
                    mService = mServiceInterface.asBinder();
                    mService.linkToDeath(this, 0);
                    mServiceInterface.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) {}
    }
}
Loading