OSDN Git Service

Add apis for whitelisting IMEs and accessibility services.
authorKenny Guy <kennyguy@google.com>
Wed, 20 Aug 2014 18:40:59 +0000 (19:40 +0100)
committerKenny Guy <kennyguy@google.com>
Wed, 20 Aug 2014 21:30:05 +0000 (22:30 +0100)
Adds apis for device / profile owners to white list
the accessibility and input methods a user can enable.

Bug: 14469005
Change-Id: I921888660d29a5370395db87adf75d4d106660c9

api/current.txt
core/java/android/app/admin/DevicePolicyManager.java
core/java/android/app/admin/IDevicePolicyManager.aidl
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java

index 0d8aeba..d2abbb7 100644 (file)
@@ -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);
index 053afb7..82e7e3d 100644 (file)
@@ -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
index 324b963..5f8a2a4 100644 (file)
@@ -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);
index 02f8bd5..69ef2a0 100644 (file)
@@ -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) {