Skip to content

Commit 58d37b5

Browse files
committed
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
1 parent 059aedf commit 58d37b5

File tree

10 files changed

+1010
-593
lines changed

10 files changed

+1010
-593
lines changed

core/java/android/app/ApplicationPackageManager.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,8 @@
4242
import android.content.pm.ResolveInfo;
4343
import android.content.pm.ServiceInfo;
4444
import android.content.pm.ManifestDigest;
45-
import android.content.pm.UserInfo;
4645
import android.content.pm.VerificationParams;
4746
import android.content.pm.VerifierDeviceIdentity;
48-
import android.content.pm.PackageManager.NameNotFoundException;
4947
import android.content.res.Resources;
5048
import android.content.res.XmlResourceParser;
5149
import android.graphics.drawable.Drawable;
@@ -453,11 +451,17 @@ public List<ApplicationInfo> getInstalledApplications(int flags) {
453451

454452
@Override
455453
public ResolveInfo resolveActivity(Intent intent, int flags) {
454+
return resolveActivityAsUser(intent, flags, UserHandle.myUserId());
455+
}
456+
457+
@Override
458+
public ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
456459
try {
457460
return mPM.resolveIntent(
458461
intent,
459462
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
460-
flags, UserHandle.myUserId());
463+
flags,
464+
userId);
461465
} catch (RemoteException e) {
462466
throw new RuntimeException("Package manager has died", e);
463467
}
@@ -466,12 +470,12 @@ public ResolveInfo resolveActivity(Intent intent, int flags) {
466470
@Override
467471
public List<ResolveInfo> queryIntentActivities(Intent intent,
468472
int flags) {
469-
return queryIntentActivitiesForUser(intent, flags, UserHandle.myUserId());
473+
return queryIntentActivitiesAsUser(intent, flags, UserHandle.myUserId());
470474
}
471475

472476
/** @hide Same as above but for a specific user */
473477
@Override
474-
public List<ResolveInfo> queryIntentActivitiesForUser(Intent intent,
478+
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
475479
int flags, int userId) {
476480
try {
477481
return mPM.queryIntentActivities(
@@ -551,18 +555,23 @@ public ResolveInfo resolveService(Intent intent, int flags) {
551555
}
552556

553557
@Override
554-
public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
558+
public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
555559
try {
556560
return mPM.queryIntentServices(
557561
intent,
558562
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
559563
flags,
560-
UserHandle.myUserId());
564+
userId);
561565
} catch (RemoteException e) {
562566
throw new RuntimeException("Package manager has died", e);
563567
}
564568
}
565569

570+
@Override
571+
public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
572+
return queryIntentServicesAsUser(intent, flags, UserHandle.myUserId());
573+
}
574+
566575
@Override
567576
public ProviderInfo resolveContentProvider(String name,
568577
int flags) {

core/java/android/content/pm/PackageManager.java

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,39 @@ public abstract int getUidForSharedUser(String sharedUserName)
17961796
*/
17971797
public abstract ResolveInfo resolveActivity(Intent intent, int flags);
17981798

1799+
/**
1800+
* Determine the best action to perform for a given Intent for a given user. This
1801+
* is how {@link Intent#resolveActivity} finds an activity if a class has not
1802+
* been explicitly specified.
1803+
*
1804+
* <p><em>Note:</em> if using an implicit Intent (without an explicit ComponentName
1805+
* specified), be sure to consider whether to set the {@link #MATCH_DEFAULT_ONLY}
1806+
* only flag. You need to do so to resolve the activity in the same way
1807+
* that {@link android.content.Context#startActivity(Intent)} and
1808+
* {@link android.content.Intent#resolveActivity(PackageManager)
1809+
* Intent.resolveActivity(PackageManager)} do.</p>
1810+
*
1811+
* @param intent An intent containing all of the desired specification
1812+
* (action, data, type, category, and/or component).
1813+
* @param flags Additional option flags. The most important is
1814+
* {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
1815+
* those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
1816+
* @param userId The user id.
1817+
*
1818+
* @return Returns a ResolveInfo containing the final activity intent that
1819+
* was determined to be the best action. Returns null if no
1820+
* matching activity was found. If multiple matching activities are
1821+
* found and there is no default set, returns a ResolveInfo
1822+
* containing something else, such as the activity resolver.
1823+
*
1824+
* @see #MATCH_DEFAULT_ONLY
1825+
* @see #GET_INTENT_FILTERS
1826+
* @see #GET_RESOLVED_FILTER
1827+
*
1828+
* @hide
1829+
*/
1830+
public abstract ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);
1831+
17991832
/**
18001833
* Retrieve all activities that can be performed for the given intent.
18011834
*
@@ -1836,7 +1869,7 @@ public abstract List<ResolveInfo> queryIntentActivities(Intent intent,
18361869
* @see #GET_RESOLVED_FILTER
18371870
* @hide
18381871
*/
1839-
public abstract List<ResolveInfo> queryIntentActivitiesForUser(Intent intent,
1872+
public abstract List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
18401873
int flags, int userId);
18411874

18421875

@@ -1943,6 +1976,27 @@ public abstract List<ResolveInfo> queryBroadcastReceivers(Intent intent,
19431976
public abstract List<ResolveInfo> queryIntentServices(Intent intent,
19441977
int flags);
19451978

1979+
/**
1980+
* Retrieve all services that can match the given intent for a given user.
1981+
*
1982+
* @param intent The desired intent as per resolveService().
1983+
* @param flags Additional option flags.
1984+
* @param userId The user id.
1985+
*
1986+
* @return A List&lt;ResolveInfo&gt; containing one entry for each matching
1987+
* ServiceInfo. These are ordered from best to worst match -- that
1988+
* is, the first item in the list is what is returned by
1989+
* resolveService(). If there are no matching services, an empty
1990+
* list is returned.
1991+
*
1992+
* @see #GET_INTENT_FILTERS
1993+
* @see #GET_RESOLVED_FILTER
1994+
*
1995+
* @hide
1996+
*/
1997+
public abstract List<ResolveInfo> queryIntentServicesAsUser(Intent intent,
1998+
int flags, int userId);
1999+
19462000
/**
19472001
* Find a single content provider by its base path name.
19482002
*

core/java/android/view/accessibility/AccessibilityManager.java

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import android.os.RemoteException;
2828
import android.os.ServiceManager;
2929
import android.os.SystemClock;
30+
import android.os.UserHandle;
3031
import android.util.Log;
3132
import android.view.IWindow;
3233
import android.view.View;
@@ -79,6 +80,8 @@ public final class AccessibilityManager {
7980

8081
final IAccessibilityManager mService;
8182

83+
final int mUserId;
84+
8285
final Handler mHandler;
8386

8487
boolean mIsEnabled;
@@ -128,36 +131,73 @@ public void handleMessage(Message message) {
128131
}
129132
}
130133

134+
/**
135+
* Creates the singleton AccessibilityManager to be shared across users. This
136+
* has to be called before the local AccessibilityManager is created to ensure
137+
* it registers itself in the system correctly.
138+
* <p>
139+
* Note: Calling this method requires INTERACT_ACROSS_USERS_FULL or
140+
* INTERACT_ACROSS_USERS permission.
141+
* </p>
142+
* @param context Context in which this manager operates.
143+
* @throws IllegalStateException if not called before the local
144+
* AccessibilityManager is instantiated.
145+
*
146+
* @hide
147+
*/
148+
public static void createAsSharedAcrossUsers(Context context) {
149+
synchronized (sInstanceSync) {
150+
if (sInstance != null) {
151+
throw new IllegalStateException("AccessibilityManager already created.");
152+
}
153+
createSingletonInstance(context, UserHandle.USER_CURRENT);
154+
}
155+
}
156+
131157
/**
132158
* Get an AccessibilityManager instance (create one if necessary).
133159
*
160+
* @param context Context in which this manager operates.
161+
*
134162
* @hide
135163
*/
136164
public static AccessibilityManager getInstance(Context context) {
137165
synchronized (sInstanceSync) {
138166
if (sInstance == null) {
139-
IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
140-
IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
141-
sInstance = new AccessibilityManager(context, service);
167+
createSingletonInstance(context, UserHandle.myUserId());
142168
}
143169
}
144170
return sInstance;
145171
}
146172

173+
/**
174+
* Creates the singleton instance.
175+
*
176+
* @param context Context in which this manager operates.
177+
* @param userId The user id under which to operate.
178+
*/
179+
private static void createSingletonInstance(Context context, int userId) {
180+
IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
181+
IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
182+
sInstance = new AccessibilityManager(context, service, userId);
183+
}
184+
147185
/**
148186
* Create an instance.
149187
*
150188
* @param context A {@link Context}.
151189
* @param service An interface to the backing service.
190+
* @param userId User id under which to run.
152191
*
153192
* @hide
154193
*/
155-
public AccessibilityManager(Context context, IAccessibilityManager service) {
194+
public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
156195
mHandler = new MyHandler(context.getMainLooper());
157196
mService = service;
197+
mUserId = userId;
158198

159199
try {
160-
final int stateFlags = mService.addClient(mClient);
200+
final int stateFlags = mService.addClient(mClient, userId);
161201
setState(stateFlags);
162202
} catch (RemoteException re) {
163203
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
@@ -222,7 +262,7 @@ public void sendAccessibilityEvent(AccessibilityEvent event) {
222262
// client using it is called through Binder from another process. Example: MMS
223263
// app adds a SMS notification and the NotificationManagerService calls this method
224264
long identityToken = Binder.clearCallingIdentity();
225-
doRecycle = mService.sendAccessibilityEvent(event);
265+
doRecycle = mService.sendAccessibilityEvent(event, mUserId);
226266
Binder.restoreCallingIdentity(identityToken);
227267
if (DEBUG) {
228268
Log.i(LOG_TAG, event + " sent");
@@ -244,7 +284,7 @@ public void interrupt() {
244284
throw new IllegalStateException("Accessibility off. Did you forget to check that?");
245285
}
246286
try {
247-
mService.interrupt();
287+
mService.interrupt(mUserId);
248288
if (DEBUG) {
249289
Log.i(LOG_TAG, "Requested interrupt from all services");
250290
}
@@ -280,7 +320,7 @@ public List<ServiceInfo> getAccessibilityServiceList() {
280320
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
281321
List<AccessibilityServiceInfo> services = null;
282322
try {
283-
services = mService.getInstalledAccessibilityServiceList();
323+
services = mService.getInstalledAccessibilityServiceList(mUserId);
284324
if (DEBUG) {
285325
Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
286326
}
@@ -307,7 +347,7 @@ public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
307347
int feedbackTypeFlags) {
308348
List<AccessibilityServiceInfo> services = null;
309349
try {
310-
services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags);
350+
services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags, mUserId);
311351
if (DEBUG) {
312352
Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
313353
}
@@ -385,7 +425,7 @@ private void notifyAccessibilityStateChanged() {
385425
public int addAccessibilityInteractionConnection(IWindow windowToken,
386426
IAccessibilityInteractionConnection connection) {
387427
try {
388-
return mService.addAccessibilityInteractionConnection(windowToken, connection);
428+
return mService.addAccessibilityInteractionConnection(windowToken, connection, mUserId);
389429
} catch (RemoteException re) {
390430
Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
391431
}

core/java/android/view/accessibility/IAccessibilityManager.aidl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,18 @@ import android.view.IWindow;
3434
*/
3535
interface IAccessibilityManager {
3636

37-
int addClient(IAccessibilityManagerClient client);
37+
int addClient(IAccessibilityManagerClient client, int userId);
3838

39-
boolean sendAccessibilityEvent(in AccessibilityEvent uiEvent);
39+
boolean sendAccessibilityEvent(in AccessibilityEvent uiEvent, int userId);
4040

41-
List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
41+
List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId);
4242

43-
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType);
43+
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
4444

45-
void interrupt();
45+
void interrupt(int userId);
4646

4747
int addAccessibilityInteractionConnection(IWindow windowToken,
48-
in IAccessibilityInteractionConnection connection);
48+
in IAccessibilityInteractionConnection connection, int userId);
4949

5050
void removeAccessibilityInteractionConnection(IWindow windowToken);
5151

packages/SystemUI/src/com/android/systemui/SystemUIService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import android.util.Slog;
3333
import android.view.IWindowManager;
3434
import android.view.WindowManagerGlobal;
35+
import android.view.accessibility.AccessibilityManager;
3536

3637
public class SystemUIService extends Service {
3738
static final String TAG = "SystemUIService";
@@ -67,6 +68,10 @@ private Class chooseClass(Object o) {
6768

6869
@Override
6970
public void onCreate() {
71+
// Tell the accessibility layer that this process will
72+
// run as the current user, i.e. run across users.
73+
AccessibilityManager.createAsSharedAcrossUsers(this);
74+
7075
// Pick status bar or system bar.
7176
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
7277
try {

0 commit comments

Comments
 (0)