diff options
author | Kenny Guy <kennyguy@google.com> | 2014-08-20 19:40:59 +0100 |
---|---|---|
committer | Kenny Guy <kennyguy@google.com> | 2014-08-20 22:30:05 +0100 |
commit | fa80a4faa3ab32f61742b684e832126dae8468e7 (patch) | |
tree | 399edfb680ca4f5dbf3aa392956f0ddad4faab19 /services | |
parent | 833dcd4bea5cadccaa55f9a06942bb36d01d73ac (diff) | |
download | frameworks_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
Diffstat (limited to 'services')
-rw-r--r-- | services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java | 415 |
1 files changed, 414 insertions, 1 deletions
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) { |