summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSvetoslav Ganov <svetoslavganov@google.com>2012-09-18 12:04:19 -0700
committerSvetoslav Ganov <svetoslavganov@google.com>2012-09-21 16:48:07 -0700
commit58d37b55bd228032355360ea3303e46a804e0516 (patch)
tree7a931f0e6054ef5835f2935dd8d2faa52a93ff53
parent059aedf808c8b9b3da1457eef52f3d477878d50b (diff)
downloadframeworks_base-58d37b55bd228032355360ea3303e46a804e0516.zip
frameworks_base-58d37b55bd228032355360ea3303e46a804e0516.tar.gz
frameworks_base-58d37b55bd228032355360ea3303e46a804e0516.tar.bz2
Multi-user support for the accessibility layer.
1. This change converts the accessibility manager service to maintain a state per user. When the user changes the services for the user that is going away are disconnected, the local accessibility managers in the processes for this user are disabled, the state is swapped with the new user's one, and the new user state is refreshed. This change updates all calls into the system to use their user specific versions when applicable. For example, regisetring content observers, package monitors, calls into other system services, etc. There are some components that are shared across users such as UI created by the system process and the SystemUI package. Such components are managed as a global state shared across all users and are updated accordingly on a user switch. Since the SystemUI is running in a normal app process this change adds hidden APIs on the local window manager to allow the SystemUI to notify the accessibility layer that it will run accross users. Calls to AccessibiltyManager's isEnabled(), isTouchExplorationEnabled() and sendAccessibilityEvent return false or a are a nop for a background user sice he should not send accessibility events, and should not perform touch exploration. Update the internal accessibility tests due to changes in the AccessibilityManager. This change also fixes several issues that were encountered such as calling out the accessibility manager service with a lock held. Removed some incorrect debugging code from the TouchExplorer that was leading to a system crash. bug:6967373 Change-Id: I2cf32ffdee1d827a8197ae4ce717dc0ff798b259
-rw-r--r--core/java/android/app/ApplicationPackageManager.java23
-rw-r--r--core/java/android/content/pm/PackageManager.java56
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java60
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl12
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIService.java5
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java1332
-rw-r--r--services/java/com/android/server/accessibility/TouchExplorer.java15
-rw-r--r--services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java32
-rw-r--r--services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java52
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java16
10 files changed, 1010 insertions, 593 deletions
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7870031..18503f6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -42,10 +42,8 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.ManifestDigest;
-import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -453,11 +451,17 @@ final class ApplicationPackageManager extends PackageManager {
@Override
public ResolveInfo resolveActivity(Intent intent, int flags) {
+ return resolveActivityAsUser(intent, flags, UserHandle.myUserId());
+ }
+
+ @Override
+ public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
try {
return mPM.resolveIntent(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
- flags, UserHandle.myUserId());
+ flags,
+ userId);
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
@@ -466,12 +470,12 @@ final class ApplicationPackageManager extends PackageManager {
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
int flags) {
- return queryIntentActivitiesForUser(intent, flags, UserHandle.myUserId());
+ return queryIntentActivitiesAsUser(intent, flags, UserHandle.myUserId());
}
/** @hide Same as above but for a specific user */
@Override
- public List<ResolveInfo> queryIntentActivitiesForUser(Intent intent,
+ public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
int flags, int userId) {
try {
return mPM.queryIntentActivities(
@@ -551,19 +555,24 @@ final class ApplicationPackageManager extends PackageManager {
}
@Override
- public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
+ public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
try {
return mPM.queryIntentServices(
intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
flags,
- UserHandle.myUserId());
+ userId);
} catch (RemoteException e) {
throw new RuntimeException("Package manager has died", e);
}
}
@Override
+ public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
+ return queryIntentServicesAsUser(intent, flags, UserHandle.myUserId());
+ }
+
+ @Override
public ProviderInfo resolveContentProvider(String name,
int flags) {
try {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fd488ae..171ff3f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1797,6 +1797,39 @@ public abstract class PackageManager {
public abstract ResolveInfo resolveActivity(Intent intent, int flags);
/**
+ * Determine the best action to perform for a given Intent for a given user. This
+ * is how {@link Intent#resolveActivity} finds an activity if a class has not
+ * been explicitly specified.
+ *
+ * <p><em>Note:</em> if using an implicit Intent (without an explicit ComponentName
+ * specified), be sure to consider whether to set the {@link #MATCH_DEFAULT_ONLY}
+ * only flag. You need to do so to resolve the activity in the same way
+ * that {@link android.content.Context#startActivity(Intent)} and
+ * {@link android.content.Intent#resolveActivity(PackageManager)
+ * Intent.resolveActivity(PackageManager)} do.</p>
+ *
+ * @param intent An intent containing all of the desired specification
+ * (action, data, type, category, and/or component).
+ * @param flags Additional option flags. The most important is
+ * {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
+ * those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
+ * @param userId The user id.
+ *
+ * @return Returns a ResolveInfo containing the final activity intent that
+ * was determined to be the best action. Returns null if no
+ * matching activity was found. If multiple matching activities are
+ * found and there is no default set, returns a ResolveInfo
+ * containing something else, such as the activity resolver.
+ *
+ * @see #MATCH_DEFAULT_ONLY
+ * @see #GET_INTENT_FILTERS
+ * @see #GET_RESOLVED_FILTER
+ *
+ * @hide
+ */
+ public abstract ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);
+
+ /**
* Retrieve all activities that can be performed for the given intent.
*
* @param intent The desired intent as per resolveActivity().
@@ -1836,7 +1869,7 @@ public abstract class PackageManager {
* @see #GET_RESOLVED_FILTER
* @hide
*/
- public abstract List<ResolveInfo> queryIntentActivitiesForUser(Intent intent,
+ public abstract List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
int flags, int userId);
@@ -1944,6 +1977,27 @@ public abstract class PackageManager {
int flags);
/**
+ * Retrieve all services that can match the given intent for a given user.
+ *
+ * @param intent The desired intent as per resolveService().
+ * @param flags Additional option flags.
+ * @param userId The user id.
+ *
+ * @return A List&lt;ResolveInfo&gt; containing one entry for each matching
+ * ServiceInfo. These are ordered from best to worst match -- that
+ * is, the first item in the list is what is returned by
+ * resolveService(). If there are no matching services, an empty
+ * list is returned.
+ *
+ * @see #GET_INTENT_FILTERS
+ * @see #GET_RESOLVED_FILTER
+ *
+ * @hide
+ */
+ public abstract List<ResolveInfo> queryIntentServicesAsUser(Intent intent,
+ int flags, int userId);
+
+ /**
* Find a single content provider by its base path name.
*
* @param name The name of the provider to find.
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 77fd12a..732699b 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -27,6 +27,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.util.Log;
import android.view.IWindow;
import android.view.View;
@@ -79,6 +80,8 @@ public final class AccessibilityManager {
final IAccessibilityManager mService;
+ final int mUserId;
+
final Handler mHandler;
boolean mIsEnabled;
@@ -129,35 +132,72 @@ public final class AccessibilityManager {
}
/**
+ * Creates the singleton AccessibilityManager to be shared across users. This
+ * has to be called before the local AccessibilityManager is created to ensure
+ * it registers itself in the system correctly.
+ * <p>
+ * Note: Calling this method requires INTERACT_ACROSS_USERS_FULL or
+ * INTERACT_ACROSS_USERS permission.
+ * </p>
+ * @param context Context in which this manager operates.
+ * @throws IllegalStateException if not called before the local
+ * AccessibilityManager is instantiated.
+ *
+ * @hide
+ */
+ public static void createAsSharedAcrossUsers(Context context) {
+ synchronized (sInstanceSync) {
+ if (sInstance != null) {
+ throw new IllegalStateException("AccessibilityManager already created.");
+ }
+ createSingletonInstance(context, UserHandle.USER_CURRENT);
+ }
+ }
+
+ /**
* Get an AccessibilityManager instance (create one if necessary).
*
+ * @param context Context in which this manager operates.
+ *
* @hide
*/
public static AccessibilityManager getInstance(Context context) {
synchronized (sInstanceSync) {
if (sInstance == null) {
- IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
- IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
- sInstance = new AccessibilityManager(context, service);
+ createSingletonInstance(context, UserHandle.myUserId());
}
}
return sInstance;
}
/**
+ * Creates the singleton instance.
+ *
+ * @param context Context in which this manager operates.
+ * @param userId The user id under which to operate.
+ */
+ private static void createSingletonInstance(Context context, int userId) {
+ IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
+ sInstance = new AccessibilityManager(context, service, userId);
+ }
+
+ /**
* Create an instance.
*
* @param context A {@link Context}.
* @param service An interface to the backing service.
+ * @param userId User id under which to run.
*
* @hide
*/
- public AccessibilityManager(Context context, IAccessibilityManager service) {
+ public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
mHandler = new MyHandler(context.getMainLooper());
mService = service;
+ mUserId = userId;
try {
- final int stateFlags = mService.addClient(mClient);
+ final int stateFlags = mService.addClient(mClient, userId);
setState(stateFlags);
} catch (RemoteException re) {
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
@@ -222,7 +262,7 @@ public final class AccessibilityManager {
// client using it is called through Binder from another process. Example: MMS
// app adds a SMS notification and the NotificationManagerService calls this method
long identityToken = Binder.clearCallingIdentity();
- doRecycle = mService.sendAccessibilityEvent(event);
+ doRecycle = mService.sendAccessibilityEvent(event, mUserId);
Binder.restoreCallingIdentity(identityToken);
if (DEBUG) {
Log.i(LOG_TAG, event + " sent");
@@ -244,7 +284,7 @@ public final class AccessibilityManager {
throw new IllegalStateException("Accessibility off. Did you forget to check that?");
}
try {
- mService.interrupt();
+ mService.interrupt(mUserId);
if (DEBUG) {
Log.i(LOG_TAG, "Requested interrupt from all services");
}
@@ -280,7 +320,7 @@ public final class AccessibilityManager {
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
List<AccessibilityServiceInfo> services = null;
try {
- services = mService.getInstalledAccessibilityServiceList();
+ services = mService.getInstalledAccessibilityServiceList(mUserId);
if (DEBUG) {
Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
}
@@ -307,7 +347,7 @@ public final class AccessibilityManager {
int feedbackTypeFlags) {
List<AccessibilityServiceInfo> services = null;
try {
- services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags);
+ services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags, mUserId);
if (DEBUG) {
Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
}
@@ -385,7 +425,7 @@ public final class AccessibilityManager {
public int addAccessibilityInteractionConnection(IWindow windowToken,
IAccessibilityInteractionConnection connection) {
try {
- return mService.addAccessibilityInteractionConnection(windowToken, connection);
+ return mService.addAccessibilityInteractionConnection(windowToken, connection, mUserId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 5b5134a..60238627 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -34,18 +34,18 @@ import android.view.IWindow;
*/
interface IAccessibilityManager {
- int addClient(IAccessibilityManagerClient client);
+ int addClient(IAccessibilityManagerClient client, int userId);
- boolean sendAccessibilityEvent(in AccessibilityEvent uiEvent);
+ boolean sendAccessibilityEvent(in AccessibilityEvent uiEvent, int userId);
- List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
+ List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId);
- List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType);
+ List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
- void interrupt();
+ void interrupt(int userId);
int addAccessibilityInteractionConnection(IWindow windowToken,
- in IAccessibilityInteractionConnection connection);
+ in IAccessibilityInteractionConnection connection, int userId);
void removeAccessibilityInteractionConnection(IWindow windowToken);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 1bde949..427fe91 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -32,6 +32,7 @@ import android.os.ServiceManager;
import android.util.Slog;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
public class SystemUIService extends Service {
static final String TAG = "SystemUIService";
@@ -67,6 +68,10 @@ public class SystemUIService extends Service {
@Override
public void onCreate() {
+ // Tell the accessibility layer that this process will
+ // run as the current user, i.e. run across users.
+ AccessibilityManager.createAsSharedAcrossUsers(this);
+
// Pick status bar or system bar.
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
try {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index e7f3599..abbf0d7 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -50,9 +50,12 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
@@ -76,6 +79,7 @@ import android.view.accessibility.IAccessibilityManagerClient;
import com.android.internal.R;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBarService;
import org.xmlpull.v1.XmlPullParserException;
@@ -89,6 +93,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* This class is instantiated by the system as a system level service and can be
@@ -111,69 +116,62 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private static final int OWN_PROCESS_ID = android.os.Process.myPid();
- private static final int MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG = 1;
-
- private static final int MSG_TOGGLE_TOUCH_EXPLORATION = 2;
-
- private static final int MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER = 3;
-
- private static final int MSG_SEND_UPDATE_INPUT_FILTER = 4;
-
private static int sIdCounter = 0;
private static int sNextWindowId;
- final Context mContext;
-
- final Object mLock = new Object();
-
- final List<Service> mServices = new ArrayList<Service>();
+ private final Context mContext;
- final List<IAccessibilityManagerClient> mClients =
- new ArrayList<IAccessibilityManagerClient>();
+ private final Object mLock = new Object();
- final Map<ComponentName, Service> mComponentNameToServiceMap = new HashMap<ComponentName, Service>();
+ private final SimpleStringSplitter mStringColonSplitter =
+ new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
- private final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<AccessibilityServiceInfo>();
+ private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList =
+ new ArrayList<AccessibilityServiceInfo>();
- private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
+ private final PackageManager mPackageManager;
- private final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<ComponentName>();
+ private final IWindowManager mWindowManagerService;
- private final SparseArray<AccessibilityConnectionWrapper> mWindowIdToInteractionConnectionWrapperMap =
- new SparseArray<AccessibilityConnectionWrapper>();
-
- private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>();
+ private final SecurityPolicy mSecurityPolicy;
- private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
+ private final MainHandler mMainHandler;
- private PackageManager mPackageManager;
+ private Service mUiAutomationService;
- private int mHandledFeedbackTypes = 0;
+ private Service mQueryBridge;
- private boolean mIsAccessibilityEnabled;
+ private AlertDialog mEnableTouchExplorationDialog;
private AccessibilityInputFilter mInputFilter;
private boolean mHasInputFilter;
- private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList = new ArrayList<AccessibilityServiceInfo>();
-
- private boolean mIsTouchExplorationEnabled;
+ private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
+ new RemoteCallbackList<IAccessibilityManagerClient>();
- private boolean mIsScreenMagnificationEnabled;
+ private final SparseArray<AccessibilityConnectionWrapper> mGlobalInteractionConnections =
+ new SparseArray<AccessibilityConnectionWrapper>();
- private final IWindowManager mWindowManager;
+ private final SparseArray<IBinder> mGlobalWindowTokens = new SparseArray<IBinder>();
- private final SecurityPolicy mSecurityPolicy;
-
- private final MainHandler mMainHandler;
+ private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
- private Service mUiAutomationService;
+ private int mCurrentUserId = UserHandle.USER_NULL;
- private Service mQueryBridge;
+ private UserState getCurrentUserStateLocked() {
+ return getUserStateLocked(mCurrentUserId);
+ }
- private AlertDialog mEnableTouchExplorationDialog;
+ private UserState getUserStateLocked(int userId) {
+ UserState state = mUserStates.get(userId);
+ if (state == null) {
+ state = new UserState(userId);
+ mUserStates.put(userId, state);
+ }
+ return state;
+ }
/**
* Creates a new instance.
@@ -183,28 +181,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public AccessibilityManagerService(Context context) {
mContext = context;
mPackageManager = mContext.getPackageManager();
- mWindowManager = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE);
+ mWindowManagerService = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE);
mSecurityPolicy = new SecurityPolicy();
mMainHandler = new MainHandler(mContext.getMainLooper());
- registerPackageChangeAndBootCompletedBroadcastReceiver();
- registerSettingsContentObservers();
+ registerBroadcastReceivers();
+ new AccessibilityContentObserver(mMainHandler).register(
+ context.getContentResolver());
}
- /**
- * Registers a {@link BroadcastReceiver} for the events of
- * adding/changing/removing/restarting a package and boot completion.
- */
- private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
- Context context = mContext;
-
+ private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
synchronized (mLock) {
+ if (getChangingUserId() != mCurrentUserId) {
+ return;
+ }
// We will update when the automation service dies.
if (mUiAutomationService == null) {
- populateInstalledAccessibilityServiceLocked();
- manageServicesLocked();
+ UserState userState = getCurrentUserStateLocked();
+ populateInstalledAccessibilityServiceLocked(userState);
+ manageServicesLocked(userState);
}
}
}
@@ -212,7 +209,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
- Iterator<ComponentName> it = mEnabledServices.iterator();
+ final int userId = getChangingUserId();
+ if (userId != mCurrentUserId) {
+ return;
+ }
+ UserState state = getUserStateLocked(userId);
+ Iterator<ComponentName> it = state.mEnabledServices.iterator();
while (it.hasNext()) {
ComponentName comp = it.next();
String compPkg = comp.getPackageName();
@@ -221,13 +223,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// Update the enabled services setting.
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- mEnabledServices);
+ state.mEnabledServices, userId);
// Update the touch exploration granted services setting.
- mTouchExplorationGrantedServices.remove(comp);
+ state.mTouchExplorationGrantedServices.remove(comp);
persistComponentNamesToSettingLocked(
Settings.Secure.
TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- mEnabledServices);
+ state.mEnabledServices, userId);
return;
}
}
@@ -238,7 +240,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public boolean onHandleForceStop(Intent intent, String[] packages,
int uid, boolean doit) {
synchronized (mLock) {
- Iterator<ComponentName> it = mEnabledServices.iterator();
+ final int userId = getChangingUserId();
+ if (userId != mCurrentUserId) {
+ return false;
+ }
+ UserState state = getUserStateLocked(userId);
+ Iterator<ComponentName> it = state.mEnabledServices.iterator();
while (it.hasNext()) {
ComponentName comp = it.next();
String compPkg = comp.getPackageName();
@@ -250,179 +257,97 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
it.remove();
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- mEnabledServices);
+ state.mEnabledServices, userId);
}
}
}
return false;
}
}
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
- synchronized (mLock) {
- // We will update when the automation service dies.
- if (mUiAutomationService == null) {
- updateInternalStateLocked();
- }
- }
- return;
- }
- super.onReceive(context, intent);
- }
};
// package changes
- monitor.register(context, null, true);
+ monitor.register(mContext, null, UserHandle.ALL, true);
- // boot completed
- IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
- mContext.registerReceiver(monitor, bootFiler, null, monitor.getRegisteredHandler());
- }
-
- /**
- * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
- * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
- */
- private void registerSettingsContentObservers() {
- ContentResolver contentResolver = mContext.getContentResolver();
-
- Uri accessibilityEnabledUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_ENABLED);
- contentResolver.registerContentObserver(accessibilityEnabledUri, false,
- new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- synchronized (mLock) {
- // We will update when the automation service dies.
- if (mUiAutomationService == null) {
- handleAccessibilityEnabledSettingChangedLocked();
- updateInputFilterLocked();
- sendStateToClientsLocked();
- }
- }
- }
- });
+ // user change
+ IntentFilter userFilter = new IntentFilter();
+ userFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ userFilter.addAction(Intent.ACTION_USER_REMOVED);
- Uri touchExplorationRequestedUri = Settings.Secure.getUriFor(
- Settings.Secure.TOUCH_EXPLORATION_ENABLED);
- contentResolver.registerContentObserver(touchExplorationRequestedUri, false,
- new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- synchronized (mLock) {
- // We will update when the automation service dies.
- if (mUiAutomationService == null) {
- handleTouchExplorationEnabledSettingChangedLocked();
- updateInputFilterLocked();
- sendStateToClientsLocked();
- }
- }
- }
- });
-
- Uri accessibilityScreenMagnificationEnabledUri = Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
- contentResolver.registerContentObserver(accessibilityScreenMagnificationEnabledUri, false,
- new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- synchronized (mLock) {
- // We will update when the automation service dies.
- if (mUiAutomationService == null) {
- handleScreenMagnificationEnabledSettingChangedLocked();
- updateInputFilterLocked();
- sendStateToClientsLocked();
- }
- }
- }
- });
-
- Uri accessibilityServicesUri =
- Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
- contentResolver.registerContentObserver(accessibilityServicesUri, false,
- new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- synchronized (mLock) {
- // We will update when the automation service dies.
- if (mUiAutomationService == null) {
- populateEnabledAccessibilityServicesLocked();
- manageServicesLocked();
- }
- }
- }
- });
-
- Uri touchExplorationGrantedServicesUri = Settings.Secure.getUriFor(
- Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
- contentResolver.registerContentObserver(touchExplorationGrantedServicesUri, false,
- new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- synchronized (mLock) {
- // We will update when the automation service dies.
- if (mUiAutomationService == null) {
- populateTouchExplorationGrantedAccessibilityServicesLocked();
- handleTouchExplorationGrantedAccessibilityServicesChangedLocked();
- }
- }
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
}
- });
+ }
+ }, userFilter);
}
- public int addClient(IAccessibilityManagerClient client) throws RemoteException {
+ public int addClient(IAccessibilityManagerClient client, int userId) {
synchronized (mLock) {
- final IAccessibilityManagerClient addedClient = client;
- mClients.add(addedClient);
- // Clients are registered all the time until their process is
- // killed, therefore we do not have a corresponding unlinkToDeath.
- client.asBinder().linkToDeath(new DeathRecipient() {
- public void binderDied() {
- synchronized (mLock) {
- addedClient.asBinder().unlinkToDeath(this, 0);
- mClients.remove(addedClient);
- }
- }
- }, 0);
- return getState();
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ // If the client is from a process that runs across users such as
+ // the system UI or the system we add it to the global state that
+ // is shared across users.
+ UserState userState = getUserStateLocked(resolvedUserId);
+ if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
+ mGlobalClients.register(client);
+ return getClientState(userState);
+ } else {
+ userState.mClients.register(client);
+ // If this client is not for the current user we do not
+ // return a state since it is not for the foreground user.
+ // We will send the state to the client on a user switch.
+ return (resolvedUserId == mCurrentUserId) ? getClientState(userState) : 0;
+ }
}
}
- public boolean sendAccessibilityEvent(AccessibilityEvent event) {
+ public boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) {
synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ // This method does nothing for a background user.
+ if (resolvedUserId != mCurrentUserId) {
+ return true; // yes, recycle the event
+ }
if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
mSecurityPolicy.updateActiveWindowAndEventSourceLocked(event);
notifyAccessibilityServicesDelayedLocked(event, false);
notifyAccessibilityServicesDelayedLocked(event, true);
}
if (mHasInputFilter && mInputFilter != null) {
- mMainHandler.obtainMessage(MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER,
+ mMainHandler.obtainMessage(MainHandler.MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER,
AccessibilityEvent.obtain(event)).sendToTarget();
}
event.recycle();
- mHandledFeedbackTypes = 0;
+ getUserStateLocked(resolvedUserId).mHandledFeedbackTypes = 0;
}
return (OWN_PROCESS_ID != Binder.getCallingPid());
}
- public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
+ public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
synchronized (mLock) {
- return mInstalledServices;
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ return getUserStateLocked(resolvedUserId).mInstalledServices;
}
}
- public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
- List<AccessibilityServiceInfo> result = mEnabledServicesForFeedbackTempList;
- result.clear();
- List<Service> services = mServices;
+ public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType,
+ int userId) {
+ List<AccessibilityServiceInfo> result = null;
synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ result = mEnabledServicesForFeedbackTempList;
+ result.clear();
+ List<Service> services = getUserStateLocked(resolvedUserId).mServices;
while (feedbackType != 0) {
final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType));
feedbackType &= ~feedbackTypeBit;
@@ -438,30 +363,51 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return result;
}
- public void interrupt() {
+ public void interrupt(int userId) {
+ CopyOnWriteArrayList<Service> services;
synchronized (mLock) {
- for (int i = 0, count = mServices.size(); i < count; i++) {
- Service service = mServices.get(i);
- try {
- service.mServiceInterface.onInterrupt();
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error during sending interrupt request to "
- + service.mService, re);
- }
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+ // This method does nothing for a background user.
+ if (resolvedUserId != mCurrentUserId) {
+ return;
+ }
+ services = getUserStateLocked(resolvedUserId).mServices;
+ }
+ for (int i = 0, count = services.size(); i < count; i++) {
+ Service service = services.get(i);
+ try {
+ service.mServiceInterface.onInterrupt();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error during sending interrupt request to "
+ + service.mService, re);
}
}
}
public int addAccessibilityInteractionConnection(IWindow windowToken,
- IAccessibilityInteractionConnection connection) throws RemoteException {
+ IAccessibilityInteractionConnection connection, int userId) throws RemoteException {
synchronized (mLock) {
- final IWindow addedWindowToken = windowToken;
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(userId);
final int windowId = sNextWindowId++;
- AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(windowId,
- connection);
- wrapper.linkToDeath();
- mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder());
- mWindowIdToInteractionConnectionWrapperMap.put(windowId, wrapper);
+ // If the window is from a process that runs across users such as
+ // the system UI or the system we add it to the global state that
+ // is shared across users.
+ if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
+ AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(
+ windowId, connection, UserHandle.USER_ALL);
+ wrapper.linkToDeath();
+ mGlobalInteractionConnections.put(windowId, wrapper);
+ mGlobalWindowTokens.put(windowId, windowToken.asBinder());
+ } else {
+ AccessibilityConnectionWrapper wrapper = new AccessibilityConnectionWrapper(
+ windowId, connection, resolvedUserId);
+ wrapper.linkToDeath();
+ UserState userState = getUserStateLocked(resolvedUserId);
+ userState.mInteractionConnections.put(windowId, wrapper);
+ userState.mWindowTokens.put(windowId, windowToken.asBinder());
+ }
if (DEBUG) {
Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId);
}
@@ -469,22 +415,47 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
- public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+ public void removeAccessibilityInteractionConnection(IWindow window) {
synchronized (mLock) {
- final int count = mWindowIdToWindowTokenMap.size();
- for (int i = 0; i < count; i++) {
- if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) {
- final int windowId = mWindowIdToWindowTokenMap.keyAt(i);
- AccessibilityConnectionWrapper wrapper =
- mWindowIdToInteractionConnectionWrapperMap.get(windowId);
- wrapper.unlinkToDeath();
- removeAccessibilityInteractionConnectionLocked(windowId);
+ mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ IBinder token = window.asBinder();
+ final boolean removedGlobal =
+ removeAccessibilityInteractionConnectionInternalLocked(
+ token, mGlobalWindowTokens, mGlobalInteractionConnections);
+ if (removedGlobal) {
+ return;
+ }
+ final int userCount = mUserStates.size();
+ for (int i = 0; i < userCount; i++) {
+ UserState userState = mUserStates.valueAt(i);
+ final boolean removedForUser =
+ removeAccessibilityInteractionConnectionInternalLocked(
+ token, userState.mWindowTokens, userState.mInteractionConnections);
+ if (removedForUser) {
return;
}
}
}
}
+ private boolean removeAccessibilityInteractionConnectionInternalLocked(IBinder windowToken,
+ SparseArray<IBinder> windowTokens,
+ SparseArray<AccessibilityConnectionWrapper> interactionConnections) {
+ final int count = windowTokens.size();
+ for (int i = 0; i < count; i++) {
+ if (windowTokens.valueAt(i) == windowToken) {
+ final int windowId = windowTokens.keyAt(i);
+ windowTokens.removeAt(i);
+ AccessibilityConnectionWrapper wrapper = interactionConnections.get(windowId);
+ wrapper.unlinkToDeath();
+ interactionConnections.remove(windowId);
+ return true;
+ }
+ }
+ return false;
+ }
+
public void registerUiTestAutomationService(IAccessibilityServiceClient serviceClient,
AccessibilityServiceInfo accessibilityServiceInfo) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
@@ -495,21 +466,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// If an automation services is connected to the system all services are stopped
// so the automation one is the only one running. Settings are not changed so when
// the automation service goes away the state is restored from the settings.
+ UserState userState = getCurrentUserStateLocked();
+ unbindAllServicesLocked(userState);
- // Disable all services.
- final int runningServiceCount = mServices.size();
- for (int i = 0; i < runningServiceCount; i++) {
- Service runningService = mServices.get(i);
- runningService.unbind();
- }
// If necessary enable accessibility and announce that.
- if (!mIsAccessibilityEnabled) {
- mIsAccessibilityEnabled = true;
- sendStateToClientsLocked();
+ if (!userState.mIsAccessibilityEnabled) {
+ userState.mIsAccessibilityEnabled = true;
+ scheduleSendStateToClientsLocked(userState);
}
}
// Hook the automation service up.
- mUiAutomationService = new Service(componentName, accessibilityServiceInfo, true);
+ mUiAutomationService = new Service(mCurrentUserId, componentName,
+ accessibilityServiceInfo, true);
mUiAutomationService.onServiceConnected(componentName, serviceClient.asBinder());
}
@@ -572,30 +540,80 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @param outBounds The output to which to write the bounds.
*/
boolean getActiveWindowBounds(Rect outBounds) {
+ IBinder token;
synchronized (mLock) {
final int windowId = mSecurityPolicy.mActiveWindowId;
- IBinder token = mWindowIdToWindowTokenMap.get(windowId);
- try {
- WindowInfo info = mWindowManager.getWindowInfo(token);
- if (info != null) {
- outBounds.set(info.frame);
- return true;
- }
- } catch (RemoteException re) {
- /* ignore */
+ token = mGlobalWindowTokens.get(windowId);
+ if (token == null) {
+ token = getCurrentUserStateLocked().mWindowTokens.get(windowId);
+ }
+ }
+ WindowInfo info = null;
+ try {
+ info = mWindowManagerService.getWindowInfo(token);
+ if (info != null) {
+ outBounds.set(info.frame);
+ return true;
+ }
+ } catch (RemoteException re) {
+ /* ignore */
+ } finally {
+ if (info != null) {
+ info.recycle();
}
- return false;
}
+ return false;
}
- public int getActiveWindowId() {
+ int getActiveWindowId() {
return mSecurityPolicy.mActiveWindowId;
}
+ private void switchUser(int userId) {
+ synchronized (mLock) {
+ if (userId == mCurrentUserId) {
+ return;
+ }
+
+ // Disconnect from services for the old user.
+ UserState oldUserState = getUserStateLocked(mCurrentUserId);
+ unbindAllServicesLocked(oldUserState);
+
+ // Disable the local managers for the old user.
+ if (oldUserState.mClients.getRegisteredCallbackCount() > 0) {
+ mMainHandler.obtainMessage(MainHandler.MSG_SEND_CLEARED_STATE_TO_CLIENTS_FOR_USER,
+ oldUserState.mUserId, 0).sendToTarget();
+ }
+
+ // The user changed.
+ mCurrentUserId = userId;
+
+ // Recreate the internal state for the new user.
+ mMainHandler.obtainMessage(MainHandler.MSG_SEND_RECREATE_INTERNAL_STATE,
+ mCurrentUserId, 0).sendToTarget();
+
+ // Re-register the test automation service after the new state is recreated.
+ if (mUiAutomationService != null) {
+ unregisterUiTestAutomationService(mUiAutomationService.mServiceInterface);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = mUiAutomationService.mServiceInterface;
+ args.arg2 = mUiAutomationService.mAccessibilityServiceInfo;
+ mMainHandler.obtainMessage(MainHandler.MSG_REGISTER_UI_TEST_AUTOMATION_SERVICE,
+ args).sendToTarget();
+ }
+ }
+ }
+
+ private void removeUser(int userId) {
+ synchronized (mLock) {
+ mUserStates.remove(userId);
+ }
+ }
+
private Service getQueryBridge() {
if (mQueryBridge == null) {
AccessibilityServiceInfo info = new AccessibilityServiceInfo();
- mQueryBridge = new Service(null, info, true);
+ mQueryBridge = new Service(UserHandle.USER_NULL, null, info, true);
}
return mQueryBridge;
}
@@ -610,8 +628,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// gestures to avoid user frustration when different
// behavior is observed from different combinations of
// enabled accessibility services.
- for (int i = mServices.size() - 1; i >= 0; i--) {
- Service service = mServices.get(i);
+ UserState state = getCurrentUserStateLocked();
+ for (int i = state.mServices.size() - 1; i >= 0; i--) {
+ Service service = state.mServices.get(i);
if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
service.notifyGesture(gestureId);
return true;
@@ -624,29 +643,36 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* Removes an AccessibilityInteractionConnection.
*
* @param windowId The id of the window to which the connection is targeted.
+ * @param userId The id of the user owning the connection. UserHandle.USER_ALL
+ * if global.
*/
- private void removeAccessibilityInteractionConnectionLocked(int windowId) {
- mWindowIdToWindowTokenMap.remove(windowId);
- mWindowIdToInteractionConnectionWrapperMap.remove(windowId);
+ private void removeAccessibilityInteractionConnectionLocked(int windowId, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ mGlobalWindowTokens.remove(windowId);
+ mGlobalInteractionConnections.remove(windowId);
+ } else {
+ UserState userState = getCurrentUserStateLocked();
+ userState.mWindowTokens.remove(windowId);
+ userState.mInteractionConnections.remove(windowId);
+ }
if (DEBUG) {
Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
}
}
- /**
- * Populates the cached list of installed {@link AccessibilityService}s.
- */
- private void populateInstalledAccessibilityServiceLocked() {
- mInstalledServices.clear();
+ private void populateInstalledAccessibilityServiceLocked(UserState userState) {
+ userState.mInstalledServices.clear();
- List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
+ List<ResolveInfo> installedServices = mPackageManager.queryIntentServicesAsUser(
new Intent(AccessibilityService.SERVICE_INTERFACE),
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+ mCurrentUserId);
for (int i = 0, count = installedServices.size(); i < count; i++) {
ResolveInfo resolveInfo = installedServices.get(i);
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(serviceInfo.permission)) {
+ if (!android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE.equals(
+ serviceInfo.permission)) {
Slog.w(LOG_TAG, "Skipping accessibilty service " + new ComponentName(
serviceInfo.packageName, serviceInfo.name).flattenToShortString()
+ ": it does not require the permission "
@@ -656,7 +682,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
AccessibilityServiceInfo accessibilityServiceInfo;
try {
accessibilityServiceInfo = new AccessibilityServiceInfo(resolveInfo, mContext);
- mInstalledServices.add(accessibilityServiceInfo);
+ userState.mInstalledServices.add(accessibilityServiceInfo);
} catch (XmlPullParserException xppe) {
Slog.e(LOG_TAG, "Error while initializing AccessibilityServiceInfo", xppe);
} catch (IOException ioe) {
@@ -665,16 +691,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
- private void populateEnabledAccessibilityServicesLocked() {
+ private void populateEnabledAccessibilityServicesLocked(UserState userState) {
populateComponentNamesFromSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- mEnabledServices);
+ userState.mUserId,
+ userState.mEnabledServices);
}
- private void populateTouchExplorationGrantedAccessibilityServicesLocked() {
+ private void populateTouchExplorationGrantedAccessibilityServicesLocked(
+ UserState userState) {
populateComponentNamesFromSettingLocked(
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- mTouchExplorationGrantedServices);
+ userState.mUserId,
+ userState.mTouchExplorationGrantedServices);
}
/**
@@ -687,12 +716,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
boolean isDefault) {
try {
- for (int i = 0, count = mServices.size(); i < count; i++) {
- Service service = mServices.get(i);
+ UserState state = getCurrentUserStateLocked();
+ for (int i = 0, count = state.mServices.size(); i < count; i++) {
+ Service service = state.mServices.get(i);
if (service.mIsDefault == isDefault) {
- if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
- mHandledFeedbackTypes |= service.mFeedbackType;
+ if (canDispathEventLocked(service, event, state.mHandledFeedbackTypes)) {
+ state.mHandledFeedbackTypes |= service.mFeedbackType;
service.notifyAccessibilityEvent(event);
}
}
@@ -706,19 +736,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
/**
- * Adds a service.
+ * Adds a service for a user.
*
* @param service The service to add.
+ * @param userId The user id.
*/
- private void tryAddServiceLocked(Service service) {
+ private void tryAddServiceLocked(Service service, int userId) {
try {
- if (mServices.contains(service) || !service.isConfigured()) {
+ UserState userState = getUserStateLocked(userId);
+ if (userState.mServices.contains(service) || !service.isConfigured()) {
return;
}
service.linkToOwnDeath();
- mServices.add(service);
- mComponentNameToServiceMap.put(service.mComponentName, service);
- updateInputFilterLocked();
+ userState.mServices.add(service);
+ userState.mComponentNameToServiceMap.put(service.mComponentName, service);
+ updateInputFilterLocked(userState);
tryEnableTouchExplorationLocked(service);
} catch (RemoteException e) {
/* do nothing */
@@ -732,14 +764,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @return True if the service was removed, false otherwise.
*/
private boolean tryRemoveServiceLocked(Service service) {
- final boolean removed = mServices.remove(service);
+ UserState userState = getUserStateLocked(service.mUserId);
+ final boolean removed = userState.mServices.remove(service);
if (!removed) {
return false;
}
- mComponentNameToServiceMap.remove(service.mComponentName);
+ userState.mComponentNameToServiceMap.remove(service.mComponentName);
service.unlinkToOwnDeath();
service.dispose();
- updateInputFilterLocked();
+ updateInputFilterLocked(userState);
tryDisableTouchExplorationLocked(service);
return removed;
}
@@ -791,23 +824,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
/**
* Manages services by starting enabled ones and stopping disabled ones.
*/
- private void manageServicesLocked() {
- final int enabledInstalledServicesCount = updateServicesStateLocked(mInstalledServices,
- mEnabledServices);
+ private void manageServicesLocked(UserState userState) {
+ final int enabledInstalledServicesCount = updateServicesStateLocked(userState);
// No enabled installed services => disable accessibility to avoid
// sending accessibility events with no recipient across processes.
- if (mIsAccessibilityEnabled && enabledInstalledServicesCount == 0) {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_ENABLED, 0);
+ if (userState.mIsAccessibilityEnabled && enabledInstalledServicesCount == 0) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId);
}
}
/**
- * Unbinds all bound services.
+ * Unbinds all bound services for a user.
+ *
+ * @param userState The user state.
*/
- private void unbindAllServicesLocked() {
- List<Service> services = mServices;
-
+ private void unbindAllServicesLocked(UserState userState) {
+ List<Service> services = userState.mServices;
for (int i = 0, count = services.size(); i < count; i++) {
Service service = services.get(i);
if (service.unbind()) {
@@ -819,17 +852,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
/**
* Populates a set with the {@link ComponentName}s stored in a colon
- * separated value setting.
+ * separated value setting for a given user.
*
* @param settingName The setting to parse.
+ * @param userId The user id.
* @param outComponentNames The output component names.
*/
- private void populateComponentNamesFromSettingLocked(String settingName,
+ private void populateComponentNamesFromSettingLocked(String settingName, int userId,
Set<ComponentName> outComponentNames) {
+ String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ settingName, userId);
outComponentNames.clear();
-
- String settingValue = Settings.Secure.getString(mContext.getContentResolver(), settingName);
-
if (settingValue != null) {
TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
splitter.setString(settingValue);
@@ -854,7 +887,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @param componentNames The component names.
*/
private void persistComponentNamesToSettingLocked(String settingName,
- Set<ComponentName> componentNames) {
+ Set<ComponentName> componentNames, int userId) {
StringBuilder builder = new StringBuilder();
for (ComponentName componentName : componentNames) {
if (builder.length() > 0) {
@@ -862,34 +895,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
builder.append(componentName.flattenToShortString());
}
- Settings.Secure.putString(mContext.getContentResolver(), settingName, builder.toString());
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ settingName, builder.toString(), userId);
}
/**
* Updates the state of each service by starting (or keeping running) enabled ones and
* stopping the rest.
*
- * @param installedServices All installed {@link AccessibilityService}s.
- * @param enabledServices The {@link ComponentName}s of the enabled services.
+ * @param userState The user state for which to do that.
* @return The number of enabled installed services.
*/
- private int updateServicesStateLocked(List<AccessibilityServiceInfo> installedServices,
- Set<ComponentName> enabledServices) {
-
- Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
- boolean isEnabled = mIsAccessibilityEnabled;
+ private int updateServicesStateLocked(UserState userState) {
+ Map<ComponentName, Service> componentNameToServiceMap =
+ userState.mComponentNameToServiceMap;
+ boolean isEnabled = userState.mIsAccessibilityEnabled;
int enabledInstalledServices = 0;
- for (int i = 0, count = installedServices.size(); i < count; i++) {
- AccessibilityServiceInfo installedService = installedServices.get(i);
+ for (int i = 0, count = userState.mInstalledServices.size(); i < count; i++) {
+ AccessibilityServiceInfo installedService = userState.mInstalledServices.get(i);
ComponentName componentName = ComponentName.unflattenFromString(
installedService.getId());
Service service = componentNameToServiceMap.get(componentName);
if (isEnabled) {
- if (enabledServices.contains(componentName)) {
+ if (userState.mEnabledServices.contains(componentName)) {
if (service == null) {
- service = new Service(componentName, installedService, false);
+ service = new Service(userState.mUserId, componentName,
+ installedService, false);
}
service.bind();
enabledInstalledServices++;
@@ -908,145 +941,206 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return enabledInstalledServices;
}
- /**
- * Sends the state to the clients.
- */
- private void sendStateToClientsLocked() {
- final int state = getState();
- for (int i = 0, count = mClients.size(); i < count; i++) {
+ private void scheduleSendStateToClientsLocked(UserState userState) {
+ if (mGlobalClients.getRegisteredCallbackCount() > 0
+ || userState.mClients.getRegisteredCallbackCount() > 0) {
+ final int clientState = getClientState(userState);
+ mMainHandler.obtainMessage(MainHandler.MSG_SEND_STATE_TO_CLIENTS,
+ clientState, userState.mUserId) .sendToTarget();
+ }
+ }
+
+ private void updateInputFilterLocked(UserState userState) {
+ boolean setInputFilter = false;
+ AccessibilityInputFilter inputFilter = null;
+ synchronized (mLock) {
+ if ((userState.mIsAccessibilityEnabled && userState.mIsTouchExplorationEnabled)
+ || userState.mIsDisplayMagnificationEnabled) {
+ if (!mHasInputFilter) {
+ mHasInputFilter = true;
+ if (mInputFilter == null) {
+ mInputFilter = new AccessibilityInputFilter(mContext,
+ AccessibilityManagerService.this);
+ }
+ inputFilter = mInputFilter;
+ setInputFilter = true;
+ }
+ int flags = 0;
+ if (userState.mIsDisplayMagnificationEnabled) {
+ flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
+ }
+ if (userState.mIsTouchExplorationEnabled) {
+ flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
+ }
+ mInputFilter.setEnabledFeatures(flags);
+ } else {
+ if (mHasInputFilter) {
+ mHasInputFilter = false;
+ mInputFilter.setEnabledFeatures(0);
+ inputFilter = null;
+ setInputFilter = true;
+ }
+ }
+ }
+ if (setInputFilter) {
try {
- mClients.get(i).setState(state);
+ mWindowManagerService.setInputFilter(inputFilter);
} catch (RemoteException re) {
- mClients.remove(i);
- count--;
- i--;
+ /* ignore */
}
}
}
- /**
- * Gets the current state as a set of flags.
- *
- * @return The state.
- */
- private int getState() {
- int state = 0;
- if (mIsAccessibilityEnabled) {
- state |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
- }
- // Touch exploration relies on enabled accessibility.
- if (mIsAccessibilityEnabled && mIsTouchExplorationEnabled) {
- state |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+ private void showEnableTouchExplorationDialog(final Service service) {
+ String label = service.mResolveInfo.loadLabel(
+ mContext.getPackageManager()).toString();
+ synchronized (mLock) {
+ final UserState state = getCurrentUserStateLocked();
+ if (state.mIsTouchExplorationEnabled) {
+ return;
+ }
+ if (mEnableTouchExplorationDialog != null
+ && mEnableTouchExplorationDialog.isShowing()) {
+ return;
+ }
+ mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setPositiveButton(android.R.string.ok, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // The user allowed the service to toggle touch exploration.
+ state.mTouchExplorationGrantedServices.add(service.mComponentName);
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+ state.mTouchExplorationGrantedServices, state.mUserId);
+ // Enable touch exploration.
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
+ service.mUserId);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .setTitle(R.string.enable_explore_by_touch_warning_title)
+ .setMessage(mContext.getString(
+ R.string.enable_explore_by_touch_warning_message, label))
+ .create();
+ mEnableTouchExplorationDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
+ mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true);
+ mEnableTouchExplorationDialog.show();
}
- return state;
}
- /**
- * Updates the state of the input filter.
- */
- private void updateInputFilterLocked() {
- mMainHandler.obtainMessage(MSG_SEND_UPDATE_INPUT_FILTER).sendToTarget();
+ private int getClientState(UserState userState) {
+ int clientState = 0;
+ if (userState.mIsAccessibilityEnabled) {
+ clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+ }
+ // Touch exploration relies on enabled accessibility.
+ if (userState.mIsAccessibilityEnabled && userState.mIsTouchExplorationEnabled) {
+ clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+ }
+ return clientState;
}
- /**
- * Updated the internal state of this service to match the current settings.
- */
- private void updateInternalStateLocked() {
- populateInstalledAccessibilityServiceLocked();
- populateEnabledAccessibilityServicesLocked();
- populateTouchExplorationGrantedAccessibilityServicesLocked();
+ private void recreateInternalStateLocked(UserState userState) {
+ populateInstalledAccessibilityServiceLocked(userState);
+ populateEnabledAccessibilityServicesLocked(userState);
+ populateTouchExplorationGrantedAccessibilityServicesLocked(userState);
- handleTouchExplorationEnabledSettingChangedLocked();
- handleScreenMagnificationEnabledSettingChangedLocked();
- handleAccessibilityEnabledSettingChangedLocked();
+ handleTouchExplorationEnabledSettingChangedLocked(userState);
+ handleDisplayMagnificationEnabledSettingChangedLocked(userState);
+ handleAccessibilityEnabledSettingChangedLocked(userState);
- updateInputFilterLocked();
- sendStateToClientsLocked();
+ updateInputFilterLocked(userState);
+ scheduleSendStateToClientsLocked(userState);
}
- /**
- * Updated the state based on the accessibility enabled setting.
- */
- private void handleAccessibilityEnabledSettingChangedLocked() {
- mIsAccessibilityEnabled = Settings.Secure.getInt(
- mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
- if (mIsAccessibilityEnabled) {
- manageServicesLocked();
+ private void handleAccessibilityEnabledSettingChangedLocked(UserState userState) {
+ userState.mIsAccessibilityEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId) == 1;
+ if (userState.mIsAccessibilityEnabled ) {
+ manageServicesLocked(userState);
} else {
- unbindAllServicesLocked();
+ unbindAllServicesLocked(userState);
}
}
- /**
- * Updates the state based on the touch exploration enabled setting.
- */
- private void handleTouchExplorationEnabledSettingChangedLocked() {
- mIsTouchExplorationEnabled = Settings.Secure.getInt(
+ private void handleTouchExplorationEnabledSettingChangedLocked(UserState userState) {
+ userState.mIsTouchExplorationEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1;
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId) == 1;
}
- /**
- * Updates the state based on the screen magnification enabled setting.
- */
- private void handleScreenMagnificationEnabledSettingChangedLocked() {
- mIsScreenMagnificationEnabled = Settings.Secure.getInt(
+ private void handleDisplayMagnificationEnabledSettingChangedLocked(UserState userState) {
+ userState.mIsDisplayMagnificationEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1;
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+ 0, userState.mUserId) == 1;
}
- private void handleTouchExplorationGrantedAccessibilityServicesChangedLocked() {
- final int serviceCount = mServices.size();
+ private void handleTouchExplorationGrantedAccessibilityServicesChangedLocked(
+ UserState userState) {
+ final int serviceCount = userState.mServices.size();
for (int i = 0; i < serviceCount; i++) {
- Service service = mServices.get(i);
+ Service service = userState.mServices.get(i);
if (service.mRequestTouchExplorationMode
- && mTouchExplorationGrantedServices.contains(service.mComponentName)) {
+ && userState.mTouchExplorationGrantedServices.contains(
+ service.mComponentName)) {
tryEnableTouchExplorationLocked(service);
return;
}
}
- if (mIsTouchExplorationEnabled) {
- mMainHandler.obtainMessage(MSG_TOGGLE_TOUCH_EXPLORATION, 0,
- 0).sendToTarget();
+ if (userState.mIsTouchExplorationEnabled) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId);
}
}
private void tryEnableTouchExplorationLocked(final Service service) {
- if (!mIsTouchExplorationEnabled && service.mRequestTouchExplorationMode) {
- final boolean canToggleTouchExploration = mTouchExplorationGrantedServices.contains(
- service.mComponentName);
+ UserState userState = getUserStateLocked(service.mUserId);
+ if (!userState.mIsTouchExplorationEnabled && service.mRequestTouchExplorationMode) {
+ final boolean canToggleTouchExploration =
+ userState.mTouchExplorationGrantedServices.contains(service.mComponentName);
if (!service.mIsAutomation && !canToggleTouchExploration) {
- mMainHandler.obtainMessage(MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG,
- service).sendToTarget();
+ showEnableTouchExplorationDialog(service);
} else {
- mMainHandler.obtainMessage(MSG_TOGGLE_TOUCH_EXPLORATION, 1, 0).sendToTarget();
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, userState.mUserId);
}
}
}
private void tryDisableTouchExplorationLocked(Service service) {
- if (mIsTouchExplorationEnabled) {
- synchronized (mLock) {
- final int serviceCount = mServices.size();
- for (int i = 0; i < serviceCount; i++) {
- Service other = mServices.get(i);
- if (other != service && other.mRequestTouchExplorationMode) {
- return;
- }
+ UserState userState = getUserStateLocked(service.mUserId);
+ if (userState.mIsTouchExplorationEnabled) {
+ final int serviceCount = userState.mServices.size();
+ for (int i = 0; i < serviceCount; i++) {
+ Service other = userState.mServices.get(i);
+ if (other != service && other.mRequestTouchExplorationMode) {
+ return;
}
- mMainHandler.obtainMessage(MSG_TOGGLE_TOUCH_EXPLORATION, 0, 0).sendToTarget();
}
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId);
}
}
private class AccessibilityConnectionWrapper implements DeathRecipient {
private final int mWindowId;
+ private final int mUserId;
private final IAccessibilityInteractionConnection mConnection;
public AccessibilityConnectionWrapper(int windowId,
- IAccessibilityInteractionConnection connection) {
+ IAccessibilityInteractionConnection connection, int userId) {
mWindowId = windowId;
+ mUserId = userId;
mConnection = connection;
}
@@ -1062,12 +1156,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public void binderDied() {
unlinkToDeath();
synchronized (mLock) {
- removeAccessibilityInteractionConnectionLocked(mWindowId);
+ removeAccessibilityInteractionConnectionLocked(mWindowId, mUserId);
}
}
}
- private class MainHandler extends Handler {
+ private final class MainHandler extends Handler {
+ public static final int MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER = 1;
+ public static final int MSG_SEND_STATE_TO_CLIENTS = 2;
+ public static final int MSG_SEND_CLEARED_STATE_TO_CLIENTS_FOR_USER = 3;
+ public static final int MSG_SEND_RECREATE_INTERNAL_STATE = 4;
+ public static final int MSG_REGISTER_UI_TEST_AUTOMATION_SERVICE = 5;
public MainHandler(Looper looper) {
super(looper);
@@ -1077,104 +1176,70 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public void handleMessage(Message msg) {
final int type = msg.what;
switch (type) {
- case MSG_TOGGLE_TOUCH_EXPLORATION: {
- final int value = msg.arg1;
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, value);
- } break;
- case MSG_SHOW_ENABLE_TOUCH_EXPLORATION_DIALOG: {
- final Service service = (Service) msg.obj;
- String label = service.mResolveInfo.loadLabel(
- mContext.getPackageManager()).toString();
- synchronized (mLock) {
- if (mIsTouchExplorationEnabled) {
- return;
- }
- if (mEnableTouchExplorationDialog != null
- && mEnableTouchExplorationDialog.isShowing()) {
- return;
- }
- mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setPositiveButton(android.R.string.ok, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // The user allowed the service to toggle touch exploration.
- mTouchExplorationGrantedServices.add(service.mComponentName);
- persistComponentNamesToSettingLocked(
- Settings.Secure.
- TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- mTouchExplorationGrantedServices);
- // Enable touch exploration.
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1);
- }
- })
- .setNegativeButton(android.R.string.cancel, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- })
- .setTitle(R.string.enable_explore_by_touch_warning_title)
- .setMessage(mContext.getString(
- R.string.enable_explore_by_touch_warning_message, label))
- .create();
- mEnableTouchExplorationDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG);
- mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true);
- mEnableTouchExplorationDialog.show();
- }
- } break;
case MSG_SEND_ACCESSIBILITY_EVENT_TO_INPUT_FILTER: {
AccessibilityEvent event = (AccessibilityEvent) msg.obj;
- if (mHasInputFilter && mInputFilter != null) {
- mInputFilter.notifyAccessibilityEvent(event);
+ synchronized (mLock) {
+ if (mHasInputFilter && mInputFilter != null) {
+ mInputFilter.notifyAccessibilityEvent(event);
+ }
}
event.recycle();
} break;
- case MSG_SEND_UPDATE_INPUT_FILTER: {
- boolean setInputFilter = false;
- AccessibilityInputFilter inputFilter = null;
+ case MSG_SEND_STATE_TO_CLIENTS: {
+ final int clientState = msg.arg1;
+ final int userId = msg.arg2;
+ sendStateToClients(clientState, mGlobalClients);
+ sendStateToClientsForUser(clientState, userId);
+ } break;
+ case MSG_SEND_CLEARED_STATE_TO_CLIENTS_FOR_USER: {
+ final int userId = msg.arg1;
+ sendStateToClientsForUser(0, userId);
+ } break;
+ case MSG_SEND_RECREATE_INTERNAL_STATE: {
+ final int userId = msg.arg1;
synchronized (mLock) {
- if ((mIsAccessibilityEnabled && mIsTouchExplorationEnabled)
- || mIsScreenMagnificationEnabled) {
- if (!mHasInputFilter) {
- mHasInputFilter = true;
- if (mInputFilter == null) {
- mInputFilter = new AccessibilityInputFilter(mContext,
- AccessibilityManagerService.this);
- }
- inputFilter = mInputFilter;
- setInputFilter = true;
- }
- int flags = 0;
- if (mIsScreenMagnificationEnabled) {
- flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
- }
- if (mIsTouchExplorationEnabled) {
- flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
- }
- mInputFilter.setEnabledFeatures(flags);
- } else {
- if (mHasInputFilter) {
- mHasInputFilter = false;
- mInputFilter.setEnabledFeatures(0);
- inputFilter = null;
- setInputFilter = true;
- }
- }
+ UserState userState = getUserStateLocked(userId);
+ recreateInternalStateLocked(userState);
}
- if (setInputFilter) {
- try {
- mWindowManager.setInputFilter(inputFilter);
- } catch (RemoteException re) {
- /* ignore */
- }
+ } break;
+ case MSG_REGISTER_UI_TEST_AUTOMATION_SERVICE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ IAccessibilityServiceClient client =
+ (IAccessibilityServiceClient) args.arg1;
+ AccessibilityServiceInfo info = (AccessibilityServiceInfo) args.arg2;
+ registerUiTestAutomationService(client, info);
+ } finally {
+ args.recycle();
}
} break;
}
}
+
+ private void sendStateToClientsForUser(int clientState, int userId) {
+ final UserState userState;
+ synchronized (mLock) {
+ userState = getUserStateLocked(userId);
+ }
+ sendStateToClients(clientState, userState.mClients);
+ }
+
+ private void sendStateToClients(int clientState,
+ RemoteCallbackList<IAccessibilityManagerClient> clients) {
+ try {
+ final int userClientCount = clients.beginBroadcast();
+ for (int i = 0; i < userClientCount; i++) {
+ IAccessibilityManagerClient client = clients.getBroadcastItem(i);
+ try {
+ client.setState(clientState);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+ } finally {
+ clients.finishBroadcast();
+ }
+ }
}
/**
@@ -1192,6 +1257,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// used as message types allowing us to remove messages per event type.
private static final int MSG_ON_GESTURE = 0x80000000;
+ final int mUserId;
+
int mId = 0;
AccessibilityServiceInfo mAccessibilityServiceInfo;
@@ -1250,8 +1317,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
};
- public Service(ComponentName componentName,
+ public Service(int userId, ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo, boolean isAutomation) {
+ mUserId = userId;
mResolveInfo = accessibilityServiceInfo.getResolveInfo();
mId = sIdCounter++;
mComponentName = componentName;
@@ -1312,7 +1380,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
*/
public boolean bind() {
if (!mIsAutomation && mService == null) {
- return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
+ return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE, mUserId);
}
return false;
}
@@ -1355,17 +1423,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
@Override
public void setServiceInfo(AccessibilityServiceInfo info) {
- synchronized (mLock) {
- // If the XML manifest had data to configure the service its info
- // should be already set. In such a case update only the dynamically
- // configurable properties.
- AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
- if (oldInfo != null) {
- oldInfo.updateDynamicallyConfigurableProperties(info);
- setDynamicallyConfigurableProperties(oldInfo);
- } else {
- setDynamicallyConfigurableProperties(info);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ // If the XML manifest had data to configure the service its info
+ // should be already set. In such a case update only the dynamically
+ // configurable properties.
+ AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
+ if (oldInfo != null) {
+ oldInfo.updateDynamicallyConfigurableProperties(info);
+ setDynamicallyConfigurableProperties(oldInfo);
+ } else {
+ setDynamicallyConfigurableProperties(info);
+ }
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -1376,7 +1449,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
try {
mServiceInterface.setConnection(this, mId);
synchronized (mLock) {
- tryAddServiceLocked(this);
+ tryAddServiceLocked(this, mUserId);
}
} catch (RemoteException re) {
Slog.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
@@ -1388,14 +1461,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
long accessibilityNodeId, int viewId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+ final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return -1;
+ }
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
if (!permissionGranted) {
return 0;
} else {
+ resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
connection = getConnectionLocked(resolvedWindowId);
if (connection == null) {
return 0;
@@ -1425,10 +1505,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
long accessibilityNodeId, String text, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+ final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return -1;
+ }
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
+ resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
@@ -1464,10 +1551,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
long accessibilityNodeId, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
long interrogatingTid) throws RemoteException {
- final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+ final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return -1;
+ }
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
+ resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
@@ -1502,10 +1596,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
int focusType, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+ final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return -1;
+ }
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
+ resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
@@ -1540,10 +1641,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
int direction, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
throws RemoteException {
- final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+ final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return -1;
+ }
mSecurityPolicy.enforceCanRetrieveWindowContent(this);
+ resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted =
mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
if (!permissionGranted) {
@@ -1576,10 +1684,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
@Override
public boolean performAccessibilityAction(int accessibilityWindowId,
long accessibilityNodeId, int action, Bundle arguments, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
- final int resolvedWindowId = resolveAccessibilityWindowId(accessibilityWindowId);
+ IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
+ throws RemoteException {
+ final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return false;
+ }
+ mSecurityPolicy.enforceCanRetrieveWindowContent(this);
+ resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
resolvedWindowId, action, arguments);
if (!permissionGranted) {
@@ -1608,22 +1725,35 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return true;
}
- public boolean performGlobalAction(int action) {
- 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: {
- openRecents();
- } return true;
- case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
- expandStatusBar();
- } return true;
+ public boolean performGlobalAction(int action) throws RemoteException {
+ synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(
+ UserHandle.getCallingUserId());
+ if (resolvedUserId != mCurrentUserId) {
+ return false;
+ }
+ }
+ 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: {
+ openRecents();
+ } return true;
+ case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
+ expandStatusBar();
+ } return true;
+ }
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- return false;
}
public void onServiceDisconnected(ComponentName componentName) {
@@ -1658,7 +1788,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
// the state based on values in the settings database.
if (mIsAutomation) {
mUiAutomationService = null;
- updateInternalStateLocked();
+ recreateInternalStateLocked(getUserStateLocked(mUserId));
}
}
}
@@ -1817,8 +1947,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (DEBUG) {
Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
}
- AccessibilityConnectionWrapper wrapper = mWindowIdToInteractionConnectionWrapperMap.get(
- windowId);
+ AccessibilityConnectionWrapper wrapper = mGlobalInteractionConnections.get(windowId);
+ if (wrapper == null) {
+ wrapper = getCurrentUserStateLocked().mInteractionConnections.get(windowId);
+ }
if (wrapper != null && wrapper.mConnection != null) {
return wrapper.mConnection;
}
@@ -1828,7 +1960,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return null;
}
- private int resolveAccessibilityWindowId(int accessibilityWindowId) {
+ private int resolveAccessibilityWindowIdLocked(int accessibilityWindowId) {
if (accessibilityWindowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) {
return mSecurityPolicy.mActiveWindowId;
}
@@ -1836,9 +1968,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
private float getCompatibilityScale(int windowId) {
- IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId);
try {
- return mWindowManager.getWindowCompatibilityScale(windowToken);
+ IBinder windowToken = mGlobalWindowTokens.get(windowId);
+ if (windowToken != null) {
+ return mWindowManagerService.getWindowCompatibilityScale(windowToken);
+ }
+ windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
+ if (windowToken != null) {
+ return mWindowManagerService.getWindowCompatibilityScale(windowToken);
+ }
} catch (RemoteException re) {
/* ignore */
}
@@ -1941,6 +2079,38 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
}
+ public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID) {
+ return mCurrentUserId;
+ }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (callingUserId == userId) {
+ return userId;
+ }
+ if (!hasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ && !hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
+ throw new SecurityException("Call from user " + callingUserId + " as user "
+ + userId + " without permission INTERACT_ACROSS_USERS or "
+ + "INTERACT_ACROSS_USERS_FULL not allowed.");
+ }
+ if (userId == UserHandle.USER_CURRENT
+ || userId == UserHandle.USER_CURRENT_OR_SELF) {
+ return mCurrentUserId;
+ }
+ throw new IllegalArgumentException("Calling user can be changed to only "
+ + "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
+ }
+
+ public boolean isCallerInteractingAcrossUsers(int userId) {
+ final int callingUid = Binder.getCallingUid();
+ return (callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID
+ || userId == UserHandle.USER_CURRENT
+ || userId == UserHandle.USER_CURRENT_OR_SELF);
+ }
+
private boolean isRetrievalAllowingWindow(int windowId) {
return (mActiveWindowId == windowId);
}
@@ -1953,22 +2123,25 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
if (OWN_PROCESS_ID == Binder.getCallingPid()) {
return;
}
- final int permissionStatus = mContext.checkCallingPermission(permission);
- if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
+ if (hasPermission(permission)) {
throw new SecurityException("You do not have " + permission
+ " required to call " + function);
}
}
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
+ }
+
private int getFocusedWindowId() {
final long identity = Binder.clearCallingIdentity();
try {
// We call this only on window focus change or after touch
// exploration gesture end and the shown windows are not that
// many, so the linear look up is just fine.
- IBinder token = mWindowManager.getFocusedWindowToken();
+ IBinder token = mWindowManagerService.getFocusedWindowToken();
if (token != null) {
- SparseArray<IBinder> windows = mWindowIdToWindowTokenMap;
+ SparseArray<IBinder> windows = getCurrentUserStateLocked().mWindowTokens;
final int windowCount = windows.size();
for (int i = 0; i < windowCount; i++) {
if (windows.valueAt(i) == token) {
@@ -1984,4 +2157,129 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return -1;
}
}
+
+ private class UserState {
+ public final int mUserId;
+
+ public final CopyOnWriteArrayList<Service> mServices = new CopyOnWriteArrayList<Service>();
+
+ public final RemoteCallbackList<IAccessibilityManagerClient> mClients =
+ new RemoteCallbackList<IAccessibilityManagerClient>();
+
+ public final Map<ComponentName, Service> mComponentNameToServiceMap =
+ new HashMap<ComponentName, Service>();
+
+ public final List<AccessibilityServiceInfo> mInstalledServices =
+ new ArrayList<AccessibilityServiceInfo>();
+
+ public final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
+
+ public final Set<ComponentName> mTouchExplorationGrantedServices =
+ new HashSet<ComponentName>();
+
+ public final SparseArray<AccessibilityConnectionWrapper>
+ mInteractionConnections =
+ new SparseArray<AccessibilityConnectionWrapper>();
+
+ public final SparseArray<IBinder> mWindowTokens = new SparseArray<IBinder>();
+
+ public int mHandledFeedbackTypes = 0;
+
+ public boolean mIsAccessibilityEnabled;
+ public boolean mIsTouchExplorationEnabled;
+ public boolean mIsDisplayMagnificationEnabled;
+
+ public UserState(int userId) {
+ mUserId = userId;
+ }
+ }
+
+ private final class AccessibilityContentObserver extends ContentObserver {
+
+ private final Uri mAccessibilityEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_ENABLED);
+
+ private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED);
+
+ private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
+
+ private final Uri mEnabledAccessibilityServicesUri = Settings.Secure.getUriFor(
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+
+ private final Uri mTouchExplorationGrantedAccessibilityServicesUri = Settings.Secure
+ .getUriFor(Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
+
+ public AccessibilityContentObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void register(ContentResolver contentResolver) {
+ contentResolver.registerContentObserver(mAccessibilityEnabledUri,
+ false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(mTouchExplorationEnabledUri,
+ false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri,
+ false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri,
+ false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
+ mTouchExplorationGrantedAccessibilityServicesUri,
+ false, this, UserHandle.USER_ALL);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (mAccessibilityEnabledUri.equals(uri)) {
+ synchronized (mLock) {
+ // We will update when the automation service dies.
+ if (mUiAutomationService == null) {
+ UserState userState = getCurrentUserStateLocked();
+ handleAccessibilityEnabledSettingChangedLocked(userState);
+ updateInputFilterLocked(userState);
+ scheduleSendStateToClientsLocked(userState);
+ }
+ }
+ } else if (mTouchExplorationEnabledUri.equals(uri)) {
+ synchronized (mLock) {
+ // We will update when the automation service dies.
+ if (mUiAutomationService == null) {
+ UserState userState = getCurrentUserStateLocked();
+ handleTouchExplorationEnabledSettingChangedLocked(userState);
+ updateInputFilterLocked(userState);
+ scheduleSendStateToClientsLocked(userState);
+ }
+ }
+ } else if (mDisplayMagnificationEnabledUri.equals(uri)) {
+ synchronized (mLock) {
+ // We will update when the automation service dies.
+ if (mUiAutomationService == null) {
+ UserState userState = getCurrentUserStateLocked();
+ handleDisplayMagnificationEnabledSettingChangedLocked(userState);
+ updateInputFilterLocked(userState);
+ scheduleSendStateToClientsLocked(userState);
+ }
+ }
+ } else if (mEnabledAccessibilityServicesUri.equals(uri)) {
+ synchronized (mLock) {
+ // We will update when the automation service dies.
+ if (mUiAutomationService == null) {
+ UserState userState = getCurrentUserStateLocked();
+ populateEnabledAccessibilityServicesLocked(userState);
+ manageServicesLocked(userState);
+ }
+ }
+ } else if (mTouchExplorationGrantedAccessibilityServicesUri.equals(uri)) {
+ synchronized (mLock) {
+ // We will update when the automation service dies.
+ if (mUiAutomationService == null) {
+ UserState userState = getCurrentUserStateLocked();
+ populateTouchExplorationGrantedAccessibilityServicesLocked(userState);
+ handleTouchExplorationGrantedAccessibilityServicesChangedLocked(userState);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index cb6b31a..c84f988 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -1150,20 +1150,9 @@ class TouchExplorer implements EventStreamTransformation {
return;
}
- if (Build.IS_DEBUGGABLE) {
- if (mSendHoverEnterDelayed.isPending()) {
- throw new IllegalStateException("mSendHoverEnterDelayed must not be pending.");
- }
- if (mSendHoverExitDelayed.isPending()) {
- throw new IllegalStateException("mSendHoverExitDelayed must not be pending.");
- }
- if (!mPerformLongPressDelayed.isPending()) {
- throw new IllegalStateException(
- "mPerformLongPressDelayed must not be pending.");
- }
- }
-
// Remove pending event deliveries.
+ mSendHoverEnterDelayed.remove();
+ mSendHoverExitDelayed.remove();
mPerformLongPressDelayed.remove();
// The touch interaction has ended since we will send a click.
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
index 46bcc4a..fd9fc98 100644
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
@@ -25,6 +25,7 @@ import android.os.IBinder;
import android.os.Message;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.Settings;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
@@ -98,7 +99,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
// invoke the method under test
- final int stateFlagsDisabled = mManagerService.addClient(mockClient);
+ final int stateFlagsDisabled = mManagerService.addClient(mockClient, UserHandle.USER_OWNER);
boolean enabledAccessibilityDisabled =
(stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -110,7 +111,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
ensureAccessibilityEnabled(mContext, true);
// invoke the method under test
- final int stateFlagsEnabled = mManagerService.addClient(mockClient);
+ final int stateFlagsEnabled = mManagerService.addClient(mockClient, UserHandle.USER_OWNER);
boolean enabledAccessibilityEnabled =
(stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -129,7 +130,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
// invoke the method under test
- final int stateFlagsEnabled = mManagerService.addClient(mockClient);
+ final int stateFlagsEnabled = mManagerService.addClient(mockClient, UserHandle.USER_OWNER);
boolean enabledAccessibilityEnabled =
(stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -141,7 +142,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
ensureAccessibilityEnabled(mContext, false);
// invoke the method under test
- final int stateFlagsDisabled = mManagerService.addClient(mockClient);
+ final int stateFlagsDisabled = mManagerService.addClient(mockClient, UserHandle.USER_OWNER);
boolean enabledAccessibilityDisabled =
(stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
@@ -160,7 +161,8 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
String secondMockServiceClassName = MySecondMockAccessibilityService.class.getName();
// look for the two mock services
- for (AccessibilityServiceInfo info : mManagerService.getInstalledAccessibilityServiceList()) {
+ for (AccessibilityServiceInfo info : mManagerService.getInstalledAccessibilityServiceList(
+ UserHandle.USER_OWNER)) {
ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
if (packageName.equals(serviceInfo.packageName)) {
if (firstMockServiceClassName.equals(serviceInfo.name)) {
@@ -201,7 +203,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
service.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(service);
@@ -231,7 +233,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
service.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(service);
@@ -261,7 +263,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
service.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(service);
@@ -297,8 +299,8 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
service.replay();
// send the events
- mManagerService.sendAccessibilityEvent(firstEvent);
- mManagerService.sendAccessibilityEvent(secondEvent);
+ mManagerService.sendAccessibilityEvent(firstEvent, UserHandle.USER_OWNER);
+ mManagerService.sendAccessibilityEvent(secondEvent, UserHandle.USER_OWNER);
// wait for #sendAccessibilityEvent to reach the backing service
Thread.sleep(TIMEOUT_BINDER_CALL);
@@ -354,7 +356,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
secondService.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
@@ -393,7 +395,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
secondService.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
@@ -434,7 +436,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
secondService.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
@@ -477,7 +479,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
secondService.replay();
// send the event
- mManagerService.sendAccessibilityEvent(sentEvent);
+ mManagerService.sendAccessibilityEvent(sentEvent, UserHandle.USER_OWNER);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
@@ -512,7 +514,7 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase {
secondService.replay();
// call the method under test
- mManagerService.interrupt();
+ mManagerService.interrupt(UserHandle.USER_OWNER);
// verify if all expected methods have been called
assertMockServiceVerifiedWithinTimeout(firstService);
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
index e083815..e7366ea 100644
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
@@ -26,7 +26,7 @@ import static org.easymock.EasyMock.verify;
import org.easymock.IArgumentMatcher;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.content.pm.ServiceInfo;
+import android.os.UserHandle;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
@@ -70,14 +70,16 @@ public class AccessibilityManagerTest extends AndroidTestCase {
// configure the mock service behavior
IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(
+ expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
+ UserHandle.USER_OWNER)).andReturn(
AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
- expect(mockServiceInterface.getInstalledAccessibilityServiceList()).andReturn(
- expectedServices);
+ expect(mockServiceInterface.getInstalledAccessibilityServiceList(UserHandle.USER_OWNER))
+ .andReturn(expectedServices);
replay(mockServiceInterface);
// invoke the method under test
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+ AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
+ UserHandle.USER_OWNER);
List<AccessibilityServiceInfo> receivedServices =
manager.getInstalledAccessibilityServiceList();
@@ -92,13 +94,15 @@ public class AccessibilityManagerTest extends AndroidTestCase {
public void testInterrupt() throws Exception {
// configure the mock service behavior
IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(
- AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
- mockServiceInterface.interrupt();
+ expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
+ UserHandle.USER_OWNER)).andReturn(
+ AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
+ mockServiceInterface.interrupt(UserHandle.USER_OWNER);
replay(mockServiceInterface);
// invoke the method under test
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+ AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
+ UserHandle.USER_OWNER);
manager.interrupt();
// verify the mock service was properly called
@@ -109,12 +113,14 @@ public class AccessibilityManagerTest extends AndroidTestCase {
public void testIsEnabled() throws Exception {
// configure the mock service behavior
IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(
- AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
+ expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
+ UserHandle.USER_OWNER)).andReturn(
+ AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
replay(mockServiceInterface);
// invoke the method under test
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+ AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
+ UserHandle.USER_OWNER);
boolean isEnabledServiceEnabled = manager.isEnabled();
// check expected result
@@ -144,16 +150,18 @@ public class AccessibilityManagerTest extends AndroidTestCase {
// configure the mock service behavior
IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(
- AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
- expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent)))
- .andReturn(true);
- expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent)))
- .andReturn(false);
+ expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
+ UserHandle.USER_OWNER)).andReturn(
+ AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
+ expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent),
+ UserHandle.USER_OWNER)).andReturn(true);
+ expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent),
+ UserHandle.USER_OWNER)).andReturn(false);
replay(mockServiceInterface);
// invoke the method under test (manager and service in different processes)
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+ AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
+ UserHandle.USER_OWNER);
manager.sendAccessibilityEvent(sentEvent);
// check expected result
@@ -180,11 +188,13 @@ public class AccessibilityManagerTest extends AndroidTestCase {
// configure the mock service behavior
IAccessibilityManager mockServiceInterface = mMockServiceInterface;
- expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient())).andReturn(0);
+ expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
+ UserHandle.USER_OWNER)).andReturn(0);
replay(mockServiceInterface);
// invoke the method under test (accessibility disabled)
- AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface);
+ AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
+ UserHandle.USER_OWNER);
try {
manager.sendAccessibilityEvent(sentEvent);
fail("No accessibility events are sent if accessibility is disabled");
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index cd7ee76..7b7a7b3 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -38,10 +38,8 @@ import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
@@ -216,6 +214,12 @@ public class MockPackageManager extends PackageManager {
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
throw new UnsupportedOperationException();
@@ -223,7 +227,7 @@ public class MockPackageManager extends PackageManager {
/** @hide */
@Override
- public List<ResolveInfo> queryIntentActivitiesForUser(Intent intent,
+ public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
int flags, int userId) {
throw new UnsupportedOperationException();
}
@@ -255,6 +259,12 @@ public class MockPackageManager extends PackageManager {
throw new UnsupportedOperationException();
}
+ /** @hide */
+ @Override
+ public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
+ throw new UnsupportedOperationException();
+ }
+
@Override
public ProviderInfo resolveContentProvider(String name, int flags) {
throw new UnsupportedOperationException();