blob: 68ee78076f3d59687aa1c4240d4e7f0090db882e [file] [log] [blame]
/*
* Copyright (C) 2019 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.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
import static android.view.accessibility.AccessibilityManager.ShortcutType;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteCallbackList;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Class that hold states and settings per user and share between
* {@link AccessibilityManagerService} and {@link AccessibilityServiceConnection}.
*/
class AccessibilityUserState {
private static final String LOG_TAG = AccessibilityUserState.class.getSimpleName();
final int mUserId;
// Non-transient state.
final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = new RemoteCallbackList<>();
// Transient state.
final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
new HashMap<>();
final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<>();
final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>();
final Set<ComponentName> mBindingServices = new HashSet<>();
final Set<ComponentName> mCrashedServices = new HashSet<>();
final Set<ComponentName> mEnabledServices = new HashSet<>();
final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>();
final ArraySet<String> mAccessibilityShortcutKeyTargets = new ArraySet<>();
final ArraySet<String> mAccessibilityButtonTargets = new ArraySet<>();
private final ServiceInfoChangeListener mServiceInfoChangeListener;
private ComponentName mServiceChangingSoftKeyboardMode;
private String mTargetAssignedToAccessibilityButton;
private boolean mBindInstantServiceAllowed;
private boolean mIsAudioDescriptionByDefaultRequested;
private boolean mIsAutoclickEnabled;
private boolean mIsMagnificationSingleFingerTripleTapEnabled;
private boolean mMagnificationTwoFingerTripleTapEnabled;
private boolean mIsFilterKeyEventsEnabled;
private boolean mIsPerformGesturesEnabled;
private boolean mAccessibilityFocusOnlyInActiveWindow;
private boolean mIsTextHighContrastEnabled;
private boolean mIsTouchExplorationEnabled;
private boolean mServiceHandlesDoubleTap;
private boolean mRequestMultiFingerGestures;
private boolean mRequestTwoFingerPassthrough;
private boolean mSendMotionEventsEnabled;
private SparseArray<Boolean> mServiceDetectsGestures = new SparseArray<>(0);
private int mUserInteractiveUiTimeout;
private int mUserNonInteractiveUiTimeout;
private int mNonInteractiveUiTimeout = 0;
private int mInteractiveUiTimeout = 0;
private int mLastSentClientState = -1;
/** {@code true} if the device config supports window magnification. */
private final boolean mSupportWindowMagnification;
// The magnification modes on displays.
private final SparseIntArray mMagnificationModes = new SparseIntArray();
// The magnification capabilities used to know magnification mode could be switched.
private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
// Whether the following typing focus feature for magnification is enabled.
private boolean mMagnificationFollowTypingEnabled = true;
// Whether the always on magnification feature is enabled.
private boolean mAlwaysOnMagnificationEnabled = false;
/** The stroke width of the focus rectangle in pixels */
private int mFocusStrokeWidth;
/** The color of the focus rectangle */
private int mFocusColor;
// The default value of the focus stroke width.
private final int mFocusStrokeWidthDefaultValue;
// The default value of the focus color.
private final int mFocusColorDefaultValue;
private Context mContext;
@SoftKeyboardShowMode
private int mSoftKeyboardShowMode = SHOW_MODE_AUTO;
boolean isValidMagnificationModeLocked(int displayId) {
final int mode = getMagnificationModeLocked(displayId);
if (!mSupportWindowMagnification
&& mode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) {
return false;
}
return (mMagnificationCapabilities & mode) != 0;
}
interface ServiceInfoChangeListener {
void onServiceInfoChangedLocked(AccessibilityUserState userState);
}
AccessibilityUserState(int userId, @NonNull Context context,
@NonNull ServiceInfoChangeListener serviceInfoChangeListener) {
mUserId = userId;
mContext = context;
mServiceInfoChangeListener = serviceInfoChangeListener;
mFocusStrokeWidthDefaultValue = mContext.getResources().getDimensionPixelSize(
R.dimen.accessibility_focus_highlight_stroke_width);
mFocusColorDefaultValue = mContext.getResources().getColor(
R.color.accessibility_focus_highlight_color);
mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
mFocusColor = mFocusColorDefaultValue;
mSupportWindowMagnification = mContext.getResources().getBoolean(
R.bool.config_magnification_area) && mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WINDOW_MAGNIFICATION);
}
boolean isHandlingAccessibilityEventsLocked() {
return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
}
void onSwitchToAnotherUserLocked() {
// Unbind all services.
unbindAllServicesLocked();
// Clear service management state.
mBoundServices.clear();
mBindingServices.clear();
mCrashedServices.clear();
// Clear event management state.
mLastSentClientState = -1;
// clear UI timeout
mNonInteractiveUiTimeout = 0;
mInteractiveUiTimeout = 0;
// Clear state persisted in settings.
mEnabledServices.clear();
mTouchExplorationGrantedServices.clear();
mAccessibilityShortcutKeyTargets.clear();
mAccessibilityButtonTargets.clear();
mTargetAssignedToAccessibilityButton = null;
mIsTouchExplorationEnabled = false;
mServiceHandlesDoubleTap = false;
mRequestMultiFingerGestures = false;
mRequestTwoFingerPassthrough = false;
mSendMotionEventsEnabled = false;
mIsMagnificationSingleFingerTripleTapEnabled = false;
mMagnificationTwoFingerTripleTapEnabled = false;
mIsAutoclickEnabled = false;
mUserNonInteractiveUiTimeout = 0;
mUserInteractiveUiTimeout = 0;
mMagnificationModes.clear();
mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
mFocusColor = mFocusColorDefaultValue;
mMagnificationFollowTypingEnabled = true;
mAlwaysOnMagnificationEnabled = false;
}
void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
if (!mBoundServices.contains(serviceConnection)) {
if (!Flags.addWindowTokenWithoutLock()) {
serviceConnection.addWindowTokensForAllDisplays();
}
mBoundServices.add(serviceConnection);
mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
}
}
/**
* Removes a service.
* There are three states to a service here: off, bound, and binding.
* This stops tracking the service as bound.
*
* @param serviceConnection The service.
*/
void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
mBoundServices.remove(serviceConnection);
serviceConnection.onRemoved();
if ((mServiceChangingSoftKeyboardMode != null)
&& (mServiceChangingSoftKeyboardMode.equals(
serviceConnection.getServiceInfo().getComponentName()))) {
setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
}
// It may be possible to bind a service twice, which confuses the map. Rebuild the map
// to make sure we can still reach a service
mComponentNameToServiceMap.clear();
for (int i = 0; i < mBoundServices.size(); i++) {
AccessibilityServiceConnection boundClient = mBoundServices.get(i);
mComponentNameToServiceMap.put(boundClient.getComponentName(), boundClient);
}
mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
}
/**
* Make sure a services disconnected but still 'on' state is reflected in AccessibilityUserState
* There are four states to a service here: off, bound, and binding, and crashed.
* This drops a service from a bound state, to the crashed state.
* The crashed state describes the situation where a service used to be bound, but no longer is
* despite still being enabled.
*
* @param serviceConnection The service.
*/
void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
removeServiceLocked(serviceConnection);
mCrashedServices.add(serviceConnection.getComponentName());
}
/**
* Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
* The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
* to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
* setting can be changed by the user, and prevents the system from suppressing the soft
* keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
* to the user's preference, if they have supplied one.
*
* @param newMode The new mode
* @param requester The service requesting the change, so we can undo it when the
* service stops. Set to null if something other than a service is forcing
* the change.
*
* @return Whether or not the soft keyboard mode equals the new mode after the call
*/
boolean setSoftKeyboardModeLocked(@SoftKeyboardShowMode int newMode,
@Nullable ComponentName requester) {
if ((newMode != SHOW_MODE_AUTO)
&& (newMode != SHOW_MODE_HIDDEN)
&& (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD)) {
Slog.w(LOG_TAG, "Invalid soft keyboard mode");
return false;
}
if (mSoftKeyboardShowMode == newMode) {
return true;
}
if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
if (hasUserOverriddenHardKeyboardSetting()) {
// The user has specified a default for this setting
return false;
}
// Save the original value. But don't do this if the value in settings is already
// the new mode. That happens when we start up after a reboot, and we don't want
// to overwrite the value we had from when we first started controlling the setting.
if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
setOriginalHardKeyboardValue(getSecureIntForUser(
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0);
}
putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
} else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
}
saveSoftKeyboardValueToSettings(newMode);
mSoftKeyboardShowMode = newMode;
mServiceChangingSoftKeyboardMode = requester;
for (int i = mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = mBoundServices.get(i);
service.notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
}
return true;
}
@SoftKeyboardShowMode
int getSoftKeyboardShowModeLocked() {
return mSoftKeyboardShowMode;
}
/**
* If the settings are inconsistent with the internal state, make the internal state
* match the settings.
*/
void reconcileSoftKeyboardModeWithSettingsLocked() {
final boolean showWithHardKeyboardSettings =
getSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0;
if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
if (!showWithHardKeyboardSettings) {
// The user has overridden the setting. Respect that and prevent further changes
// to this behavior.
setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
setUserOverridesHardKeyboardSetting();
}
}
// If the setting and the internal state are out of sync, set both to default
if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode) {
Slog.e(LOG_TAG, "Show IME setting inconsistent with internal state. Overwriting");
setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
SHOW_MODE_AUTO, mUserId);
}
}
boolean getBindInstantServiceAllowedLocked() {
return mBindInstantServiceAllowed;
}
/* Need to have a permission check on callee */
void setBindInstantServiceAllowedLocked(boolean allowed) {
mBindInstantServiceAllowed = allowed;
}
/**
* Returns binding service list.
*/
Set<ComponentName> getBindingServicesLocked() {
return mBindingServices;
}
/**
* Returns crashed service list.
*/
Set<ComponentName> getCrashedServicesLocked() {
return mCrashedServices;
}
/**
* Returns enabled service list.
*/
Set<ComponentName> getEnabledServicesLocked() {
return mEnabledServices;
}
/**
* Remove the service from the crashed and binding service lists if the user disabled it.
*/
void removeDisabledServicesFromTemporaryStatesLocked() {
for (int i = 0, count = mInstalledServices.size(); i < count; i++) {
final AccessibilityServiceInfo installedService = mInstalledServices.get(i);
final ComponentName componentName = ComponentName.unflattenFromString(
installedService.getId());
if (!mEnabledServices.contains(componentName)) {
// Remove from mCrashedServices, since users may toggle the on/off switch to retry.
mCrashedServices.remove(componentName);
// Remove from mBindingServices, since services can get stuck in the binding state
// if binding starts but never finishes. If the service later attempts to finish
// binding but it is not in the enabled list then it will exit before initializing;
// see AccessibilityServiceConnection#initializeService().
mBindingServices.remove(componentName);
}
}
}
List<AccessibilityServiceConnection> getBoundServicesLocked() {
return mBoundServices;
}
int getClientStateLocked(boolean uiAutomationCanIntrospect,
int traceClientState) {
int clientState = 0;
final boolean a11yEnabled = uiAutomationCanIntrospect
|| isHandlingAccessibilityEventsLocked();
if (a11yEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
}
// Touch exploration relies on enabled accessibility.
if (a11yEnabled && mIsTouchExplorationEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
clientState |= AccessibilityManager.STATE_FLAG_DISPATCH_DOUBLE_TAP;
clientState |= AccessibilityManager.STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES;
}
if (mIsTextHighContrastEnabled) {
clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
}
if (mIsAudioDescriptionByDefaultRequested) {
clientState |=
AccessibilityManager.STATE_FLAG_AUDIO_DESCRIPTION_BY_DEFAULT_ENABLED;
}
clientState |= traceClientState;
return clientState;
}
private void setUserOverridesHardKeyboardSetting() {
final int softKeyboardSetting = getSecureIntForUser(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
mUserId);
}
private boolean hasUserOverriddenHardKeyboardSetting() {
final int softKeyboardSetting = getSecureIntForUser(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
!= 0;
}
private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
final int oldSoftKeyboardSetting = getSecureIntForUser(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
final int newSoftKeyboardSetting = oldSoftKeyboardSetting
& (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
| ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
newSoftKeyboardSetting, mUserId);
}
private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
final int oldSoftKeyboardSetting = getSecureIntForUser(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
| softKeyboardShowMode;
putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
newSoftKeyboardSetting, mUserId);
}
private int getSoftKeyboardValueFromSettings() {
return getSecureIntForUser(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
& SHOW_MODE_MASK;
}
private boolean getOriginalHardKeyboardValue() {
return (getSecureIntForUser(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
& SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
}
private void unbindAllServicesLocked() {
final List<AccessibilityServiceConnection> services = mBoundServices;
for (int count = services.size(); count > 0; count--) {
// When the service is unbound, it disappears from the list, so there's no need to
// keep track of the index
services.get(0).unbindLocked();
}
}
private int getSecureIntForUser(String key, int def, int userId) {
return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId);
}
private void putSecureIntForUser(String key, int value, int userId) {
final long identity = Binder.clearCallingIdentity();
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.append("User state[");
pw.println();
pw.append(" attributes:{id=").append(String.valueOf(mUserId));
pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled));
pw.append(", serviceHandlesDoubleTap=")
.append(String.valueOf(mServiceHandlesDoubleTap));
pw.append(", requestMultiFingerGestures=")
.append(String.valueOf(mRequestMultiFingerGestures));
pw.append(", requestTwoFingerPassthrough=")
.append(String.valueOf(mRequestTwoFingerPassthrough));
pw.append(", sendMotionEventsEnabled").append(String.valueOf(mSendMotionEventsEnabled));
pw.append(", displayMagnificationEnabled=").append(String.valueOf(
mIsMagnificationSingleFingerTripleTapEnabled));
pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size()));
pw.append(", magnificationModes=").append(String.valueOf(mMagnificationModes));
pw.append(", magnificationCapabilities=")
.append(String.valueOf(mMagnificationCapabilities));
pw.append(", audioDescriptionByDefaultEnabled=")
.append(String.valueOf(mIsAudioDescriptionByDefaultRequested));
pw.append(", magnificationFollowTypingEnabled=")
.append(String.valueOf(mMagnificationFollowTypingEnabled));
pw.append(", alwaysOnMagnificationEnabled=")
.append(String.valueOf(mAlwaysOnMagnificationEnabled));
pw.append("}");
pw.println();
pw.append(" shortcut key:{");
int size = mAccessibilityShortcutKeyTargets.size();
for (int i = 0; i < size; i++) {
final String componentId = mAccessibilityShortcutKeyTargets.valueAt(i);
pw.append(componentId);
if (i + 1 < size) {
pw.append(", ");
}
}
pw.println("}");
pw.append(" button:{");
size = mAccessibilityButtonTargets.size();
for (int i = 0; i < size; i++) {
final String componentId = mAccessibilityButtonTargets.valueAt(i);
pw.append(componentId);
if (i + 1 < size) {
pw.append(", ");
}
}
pw.println("}");
pw.append(" button target:{").append(mTargetAssignedToAccessibilityButton);
pw.println("}");
pw.append(" Bound services:{");
final int serviceCount = mBoundServices.size();
for (int j = 0; j < serviceCount; j++) {
if (j > 0) {
pw.append(", ");
pw.println();
pw.append(" ");
}
AccessibilityServiceConnection service = mBoundServices.get(j);
service.dump(fd, pw, args);
}
pw.println("}");
pw.append(" Enabled services:{");
Iterator<ComponentName> it = mEnabledServices.iterator();
if (it.hasNext()) {
ComponentName componentName = it.next();
pw.append(componentName.toShortString());
while (it.hasNext()) {
componentName = it.next();
pw.append(", ");
pw.append(componentName.toShortString());
}
}
pw.println("}");
pw.append(" Binding services:{");
it = mBindingServices.iterator();
if (it.hasNext()) {
ComponentName componentName = it.next();
pw.append(componentName.toShortString());
while (it.hasNext()) {
componentName = it.next();
pw.append(", ");
pw.append(componentName.toShortString());
}
}
pw.println("}");
pw.append(" Crashed services:{");
it = mCrashedServices.iterator();
if (it.hasNext()) {
ComponentName componentName = it.next();
pw.append(componentName.toShortString());
while (it.hasNext()) {
componentName = it.next();
pw.append(", ");
pw.append(componentName.toShortString());
}
}
pw.println("}");
pw.println(" Client list info:{");
mUserClients.dump(pw, " Client list ");
pw.println(" Registered clients:{");
for (int i = 0; i < mUserClients.getRegisteredCallbackCount(); i++) {
AccessibilityManagerService.Client client = (AccessibilityManagerService.Client)
mUserClients.getRegisteredCallbackCookie(i);
pw.append(Arrays.toString(client.mPackageNames));
}
pw.println("}]");
}
public boolean isAutoclickEnabledLocked() {
return mIsAutoclickEnabled;
}
public void setAutoclickEnabledLocked(boolean enabled) {
mIsAutoclickEnabled = enabled;
}
public boolean isMagnificationSingleFingerTripleTapEnabledLocked() {
return mIsMagnificationSingleFingerTripleTapEnabled;
}
public void setMagnificationSingleFingerTripleTapEnabledLocked(boolean enabled) {
mIsMagnificationSingleFingerTripleTapEnabled = enabled;
}
public boolean isMagnificationTwoFingerTripleTapEnabledLocked() {
return mMagnificationTwoFingerTripleTapEnabled;
}
public void setMagnificationTwoFingerTripleTapEnabledLocked(boolean enabled) {
mMagnificationTwoFingerTripleTapEnabled = enabled;
}
public boolean isFilterKeyEventsEnabledLocked() {
return mIsFilterKeyEventsEnabled;
}
public void setFilterKeyEventsEnabledLocked(boolean enabled) {
mIsFilterKeyEventsEnabled = enabled;
}
public int getInteractiveUiTimeoutLocked() {
return mInteractiveUiTimeout;
}
public void setInteractiveUiTimeoutLocked(int timeout) {
mInteractiveUiTimeout = timeout;
}
public int getLastSentClientStateLocked() {
return mLastSentClientState;
}
public void setLastSentClientStateLocked(int state) {
mLastSentClientState = state;
}
/**
* Returns true if navibar magnification or shortcut key magnification is enabled.
*/
public boolean isShortcutMagnificationEnabledLocked() {
return mAccessibilityShortcutKeyTargets.contains(MAGNIFICATION_CONTROLLER_NAME)
|| mAccessibilityButtonTargets.contains(MAGNIFICATION_CONTROLLER_NAME);
}
/**
* Gets the magnification mode for the given display.
* @return magnification mode
*
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
*/
public int getMagnificationModeLocked(int displayId) {
int mode = mMagnificationModes.get(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_NONE);
if (mode == ACCESSIBILITY_MAGNIFICATION_MODE_NONE) {
mode = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
setMagnificationModeLocked(displayId, mode);
}
return mode;
}
/**
* Gets the magnification capabilities setting of current user.
*
* @return magnification capabilities
*
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL
*/
int getMagnificationCapabilitiesLocked() {
return mMagnificationCapabilities;
}
/**
* Sets the magnification capabilities from Settings value.
*
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
* @see Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_ALL
*/
public void setMagnificationCapabilitiesLocked(int capabilities) {
mMagnificationCapabilities = capabilities;
}
public void setMagnificationFollowTypingEnabled(boolean enabled) {
mMagnificationFollowTypingEnabled = enabled;
}
public boolean isMagnificationFollowTypingEnabled() {
return mMagnificationFollowTypingEnabled;
}
public void setAlwaysOnMagnificationEnabled(boolean enabled) {
mAlwaysOnMagnificationEnabled = enabled;
}
public boolean isAlwaysOnMagnificationEnabled() {
return mAlwaysOnMagnificationEnabled;
}
/**
* Sets the magnification mode to the given display.
*
* @param displayId The display id.
* @param mode The magnification mode.
*/
public void setMagnificationModeLocked(int displayId, int mode) {
mMagnificationModes.put(displayId, mode);
}
/**
* Disable both shortcuts' magnification function.
*/
public void disableShortcutMagnificationLocked() {
mAccessibilityShortcutKeyTargets.remove(MAGNIFICATION_CONTROLLER_NAME);
mAccessibilityButtonTargets.remove(MAGNIFICATION_CONTROLLER_NAME);
}
/**
* Returns a set which contains the flattened component names and the system class names
* assigned to the given shortcut.
*
* @param shortcutType The shortcut type.
* @return The array set of the strings
*/
public ArraySet<String> getShortcutTargetsLocked(@ShortcutType int shortcutType) {
if (shortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
return mAccessibilityShortcutKeyTargets;
} else if (shortcutType == ACCESSIBILITY_BUTTON) {
return mAccessibilityButtonTargets;
}
return null;
}
/**
* Whether or not the given shortcut target is installed in device.
*
* @param name The shortcut target name
* @return true if the shortcut target is installed.
*/
public boolean isShortcutTargetInstalledLocked(String name) {
if (TextUtils.isEmpty(name)) {
return false;
}
if (MAGNIFICATION_CONTROLLER_NAME.equals(name)) {
return true;
}
final ComponentName componentName = ComponentName.unflattenFromString(name);
if (componentName == null) {
return false;
}
if (AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
.containsKey(componentName)) {
return true;
}
if (getInstalledServiceInfoLocked(componentName) != null) {
return true;
}
for (int i = 0; i < mInstalledShortcuts.size(); i++) {
if (mInstalledShortcuts.get(i).getComponentName().equals(componentName)) {
return true;
}
}
return false;
}
/**
* Removes given shortcut target in the list.
*
* @param shortcutType The shortcut type.
* @param target The component name of the shortcut target.
* @return true if the shortcut target is removed.
*/
public boolean removeShortcutTargetLocked(@ShortcutType int shortcutType,
ComponentName target) {
return getShortcutTargetsLocked(shortcutType).removeIf(name -> {
ComponentName componentName;
if (name == null
|| (componentName = ComponentName.unflattenFromString(name)) == null) {
return false;
}
return componentName.equals(target);
});
}
/**
* Returns installed accessibility service info by the given service component name.
*/
public AccessibilityServiceInfo getInstalledServiceInfoLocked(ComponentName componentName) {
for (int i = 0; i < mInstalledServices.size(); i++) {
final AccessibilityServiceInfo serviceInfo = mInstalledServices.get(i);
if (serviceInfo.getComponentName().equals(componentName)) {
return serviceInfo;
}
}
return null;
}
/**
* Returns accessibility service connection by the given service component name.
*/
public AccessibilityServiceConnection getServiceConnectionLocked(ComponentName componentName) {
return mComponentNameToServiceMap.get(componentName);
}
public int getNonInteractiveUiTimeoutLocked() {
return mNonInteractiveUiTimeout;
}
public void setNonInteractiveUiTimeoutLocked(int timeout) {
mNonInteractiveUiTimeout = timeout;
}
public boolean isPerformGesturesEnabledLocked() {
return mIsPerformGesturesEnabled;
}
public void setPerformGesturesEnabledLocked(boolean enabled) {
mIsPerformGesturesEnabled = enabled;
}
public boolean isAccessibilityFocusOnlyInActiveWindow() {
return mAccessibilityFocusOnlyInActiveWindow;
}
public void setAccessibilityFocusOnlyInActiveWindow(boolean enabled) {
mAccessibilityFocusOnlyInActiveWindow = enabled;
}
public ComponentName getServiceChangingSoftKeyboardModeLocked() {
return mServiceChangingSoftKeyboardMode;
}
public void setServiceChangingSoftKeyboardModeLocked(
ComponentName serviceChangingSoftKeyboardMode) {
mServiceChangingSoftKeyboardMode = serviceChangingSoftKeyboardMode;
}
public boolean isTextHighContrastEnabledLocked() {
return mIsTextHighContrastEnabled;
}
public void setTextHighContrastEnabledLocked(boolean enabled) {
mIsTextHighContrastEnabled = enabled;
}
public boolean isAudioDescriptionByDefaultEnabledLocked() {
return mIsAudioDescriptionByDefaultRequested;
}
public void setAudioDescriptionByDefaultEnabledLocked(boolean enabled) {
mIsAudioDescriptionByDefaultRequested = enabled;
}
public boolean isTouchExplorationEnabledLocked() {
return mIsTouchExplorationEnabled;
}
public void setTouchExplorationEnabledLocked(boolean enabled) {
mIsTouchExplorationEnabled = enabled;
}
public boolean isServiceHandlesDoubleTapEnabledLocked() {
return mServiceHandlesDoubleTap;
}
public void setServiceHandlesDoubleTapLocked(boolean enabled) {
mServiceHandlesDoubleTap = enabled;
}
public boolean isMultiFingerGesturesEnabledLocked() {
return mRequestMultiFingerGestures;
}
public void setMultiFingerGesturesLocked(boolean enabled) {
mRequestMultiFingerGestures = enabled;
}
public boolean isTwoFingerPassthroughEnabledLocked() {
return mRequestTwoFingerPassthrough;
}
public void setTwoFingerPassthroughLocked(boolean enabled) {
mRequestTwoFingerPassthrough = enabled;
}
public boolean isSendMotionEventsEnabled() {
return mSendMotionEventsEnabled;
}
public void setSendMotionEventsEnabled(boolean mode) {
mSendMotionEventsEnabled = mode;
}
public int getUserInteractiveUiTimeoutLocked() {
return mUserInteractiveUiTimeout;
}
public void setUserInteractiveUiTimeoutLocked(int timeout) {
mUserInteractiveUiTimeout = timeout;
}
public int getUserNonInteractiveUiTimeoutLocked() {
return mUserNonInteractiveUiTimeout;
}
public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
mUserNonInteractiveUiTimeout = timeout;
}
/**
* Gets a shortcut target which is assigned to the accessibility button by the chooser
* activity.
*
* @return The flattened component name or the system class name of the shortcut target.
*/
public String getTargetAssignedToAccessibilityButton() {
return mTargetAssignedToAccessibilityButton;
}
/**
* Sets a shortcut target which is assigned to the accessibility button by the chooser
* activity.
*
* @param target The flattened component name or the system class name of the shortcut target.
*/
public void setTargetAssignedToAccessibilityButton(String target) {
mTargetAssignedToAccessibilityButton = target;
}
/**
* Whether or not the given target name is contained in the shortcut collection. Since the
* component name string format could be short or long, this function un-flatten the component
* name from the string in {@code shortcutTargets} and compared with the given target name.
*
* @param shortcutTargets The shortcut type.
* @param targetName The target name.
* @return {@code true} if the target is in the shortcut collection.
*/
public static boolean doesShortcutTargetsStringContain(Collection<String> shortcutTargets,
String targetName) {
if (shortcutTargets == null || targetName == null) {
return false;
}
// Some system features, such as magnification, don't have component name. Using string
// compare first.
if (shortcutTargets.contains(targetName)) {
return true;
}
final ComponentName targetComponentName = ComponentName.unflattenFromString(targetName);
if (targetComponentName == null) {
return false;
}
for (String stringName : shortcutTargets) {
if (!TextUtils.isEmpty(stringName)
&& targetComponentName.equals(ComponentName.unflattenFromString(stringName))) {
return true;
}
}
return false;
}
/**
* Gets the stroke width of the focus rectangle.
* @return The stroke width.
*/
public int getFocusStrokeWidthLocked() {
return mFocusStrokeWidth;
}
/**
* Gets the color of the focus rectangle.
* @return The color.
*/
public int getFocusColorLocked() {
return mFocusColor;
}
/**
* Sets the stroke width and color of the focus rectangle.
*
* @param strokeWidth The strokeWidth of the focus rectangle.
* @param color The color of the focus rectangle.
*/
public void setFocusAppearanceLocked(int strokeWidth, int color) {
mFocusStrokeWidth = strokeWidth;
mFocusColor = color;
}
public void setServiceDetectsGesturesEnabled(int displayId, boolean mode) {
mServiceDetectsGestures.put(displayId, mode);
}
public void resetServiceDetectsGestures() {
mServiceDetectsGestures.clear();
}
public boolean isServiceDetectsGesturesEnabled(int displayId) {
if (mServiceDetectsGestures.contains(displayId)) {
return mServiceDetectsGestures.get(displayId);
}
return false;
}
}