summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Guy <kennyguy@google.com>2014-08-20 19:40:59 +0100
committerKenny Guy <kennyguy@google.com>2014-08-20 22:30:05 +0100
commitfa80a4faa3ab32f61742b684e832126dae8468e7 (patch)
tree399edfb680ca4f5dbf3aa392956f0ddad4faab19
parent833dcd4bea5cadccaa55f9a06942bb36d01d73ac (diff)
downloadframeworks_base-fa80a4faa3ab32f61742b684e832126dae8468e7.zip
frameworks_base-fa80a4faa3ab32f61742b684e832126dae8468e7.tar.gz
frameworks_base-fa80a4faa3ab32f61742b684e832126dae8468e7.tar.bz2
Add apis for whitelisting IMEs and accessibility services.
Adds apis for device / profile owners to white list the accessibility and input methods a user can enable. Bug: 14469005 Change-Id: I921888660d29a5370395db87adf75d4d106660c9
-rw-r--r--api/current.txt4
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java155
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java415
4 files changed, 582 insertions, 1 deletions
diff --git a/api/current.txt b/api/current.txt
index 0d8aeba..d2abbb7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5431,6 +5431,8 @@ package android.app.admin {
method public int getPasswordMinimumSymbols(android.content.ComponentName);
method public int getPasswordMinimumUpperCase(android.content.ComponentName);
method public int getPasswordQuality(android.content.ComponentName);
+ method public java.util.List<java.lang.String> getPermittedAccessibilityServices(android.content.ComponentName);
+ method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
method public boolean getScreenCaptureDisabled(android.content.ComponentName);
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
@@ -5473,6 +5475,8 @@ package android.app.admin {
method public void setPasswordMinimumSymbols(android.content.ComponentName, int);
method public void setPasswordMinimumUpperCase(android.content.ComponentName, int);
method public void setPasswordQuality(android.content.ComponentName, int);
+ method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
+ method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
method public void setProfileEnabled(android.content.ComponentName);
method public void setProfileName(android.content.ComponentName, java.lang.String);
method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 053afb7..82e7e3d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2622,6 +2622,161 @@ public class DevicePolicyManager {
}
/**
+ * Called by a profile or device owner to set the permitted accessibility services. When
+ * set by a device owner or profile owner the restriction applies to all profiles of the
+ * user the device owner or profile owner is an admin for.
+ *
+ * By default the user can use any accessiblity service. When zero or more packages have
+ * been added, accessiblity services that are not in the list and not part of the system
+ * can not be enabled by the user.
+ *
+ * <p> Calling with a null value for the list disables the restriction so that all services
+ * can be used, calling with an empty list only allows the builtin system's services.
+ *
+ * <p> System accesibility services are always available to the user the list can't modify
+ * this.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageNames List of accessibility service package names.
+ *
+ * @return true if setting the restriction succeeded. It fail if there is
+ * one or more non-system accessibility services enabled, that are not in the list.
+ */
+ public boolean setPermittedAccessibilityServices(ComponentName admin,
+ List<String> packageNames) {
+ if (mService != null) {
+ try {
+ return mService.setPermittedAccessibilityServices(admin, packageNames);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the list of permitted accessibility services set by this device or profile owner.
+ *
+ * <p>An empty list means no accessibility services except system services are allowed.
+ * Null means all accessibility services are allowed.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @return List of accessiblity service package names.
+ */
+ public List<String> getPermittedAccessibilityServices(ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getPermittedAccessibilityServices(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the list of accessibility services permitted by the device or profiles
+ * owners of this user.
+ *
+ * <p>Null means all accessibility services are allowed, if a non-null list is returned
+ * it will contain the intersection of the permitted lists for any device or profile
+ * owners that apply to this user. It will also include any system accessibility services.
+ *
+ * @param userId which user to check for.
+ * @return List of accessiblity service package names.
+ * @hide
+ */
+ @SystemApi
+ public List<String> getPermittedAccessibilityServices(int userId) {
+ if (mService != null) {
+ try {
+ return mService.getPermittedAccessibilityServicesForUser(userId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Called by a profile or device owner to set the permitted input methods services. When
+ * set by a device owner or profile owner the restriction applies to all profiles of the
+ * user the device owner or profile owner is an admin for.
+ *
+ * By default the user can use any input method. When zero or more packages have
+ * been added, input method that are not in the list and not part of the system
+ * can not be enabled by the user.
+ *
+ * This method will fail if it is called for a admin that is not for the foreground user
+ * or a profile of the foreground user.
+ *
+ * <p> Calling with a null value for the list disables the restriction so that all input methods
+ * can be used, calling with an empty list disables all but the system's own input methods.
+ *
+ * <p> System input methods are always available to the user this method can't modify this.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageNames List of input method package names.
+ * @return true if setting the restriction succeeded. It will fail if there is
+ * one or more input method enabled, that are not in the list or user if the foreground
+ * user.
+ */
+ public boolean setPermittedInputMethods(ComponentName admin, List<String> packageNames) {
+ if (mService != null) {
+ try {
+ return mService.setPermittedInputMethods(admin, packageNames);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns the list of permitted input methods set by this device or profile owner.
+ *
+ * <p>An empty list means no input methods except system input methods are allowed.
+ * Null means all input methods are allowed.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @return List of input method package names.
+ */
+ public List<String> getPermittedInputMethods(ComponentName admin) {
+ if (mService != null) {
+ try {
+ return mService.getPermittedInputMethods(admin);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the list of input methods permitted by the device or profiles
+ * owners of the current user.
+ *
+ * <p>Null means all input methods are allowed, if a non-null list is returned
+ * it will contain the intersection of the permitted lists for any device or profile
+ * owners that apply to this user. It will also include any system input methods.
+ *
+ * @return List of input method package names.
+ * @hide
+ */
+ @SystemApi
+ public List<String> getPermittedInputMethodsForCurrentUser() {
+ if (mService != null) {
+ try {
+ return mService.getPermittedInputMethodsForCurrentUser();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
* Called by a device owner to create a user with the specified name. The UserHandle returned
* by this method should not be persisted as user handles are recycled as users are removed and
* created. If you need to persist an identifier for this user, use
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 324b963..5f8a2a4 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -24,6 +24,7 @@ import android.net.ProxyInfo;
import android.os.Bundle;
import android.os.RemoteCallback;
import android.os.UserHandle;
+import java.util.List;
/**
* Internal IPC interface to the device policy service.
@@ -137,6 +138,14 @@ interface IDevicePolicyManager {
void addCrossProfileIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
void clearCrossProfileIntentFilters(in ComponentName admin);
+ boolean setPermittedAccessibilityServices(in ComponentName admin,in List packageList);
+ List getPermittedAccessibilityServices(in ComponentName admin);
+ List getPermittedAccessibilityServicesForUser(int userId);
+
+ boolean setPermittedInputMethods(in ComponentName admin,in List packageList);
+ List getPermittedInputMethods(in ComponentName admin);
+ List getPermittedInputMethodsForCurrentUser();
+
boolean setApplicationHidden(in ComponentName admin, in String packageName, boolean hidden);
int setApplicationsHidden(in ComponentName admin, in Intent intent, boolean hidden);
boolean isApplicationHidden(in ComponentName admin, in String packageName);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 02f8bd5..69ef2a0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.devicepolicy;
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
+import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
@@ -39,14 +40,15 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.media.AudioManager;
import android.media.IAudioService;
import android.net.ConnectivityManager;
import android.net.Uri;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.net.ProxyInfo;
@@ -77,6 +79,10 @@ import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
import android.view.IWindowManager;
import com.android.internal.R;
@@ -280,6 +286,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
private static final String TAG_ACCOUNT_TYPE = "account-type";
+ private static final String TAG_PERMITTED_ACCESSIBILITY_SERVICES
+ = "permitted-accessiblity-services";
private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
private static final String TAG_MANAGE_TRUST_AGENT_FEATURES = "manage-trust-agent-features";
private static final String TAG_TRUST_AGENT_FEATURE = "feature";
@@ -289,6 +297,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String TAG_GLOBAL_PROXY_EXCLUSION_LIST = "global-proxy-exclusion-list";
private static final String TAG_GLOBAL_PROXY_SPEC = "global-proxy-spec";
private static final String TAG_SPECIFIES_GLOBAL_PROXY = "specifies-global-proxy";
+ private static final String TAG_PERMITTED_IMES = "permitted-imes";
private static final String TAG_MAX_FAILED_PASSWORD_WIPE = "max-failed-password-wipe";
private static final String TAG_MAX_TIME_TO_UNLOCK = "max-time-to-unlock";
private static final String TAG_MIN_PASSWORD_NONLETTER = "min-password-nonletter";
@@ -305,6 +314,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
"cross-profile-widget-providers";
private static final String TAG_PROVIDER = "provider";
+ private static final String TAG_PACKAGE_LIST_ITEM = "item";
final DeviceAdminInfo info;
@@ -356,6 +366,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
+ // The list of permitted accessibility services package namesas set by a profile
+ // or device owner. Null means all accessibility services are allowed, empty means
+ // none except system services are allowed.
+ List<String> permittedAccessiblityServices;
+
+ // The list of permitted input methods package names as set by a profile or device owner.
+ // Null means all input methods are allowed, empty means none except system imes are
+ // allowed.
+ List<String> permittedInputMethods;
+
// TODO: review implementation decisions with frameworks team
boolean specifiesGlobalProxy = false;
String globalProxySpec = null;
@@ -519,6 +539,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
out.endTag(null, TAG_CROSS_PROFILE_WIDGET_PROVIDERS);
}
+ writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES,
+ permittedAccessiblityServices);
+ writePackageListToXml(out, TAG_PERMITTED_IMES, permittedInputMethods);
+ }
+
+ void writePackageListToXml(XmlSerializer out, String outerTag,
+ List<String> packageList)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ if (packageList == null) {
+ return;
+ }
+
+ out.startTag(null, outerTag);
+ for (String packageName : packageList) {
+ out.startTag(null, TAG_PACKAGE_LIST_ITEM);
+ out.attribute(null, ATTR_VALUE, packageName);
+ out.endTag(null, TAG_PACKAGE_LIST_ITEM);
+ }
+ out.endTag(null, outerTag);
}
void readFromXml(XmlPullParser parser)
@@ -602,6 +641,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
trustAgentFeatures = getAllTrustAgentFeatures(parser, tag);
} else if (TAG_CROSS_PROFILE_WIDGET_PROVIDERS.equals(tag)) {
crossProfileWidgetProviders = getCrossProfileWidgetProviders(parser, tag);
+ } else if (TAG_PERMITTED_ACCESSIBILITY_SERVICES.equals(tag)) {
+ permittedAccessiblityServices = readPackageList(parser, tag);
+ } else if (TAG_PERMITTED_IMES.equals(tag)) {
+ permittedInputMethods = readPackageList(parser, tag);
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
}
@@ -609,6 +652,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ private List<String> readPackageList(XmlPullParser parser,
+ String tag) throws XmlPullParserException, IOException {
+ List<String> result = new ArrayList<String>();
+ int outerDepth = parser.getDepth();
+ int outerType;
+ while ((outerType=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (outerType != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (outerType == XmlPullParser.END_TAG || outerType == XmlPullParser.TEXT) {
+ continue;
+ }
+ String outerTag = parser.getName();
+ if (TAG_PACKAGE_LIST_ITEM.equals(outerTag)) {
+ String packageName = parser.getAttributeValue(null, ATTR_VALUE);
+ if (packageName != null) {
+ result.add(packageName);
+ } else {
+ Slog.w(LOG_TAG, "Package name missing under " + outerTag);
+ }
+ } else {
+ Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + outerTag);
+ }
+ }
+ return result;
+ }
+
private Set<String> readDisableAccountInfo(XmlPullParser parser, String tag)
throws XmlPullParserException, IOException {
int outerDepthDAM = parser.getDepth();
@@ -752,6 +820,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
pw.println(disabledKeyguardFeatures);
pw.print(prefix); pw.print("crossProfileWidgetProviders=");
pw.println(crossProfileWidgetProviders);
+ if (!(permittedAccessiblityServices == null)) {
+ pw.print(prefix); pw.print("permittedAccessibilityServices=");
+ pw.println(permittedAccessiblityServices.toString());
+ }
+ if (!(permittedInputMethods == null)) {
+ pw.print(prefix); pw.print("permittedInputMethods=");
+ pw.println(permittedInputMethods.toString());
+ }
}
}
@@ -3941,6 +4017,343 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ /**
+ * @return true if all packages in enabledPackages are either in the list
+ * permittedList or are a system app.
+ */
+ private boolean checkPackagesInPermittedListOrSystem(List<String> enabledPackages,
+ List<String> permittedList) {
+ int userIdToCheck = UserHandle.getCallingUserId();
+ long id = Binder.clearCallingIdentity();
+ try {
+ // If we have an enabled packages list for a managed profile the packages
+ // we should check are installed for the parent user.
+ UserInfo user = mUserManager.getUserInfo(userIdToCheck);
+ if (user.isManagedProfile()) {
+ userIdToCheck = user.profileGroupId;
+ }
+
+ IPackageManager pm = AppGlobals.getPackageManager();
+ for (String enabledPackage : enabledPackages) {
+ boolean systemService = false;
+ try {
+ ApplicationInfo applicationInfo = pm.getApplicationInfo(enabledPackage,
+ PackageManager.GET_UNINSTALLED_PACKAGES, userIdToCheck);
+ systemService = (applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0;
+ } catch (RemoteException e) {
+ Log.i(LOG_TAG, "Can't talk to package managed", e);
+ }
+ if (!systemService && !permittedList.contains(enabledPackage)) {
+ return false;
+ }
+ }
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ return true;
+ }
+
+ private AccessibilityManager getAccessibilityManagerForUser(int userId) {
+ // Not using AccessibilityManager.getInstance because that guesses
+ // at the user you require based on callingUid and caches for a given
+ // process.
+ IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ IAccessibilityManager service = iBinder == null
+ ? null : IAccessibilityManager.Stub.asInterface(iBinder);
+ return new AccessibilityManager(mContext, service, userId);
+ }
+
+ @Override
+ public boolean setPermittedAccessibilityServices(ComponentName who, List packageList) {
+ if (!mHasFeature) {
+ return false;
+ }
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+
+ if (packageList != null) {
+ int userId = UserHandle.getCallingUserId();
+ List<AccessibilityServiceInfo> enabledServices = null;
+ long id = Binder.clearCallingIdentity();
+ try {
+ UserInfo user = mUserManager.getUserInfo(userId);
+ if (user.isManagedProfile()) {
+ userId = user.profileGroupId;
+ }
+ AccessibilityManager accessibilityManager = getAccessibilityManagerForUser(userId);
+ enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+ } finally {
+ restoreCallingIdentity(id);
+ }
+
+ if (enabledServices != null) {
+ List<String> enabledPackages = new ArrayList<String>();
+ for (AccessibilityServiceInfo service : enabledServices) {
+ enabledPackages.add(service.getResolveInfo().serviceInfo.packageName);
+ }
+ if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList)) {
+ Slog.e(LOG_TAG, "Cannot set permitted accessibility services, "
+ + "because it contains already enabled accesibility services.");
+ return false;
+ }
+ }
+ }
+
+ synchronized (this) {
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ admin.permittedAccessiblityServices = packageList;
+ saveSettingsLocked(UserHandle.getCallingUserId());
+ }
+ return true;
+ }
+
+ @Override
+ public List getPermittedAccessibilityServices(ComponentName who) {
+ if (!mHasFeature) {
+ return null;
+ }
+
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+
+ synchronized (this) {
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ return admin.permittedAccessiblityServices;
+ }
+ }
+
+ @Override
+ public List getPermittedAccessibilityServicesForUser(int userId) {
+ if (!mHasFeature) {
+ return null;
+ }
+ synchronized (this) {
+ List<String> result = null;
+ // If we have multiple profiles we return the intersection of the
+ // permitted lists. This can happen in cases where we have a device
+ // and profile owner.
+ List<UserInfo> profiles = mUserManager.getProfiles(userId);
+ final int PROFILES_SIZE = profiles.size();
+ for (int i = 0; i < PROFILES_SIZE; ++i) {
+ // Just loop though all admins, only device or profiles
+ // owners can have permitted lists set.
+ DevicePolicyData policy = getUserData(profiles.get(i).id);
+ final int N = policy.mAdminList.size();
+ for (int j = 0; j < N; j++) {
+ ActiveAdmin admin = policy.mAdminList.get(j);
+ List<String> fromAdmin = admin.permittedAccessiblityServices;
+ if (fromAdmin != null) {
+ if (result == null) {
+ result = new ArrayList<String>(fromAdmin);
+ } else {
+ result.retainAll(fromAdmin);
+ }
+ }
+ }
+ }
+
+ // If we have a permitted list add all system accessibility services.
+ if (result != null) {
+ long id = Binder.clearCallingIdentity();
+ try {
+ UserInfo user = mUserManager.getUserInfo(userId);
+ if (user.isManagedProfile()) {
+ userId = user.profileGroupId;
+ }
+ AccessibilityManager accessibilityManager =
+ getAccessibilityManagerForUser(userId);
+ List<AccessibilityServiceInfo> installedServices =
+ accessibilityManager.getInstalledAccessibilityServiceList();
+
+ IPackageManager pm = AppGlobals.getPackageManager();
+ if (installedServices != null) {
+ for (AccessibilityServiceInfo service : installedServices) {
+ String packageName = service.getResolveInfo().serviceInfo.packageName;
+ try {
+ ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES, userId);
+ if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ result.add(packageName);
+ }
+ } catch (RemoteException e) {
+ Log.i(LOG_TAG, "Accessibility service in missing package", e);
+ }
+ }
+ }
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+
+ return result;
+ }
+ }
+
+ private boolean checkCallerIsCurrentUserOrProfile() {
+ int callingUserId = UserHandle.getCallingUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ UserInfo currentUser;
+ UserInfo callingUser = mUserManager.getUserInfo(callingUserId);
+ try {
+ currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Failed to talk to activity managed.", e);
+ return false;
+ }
+
+ if (callingUser.isManagedProfile() && callingUser.profileGroupId != currentUser.id) {
+ Slog.e(LOG_TAG, "Cannot set permitted input methods for managed profile "
+ + "of a user that isn't the foreground user.");
+ return false;
+ }
+ if (!callingUser.isManagedProfile() && callingUserId != currentUser.id ) {
+ Slog.e(LOG_TAG, "Cannot set permitted input methods "
+ + "of a user that isn't the foreground user.");
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean setPermittedInputMethods(ComponentName who, List packageList) {
+ if (!mHasFeature) {
+ return false;
+ }
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+
+ // TODO When InputMethodManager supports per user calls remove
+ // this restriction.
+ if (!checkCallerIsCurrentUserOrProfile()) {
+ return false;
+ }
+
+ if (packageList != null) {
+ // InputMethodManager fetches input methods for current user.
+ // So this can only be set when calling user is the current user
+ // or parent is current user in case of managed profiles.
+ InputMethodManager inputMethodManager = (InputMethodManager) mContext
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ List<InputMethodInfo> enabledImes = inputMethodManager.getEnabledInputMethodList();
+
+ if (enabledImes != null) {
+ List<String> enabledPackages = new ArrayList<String>();
+ for (InputMethodInfo ime : enabledImes) {
+ enabledPackages.add(ime.getPackageName());
+ }
+ if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList)) {
+ Slog.e(LOG_TAG, "Cannot set permitted input methods, "
+ + "because it contains already enabled input method.");
+ return false;
+ }
+ }
+ }
+
+ synchronized (this) {
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ admin.permittedInputMethods = packageList;
+ saveSettingsLocked(UserHandle.getCallingUserId());
+ }
+ return true;
+ }
+
+ @Override
+ public List getPermittedInputMethods(ComponentName who) {
+ if (!mHasFeature) {
+ return null;
+ }
+
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+
+ synchronized (this) {
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ return admin.permittedInputMethods;
+ }
+ }
+
+ @Override
+ public List getPermittedInputMethodsForCurrentUser() {
+ UserInfo currentUser;
+ try {
+ currentUser = ActivityManagerNative.getDefault().getCurrentUser();
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Failed to make remote calls to get current user", e);
+ // Activity managed is dead, just allow all IMEs
+ return null;
+ }
+
+ int userId = currentUser.id;
+ synchronized (this) {
+ List<String> result = null;
+ // If we have multiple profiles we return the intersection of the
+ // permitted lists. This can happen in cases where we have a device
+ // and profile owner.
+ List<UserInfo> profiles = mUserManager.getProfiles(userId);
+ final int PROFILES_SIZE = profiles.size();
+ for (int i = 0; i < PROFILES_SIZE; ++i) {
+ // Just loop though all admins, only device or profiles
+ // owners can have permitted lists set.
+ DevicePolicyData policy = getUserData(profiles.get(i).id);
+ final int N = policy.mAdminList.size();
+ for (int j = 0; j < N; j++) {
+ ActiveAdmin admin = policy.mAdminList.get(j);
+ List<String> fromAdmin = admin.permittedInputMethods;
+ if (fromAdmin != null) {
+ if (result == null) {
+ result = new ArrayList<String>(fromAdmin);
+ } else {
+ result.retainAll(fromAdmin);
+ }
+ }
+ }
+ }
+
+ // If we have a permitted list add all system input methods.
+ if (result != null) {
+ InputMethodManager inputMethodManager = (InputMethodManager) mContext
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ List<InputMethodInfo> imes = inputMethodManager.getInputMethodList();
+ long id = Binder.clearCallingIdentity();
+ try {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ if (imes != null) {
+ for (InputMethodInfo ime : imes) {
+ String packageName = ime.getPackageName();
+ try {
+ ApplicationInfo applicationInfo = pm.getApplicationInfo(
+ packageName, PackageManager.GET_UNINSTALLED_PACKAGES,
+ userId);
+ if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ result.add(packageName);
+ }
+ } catch (RemoteException e) {
+ Log.i(LOG_TAG, "Input method for missing package", e);
+ }
+ }
+ }
+ } finally {
+ restoreCallingIdentity(id);
+ }
+ }
+ return result;
+ }
+ }
+
@Override
public UserHandle createUser(ComponentName who, String name) {
synchronized (this) {