diff options
4 files changed, 152 insertions, 1 deletions
diff --git a/api/current.txt b/api/current.txt index 58c260447a37..3eb8667b770f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6371,6 +6371,7 @@ package android.app.admin { method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName); method public long getRequiredStrongAuthTimeout(android.content.ComponentName); method public boolean getScreenCaptureDisabled(android.content.ComponentName); + method public java.util.List<android.os.UserHandle> getSecondaryUsers(android.content.ComponentName); method public java.lang.CharSequence getShortSupportMessage(android.content.ComponentName); method public boolean getStorageEncryption(android.content.ComponentName); method public int getStorageEncryptionStatus(); @@ -6403,6 +6404,7 @@ package android.app.admin { method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String); method public void lockNow(); method public void lockNow(int); + method public boolean logoutUser(android.content.ComponentName); method public void reboot(android.content.ComponentName); method public void removeActiveAdmin(android.content.ComponentName); method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String); @@ -6477,6 +6479,7 @@ package android.app.admin { method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle); method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean); method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap); + method public boolean stopUser(android.content.ComponentName, android.os.UserHandle); method public boolean switchUser(android.content.ComponentName, android.os.UserHandle); method public void uninstallAllUserCaCerts(android.content.ComponentName); method public void uninstallCaCert(android.content.ComponentName, byte[]); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index acdad1c44c90..db1f3af592fc 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -6219,7 +6219,7 @@ public class DevicePolicyManager { * @return {@code true} if the user was removed, {@code false} otherwise. * @throws SecurityException if {@code admin} is not a device owner. */ - public boolean removeUser(@NonNull ComponentName admin, UserHandle userHandle) { + public boolean removeUser(@NonNull ComponentName admin, @NonNull UserHandle userHandle) { throwIfParentInstance("removeUser"); try { return mService.removeUser(admin, userHandle); @@ -6230,6 +6230,7 @@ public class DevicePolicyManager { /** * Called by a device owner to switch the specified user to the foreground. + * <p> This cannot be used to switch to a managed profile. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param userHandle the user to switch to; null will switch to primary. @@ -6247,6 +6248,65 @@ public class DevicePolicyManager { } /** + * Called by a device owner to stop the specified secondary user. + * <p> This cannot be used to stop the primary user or a managed profile. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param userHandle the user to be stopped. + * @return {@code true} if the user can be stopped, {@code false} otherwise. + * @throws SecurityException if {@code admin} is not a device owner. + */ + public boolean stopUser(@NonNull ComponentName admin, @NonNull UserHandle userHandle) { + throwIfParentInstance("stopUser"); + try { + return mService.stopUser(admin, userHandle); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Called by a profile owner that is affiliated with the device owner to stop the calling user + * and switch back to primary. + * <p> This has no effect when called on a managed profile. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @return {@code true} if the exit was successful, {@code false} otherwise. + * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device + * owner. + */ + public boolean logoutUser(@NonNull ComponentName admin) { + throwIfParentInstance("logoutUser"); + try { + return mService.logoutUser(admin); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** + * Called by a device owner to list all secondary users on the device, excluding managed + * profiles. + * <p> Used for various user management APIs, including {@link #switchUser}, {@link #removeUser} + * and {@link #stopUser}. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @return list of other {@link UserHandle}s on the device. + * @throws SecurityException if {@code admin} is not a device owner. + * @see #switchUser + * @see #removeUser + * @see #stopUser + */ + public List<UserHandle> getSecondaryUsers(@NonNull ComponentName admin) { + throwIfParentInstance("getSecondaryUsers"); + try { + return mService.getSecondaryUsers(admin); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Retrieves the application restrictions for a given target application running in the calling * user. * <p> diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 4925f341fd94..1f17dbc20c7c 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -215,6 +215,9 @@ interface IDevicePolicyManager { UserHandle createAndManageUser(in ComponentName who, in String name, in ComponentName profileOwner, in PersistableBundle adminExtras, in int flags); boolean removeUser(in ComponentName who, in UserHandle userHandle); boolean switchUser(in ComponentName who, in UserHandle userHandle); + boolean stopUser(in ComponentName who, in UserHandle userHandle); + boolean logoutUser(in ComponentName who); + List<UserHandle> getSecondaryUsers(in ComponentName who); void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName); int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ddc0c2339050..14b9dfb932f3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -19,6 +19,7 @@ package com.android.server.devicepolicy; import static android.Manifest.permission.BIND_DEVICE_ADMIN; import static android.Manifest.permission.MANAGE_CA_CERTIFICATES; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; +import static android.app.ActivityManager.USER_OP_SUCCESS; import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY; import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED; import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE; @@ -8403,6 +8404,90 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public boolean stopUser(ComponentName who, UserHandle userHandle) { + Preconditions.checkNotNull(who, "ComponentName is null"); + + synchronized (this) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + } + + final int userId = userHandle.getIdentifier(); + if (isManagedProfile(userId)) { + Log.w(LOG_TAG, "Managed profile cannot be stopped"); + return false; + } + + final long id = mInjector.binderClearCallingIdentity(); + try { + return mInjector.getIActivityManager().stopUser(userId, true /*force*/, null) + == USER_OP_SUCCESS; + } catch (RemoteException e) { + // Same process, should not happen. + return false; + } finally { + mInjector.binderRestoreCallingIdentity(id); + } + } + + @Override + public boolean logoutUser(ComponentName who) { + Preconditions.checkNotNull(who, "ComponentName is null"); + + final int callingUserId = mInjector.userHandleGetCallingUserId(); + synchronized (this) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + if (!isUserAffiliatedWithDeviceLocked(callingUserId)) { + throw new SecurityException("Admin " + who + + " is neither the device owner or affiliated user's profile owner."); + } + } + + if (isManagedProfile(callingUserId)) { + Log.w(LOG_TAG, "Managed profile cannot be logout"); + return false; + } + + final long id = mInjector.binderClearCallingIdentity(); + try { + if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) { + Log.w(LOG_TAG, "Failed to switch to primary user"); + return false; + } + return mInjector.getIActivityManager().stopUser(callingUserId, true /*force*/, null) + == USER_OP_SUCCESS; + } catch (RemoteException e) { + // Same process, should not happen. + return false; + } finally { + mInjector.binderRestoreCallingIdentity(id); + } + } + + @Override + public List<UserHandle> getSecondaryUsers(ComponentName who) { + Preconditions.checkNotNull(who, "ComponentName is null"); + synchronized (this) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); + } + + final long id = mInjector.binderClearCallingIdentity(); + try { + final List<UserInfo> userInfos = mInjector.getUserManager().getUsers(true + /*excludeDying*/); + final List<UserHandle> userHandles = new ArrayList<>(); + for (UserInfo userInfo : userInfos) { + UserHandle userHandle = userInfo.getUserHandle(); + if (!userHandle.isSystem() && !isManagedProfile(userHandle.getIdentifier())) { + userHandles.add(userInfo.getUserHandle()); + } + } + return userHandles; + } finally { + mInjector.binderRestoreCallingIdentity(id); + } + } + + @Override public Bundle getApplicationRestrictions(ComponentName who, String callerPackage, String packageName) { enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, |