Implement remove user for user hal
Bug: 150409600
Bug: 158195639
Test: atest CarServiceUnitTest:com.android.car.user.CarUserManagerUnitTest
Test: atest CarServiceUnitTest:com.android.car.hal.UserHalServiceTest
Test: atest CarServiceUnitTest:com.android.car.user.CarUserServiceTest
Test: atest CarServiceUnitTest:android.car.userlib.UserHalHelperTest
Test: atest CarSecurityPermissionTest:com.android.car.user.CarUserManagerPermissionTest
Change-Id: I6e5a67464fecf087994a19458def4ab7f538e1ce
diff --git a/service/src/com/android/car/CarShellCommand.java b/service/src/com/android/car/CarShellCommand.java
index ad7f31f..0a8c420 100644
--- a/service/src/com/android/car/CarShellCommand.java
+++ b/service/src/com/android/car/CarShellCommand.java
@@ -36,6 +36,7 @@
import android.car.user.CarUserManager;
import android.car.user.UserCreationResult;
import android.car.user.UserIdentificationAssociationResponse;
+import android.car.user.UserRemovalResult;
import android.car.user.UserSwitchResult;
import android.car.userlib.HalCallback;
import android.car.userlib.UserHalHelper;
@@ -46,6 +47,7 @@
import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType;
import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
@@ -125,6 +127,7 @@
private static final String COMMAND_INJECT_ROTARY = "inject-rotary";
private static final String COMMAND_GET_INITIAL_USER_INFO = "get-initial-user-info";
private static final String COMMAND_SWITCH_USER = "switch-user";
+ private static final String COMMAND_REMOVE_USER = "remove-user";
private static final String COMMAND_CREATE_USER = "create-user";
private static final String COMMAND_GET_INITIAL_USER = "get-initial-user";
private static final String COMMAND_SET_USER_ID_TO_OCCUPANT_ZONE =
@@ -155,6 +158,8 @@
android.Manifest.permission.MANAGE_USERS);
USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_SWITCH_USER,
android.Manifest.permission.MANAGE_USERS);
+ USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_REMOVE_USER,
+ android.Manifest.permission.MANAGE_USERS);
USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_CREATE_USER,
android.Manifest.permission.MANAGE_USERS);
USER_BUILD_COMMAND_TO_PERMISSION_MAP.put(COMMAND_GET_USER_AUTH_ASSOCIATION,
@@ -374,6 +379,10 @@
pw.println("\t The --hal-only option only calls HAL, without switching the user,");
pw.println("\t while the --timeout defines how long to wait for the HAL response.");
+ pw.printf("\t%s <USER_ID> [--hal-only]\n", COMMAND_REMOVE_USER);
+ pw.println("\t Removes user with USER_ID using the HAL integration.");
+ pw.println("\t The --hal-only option only calls HAL, without removing the user,");
+
pw.printf("\t%s [--hal-only] [--timeout TIMEOUT_MS] [--type TYPE] [--flags FLAGS] [NAME]\n",
COMMAND_CREATE_USER);
pw.println("\t Creates a new user using the HAL integration.");
@@ -609,6 +618,9 @@
case COMMAND_SWITCH_USER:
switchUser(args, writer);
break;
+ case COMMAND_REMOVE_USER:
+ removeUser(args, writer);
+ break;
case COMMAND_CREATE_USER:
createUser(args, writer);
break;
@@ -1111,6 +1123,52 @@
}
}
+ private void removeUser(String[] args, PrintWriter writer) {
+ if (args.length < 2) {
+ writer.println("Insufficient number of args");
+ return;
+ }
+
+ int userId = Integer.parseInt(args[1]);
+ boolean halOnly = false;
+
+ for (int i = 2; i < args.length; i++) {
+ String arg = args[i];
+ switch (arg) {
+ case "--hal-only":
+ halOnly = true;
+ break;
+ default:
+ writer.println("Invalid option at index " + i + ": " + arg);
+ return;
+ }
+ }
+
+ Log.d(TAG, "handleRemoveUser(): User to remove=" + userId + ", halOnly=" + halOnly);
+
+ if (halOnly) {
+ UserHalService userHal = mHal.getUserHal();
+ UsersInfo usersInfo = generateUsersInfo();
+ UserInfo userInfo = new UserInfo();
+ userInfo.userId = userId;
+ userInfo.flags = getUserHalFlags(userId);
+
+ RemoveUserRequest request = new RemoveUserRequest();
+ request.removedUserInfo = userInfo;
+ request.usersInfo = usersInfo;
+
+ userHal.removeUser(request);
+ writer.printf("User removal sent for HAL only.\n");
+ return;
+ }
+
+ CarUserManager carUserManager = getCarUserManager(mContext);
+ UserRemovalResult result = carUserManager.removeUser(userId);
+ if (result == null) return;
+ writer.printf("UserRemovalResult: status = %s\n",
+ UserRemovalResult.statusToString(result.getStatus()));
+ }
+
private static <T> T waitForFuture(@NonNull PrintWriter writer,
@NonNull AndroidFuture<T> future, int timeoutMs) {
T result = null;
diff --git a/service/src/com/android/car/hal/UserHalService.java b/service/src/com/android/car/hal/UserHalService.java
index e76df37..645f563 100644
--- a/service/src/com/android/car/hal/UserHalService.java
+++ b/service/src/com/android/car/hal/UserHalService.java
@@ -17,6 +17,7 @@
import static android.car.VehiclePropertyIds.CREATE_USER;
import static android.car.VehiclePropertyIds.INITIAL_USER_INFO;
+import static android.car.VehiclePropertyIds.REMOVE_USER;
import static android.car.VehiclePropertyIds.SWITCH_USER;
import static android.car.VehiclePropertyIds.USER_IDENTIFICATION_ASSOCIATION;
@@ -34,6 +35,7 @@
import android.hardware.automotive.vehicle.V2_0.CreateUserResponse;
import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType;
import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
import android.hardware.automotive.vehicle.V2_0.SwitchUserResponse;
@@ -166,6 +168,9 @@
mHandler.sendMessage(obtainMessage(
UserHalService::handleOnCreateUserResponse, this, value));
break;
+ case REMOVE_USER:
+ Log.w(TAG, "Received REMOVE_USER HAL event: " + value);
+ break;
case USER_IDENTIFICATION_ASSOCIATION:
mHandler.sendMessage(obtainMessage(
UserHalService::handleOnUserIdentificationAssociation, this, value));
@@ -302,6 +307,34 @@
}
/**
+ * Calls HAL to remove user.
+ *
+ * @throws IllegalStateException if the HAL does not support user management (callers should
+ * call {@link #isSupported()} first to avoid this exception).
+ */
+ public void removeUser(@NonNull RemoveUserRequest request) {
+ Objects.requireNonNull(request, "request cannot be null");
+
+ if (DBG) Log.d(TAG, "removeUser(" + request.removedUserInfo.userId + ")");
+ EventLog.writeEvent(EventLogTags.CAR_USER_HAL_REMOVE_USER_REQ,
+ request.removedUserInfo.userId, request.usersInfo.currentUser.userId);
+
+ VehiclePropValue propRequest;
+ synchronized (mLock) {
+ checkSupportedLocked();
+ request.requestId = getNextRequestId();
+ propRequest = UserHalHelper.toVehiclePropValue(request);
+
+ }
+ try {
+ if (DBG) Log.d(TAG, "Calling hal.set(): " + propRequest);
+ mHal.set(propRequest);
+ } catch (ServiceSpecificException e) {
+ Log.w(TAG, "Failed to set REMOVE USER", e);
+ }
+ }
+
+ /**
* Calls HAL to indicate an Android user was created.
*
* @param request info agout the created user.
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index 81d1b6c..bd45abb 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -35,6 +35,7 @@
import android.car.user.CarUserManager.UserLifecycleListener;
import android.car.user.UserCreationResult;
import android.car.user.UserIdentificationAssociationResponse;
+import android.car.user.UserRemovalResult;
import android.car.user.UserSwitchResult;
import android.car.userlib.CarUserManagerHelper;
import android.car.userlib.CommonConstants.CarUserServiceConstants;
@@ -53,6 +54,7 @@
import android.hardware.automotive.vehicle.V2_0.CreateUserStatus;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
+import android.hardware.automotive.vehicle.V2_0.RemoveUserRequest;
import android.hardware.automotive.vehicle.V2_0.SwitchUserRequest;
import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
import android.hardware.automotive.vehicle.V2_0.UserIdentificationGetRequest;
@@ -879,6 +881,62 @@
});
}
+ @Override
+ public UserRemovalResult removeUser(@UserIdInt int userId) {
+ checkManageUsersPermission("removeUser");
+ EventLog.writeEvent(EventLogTags.CAR_USER_SVC_REMOVE_USER_REQ, userId);
+ // If the requested user is the current user, return error.
+ if (ActivityManager.getCurrentUser() == userId) {
+ return logAndGetResults(userId,
+ UserRemovalResult.STATUS_TARGET_USER_IS_CURRENT_USER);
+ }
+
+ // If requested user is the only admin user, return error.
+ UserInfo userInfo = mUserManager.getUserInfo(userId);
+ if (userInfo == null) {
+ return logAndGetResults(userId, UserRemovalResult.STATUS_USER_DOES_NOT_EXIST);
+ }
+
+ android.hardware.automotive.vehicle.V2_0.UserInfo halUser =
+ new android.hardware.automotive.vehicle.V2_0.UserInfo();
+ halUser.userId = userInfo.id;
+ halUser.flags = UserHalHelper.convertFlags(userInfo);
+ UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
+
+ // Do not delete last admin user.
+ if (UserHalHelper.isAdmin(halUser.flags)) {
+ int size = usersInfo.existingUsers.size();
+ int totalAdminUsers = 0;
+ for (int i = 0; i < size; i++) {
+ if (UserHalHelper.isAdmin(usersInfo.existingUsers.get(i).flags)) {
+ totalAdminUsers++;
+ }
+ }
+ if (totalAdminUsers == 1) {
+ return logAndGetResults(userId,
+ UserRemovalResult.STATUS_TARGET_USER_IS_LAST_ADMIN_USER);
+ }
+ }
+
+ // First remove user from android and then remove from HAL because HAL remove user is one
+ // way call.
+ if (!mUserManager.removeUser(userId)) {
+ return logAndGetResults(userId, UserRemovalResult.STATUS_ANDROID_FAILURE);
+ }
+
+ RemoveUserRequest request = new RemoveUserRequest();
+ request.removedUserInfo = halUser;
+ request.usersInfo = usersInfo;
+ mHal.removeUser(request);
+ return logAndGetResults(userId, UserRemovalResult.STATUS_SUCCESSFUL);
+ }
+
+ private UserRemovalResult logAndGetResults(@UserIdInt int userId,
+ @UserRemovalResult.Status int result) {
+ EventLog.writeEvent(EventLogTags.CAR_USER_SVC_REMOVE_USER_RESP, userId, result);
+ return new UserRemovalResult(result);
+ }
+
private void sendUserSwitchUiCallback(@UserIdInt int targetUserId) {
if (mUserSwitchUiReceiver == null) {
Log.w(TAG_USER, "No User switch UI receiver.");