summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt3
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java62
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java85
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,