Conditionally allow non-primay profiles to use TextServices
With this CL, non-primary profiles become able to use TextServices (spelling correction services) if and only if the active spell checker service is the system's one. Basically this CL just copies the same logic from InputMethodManagerService that were implemented as I3bd87b32aec69c3f8d and Id5d4f29017b7ca6844632 with adding a condition to see if the spell checker is system's one or not. Note that this is a tentative solution and can be removed once TSMS fully supports multiuser. Also note that this CL heavily relies on the fact that setting apps isonly running in the current user. This is because that some risky operations that are exposed from TSMS are non-public APIs an accessible only from @hide methods in TextServicesManager, and those @hide methods are actually used only by the settings app so far. BUG: 16285536 Change-Id: Iae9045ba5baccd04ed68906e7afb9160677ec4a5
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
import com.android.internal.textservice.ISpellCheckerService;
|
||||
import com.android.internal.textservice.ISpellCheckerSession;
|
||||
@@ -28,14 +29,18 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
import android.app.ActivityManagerNative;
|
||||
import android.app.AppGlobals;
|
||||
import android.app.IUserSwitchObserver;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -43,6 +48,7 @@ import android.os.IRemoteCallback;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.service.textservice.SpellCheckerService;
|
||||
import android.text.TextUtils;
|
||||
@@ -84,6 +90,12 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
public TextServicesManagerService(Context context) {
|
||||
mSystemReady = false;
|
||||
mContext = context;
|
||||
|
||||
final IntentFilter broadcastFilter = new IntentFilter();
|
||||
broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
|
||||
broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
|
||||
mContext.registerReceiver(new TextServicesBroadcastReceiver(), broadcastFilter);
|
||||
|
||||
int userId = UserHandle.USER_OWNER;
|
||||
try {
|
||||
ActivityManagerNative.getDefault().registerUserSwitchObserver(
|
||||
@@ -119,6 +131,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
|
||||
private void switchUserLocked(int userId) {
|
||||
mSettings.setCurrentUserId(userId);
|
||||
updateCurrentProfileIds();
|
||||
unbindServiceLocked();
|
||||
buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings);
|
||||
SpellCheckerInfo sci = getCurrentSpellChecker(null);
|
||||
@@ -133,6 +146,16 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
void updateCurrentProfileIds() {
|
||||
List<UserInfo> profiles =
|
||||
UserManager.get(mContext).getProfiles(mSettings.getCurrentUserId());
|
||||
int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null
|
||||
for (int i = 0; i < currentProfileIds.length; i++) {
|
||||
currentProfileIds[i] = profiles.get(i).id;
|
||||
}
|
||||
mSettings.setCurrentProfileIds(currentProfileIds);
|
||||
}
|
||||
|
||||
private class TextServicesMonitor extends PackageMonitor {
|
||||
private boolean isChangingPackagesOfCurrentUser() {
|
||||
final int userId = getChangingUserId();
|
||||
@@ -171,6 +194,19 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
class TextServicesBroadcastReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
final String action = intent.getAction();
|
||||
if (Intent.ACTION_USER_ADDED.equals(action)
|
||||
|| Intent.ACTION_USER_REMOVED.equals(action)) {
|
||||
updateCurrentProfileIds();
|
||||
return;
|
||||
}
|
||||
Slog.w(TAG, "Unexpected intent " + intent);
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildSpellCheckerMapLocked(Context context,
|
||||
ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map,
|
||||
TextServicesSettings settings) {
|
||||
@@ -223,7 +259,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? "
|
||||
+ "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID
|
||||
+ " calling userId = " + userId + ", foreground user id = "
|
||||
+ mSettings.getCurrentUserId());
|
||||
+ mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid());
|
||||
try {
|
||||
final String[] packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
|
||||
for (int i = 0; i < packageNames.length; ++i) {
|
||||
@@ -237,10 +273,40 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
|
||||
if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) {
|
||||
return true;
|
||||
} else {
|
||||
Slog.w(TAG, "--- IPC called from background users. Ignore. \n" + getStackTrace());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Permits current profile to use TSFM as long as the current text service is the system's
|
||||
// one. This is a tentative solution and should be replaced with fully functional multiuser
|
||||
// support.
|
||||
// TODO: Implement multiuser support in TSMS.
|
||||
final boolean isCurrentProfile = mSettings.isCurrentProfile(userId);
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "--- userId = "+ userId + " isCurrentProfile = " + isCurrentProfile);
|
||||
}
|
||||
if (mSettings.isCurrentProfile(userId)) {
|
||||
final SpellCheckerInfo spellCheckerInfo = getCurrentSpellCheckerWithoutVerification();
|
||||
if (spellCheckerInfo != null) {
|
||||
final ServiceInfo serviceInfo = spellCheckerInfo.getServiceInfo();
|
||||
final boolean isSystemSpellChecker =
|
||||
(serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "--- current spell checker = "+ spellCheckerInfo.getPackageName()
|
||||
+ " isSystem = " + isSystemSpellChecker);
|
||||
}
|
||||
if (isSystemSpellChecker) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unlike InputMethodManagerService#calledFromValidUser, INTERACT_ACROSS_USERS_FULL isn't
|
||||
// taken into account here. Anyway this method is supposed to be removed once multiuser
|
||||
// support is implemented.
|
||||
if (DBG) {
|
||||
Slog.d(TAG, "--- IPC from userId:" + userId + " is being ignored. \n"
|
||||
+ getStackTrace());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean bindCurrentSpellCheckerService(
|
||||
@@ -292,6 +358,10 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
if (!calledFromValidUser()) {
|
||||
return null;
|
||||
}
|
||||
return getCurrentSpellCheckerWithoutVerification();
|
||||
}
|
||||
|
||||
private SpellCheckerInfo getCurrentSpellCheckerWithoutVerification() {
|
||||
synchronized (mSpellCheckerMap) {
|
||||
final String curSpellCheckerId = mSettings.getSelectedSpellChecker();
|
||||
if (DBG) {
|
||||
@@ -914,6 +984,10 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
private static class TextServicesSettings {
|
||||
private final ContentResolver mResolver;
|
||||
private int mCurrentUserId;
|
||||
@GuardedBy("mLock")
|
||||
private int[] mCurrentProfileIds = new int[0];
|
||||
private Object mLock = new Object();
|
||||
|
||||
public TextServicesSettings(ContentResolver resolver, int userId) {
|
||||
mResolver = resolver;
|
||||
mCurrentUserId = userId;
|
||||
@@ -928,6 +1002,22 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
|
||||
mCurrentUserId = userId;
|
||||
}
|
||||
|
||||
public void setCurrentProfileIds(int[] currentProfileIds) {
|
||||
synchronized (mLock) {
|
||||
mCurrentProfileIds = currentProfileIds;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCurrentProfile(int userId) {
|
||||
synchronized (mLock) {
|
||||
if (userId == mCurrentUserId) return true;
|
||||
for (int i = 0; i < mCurrentProfileIds.length; i++) {
|
||||
if (userId == mCurrentProfileIds[i]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCurrentUserId() {
|
||||
return mCurrentUserId;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user