Merge "Modifies HalCamera::clientStreamEnding() method" into rvc-dev
diff --git a/car-lib/src/android/car/ICarUserService.aidl b/car-lib/src/android/car/ICarUserService.aidl
index b3823b3..5b58b54 100644
--- a/car-lib/src/android/car/ICarUserService.aidl
+++ b/car-lib/src/android/car/ICarUserService.aidl
@@ -44,4 +44,5 @@
UserIdentificationAssociationResponse getUserIdentificationAssociation(in int[] types);
void setUserIdentificationAssociation(int timeoutMs, in int[] types, in int[] values,
in AndroidFuture<UserIdentificationAssociationResponse> result);
+ boolean isUserHalUserAssociationSupported();
}
diff --git a/car-lib/src/android/car/user/CarUserManager.java b/car-lib/src/android/car/user/CarUserManager.java
index a4daa42..c3b00f3 100644
--- a/car-lib/src/android/car/user/CarUserManager.java
+++ b/car-lib/src/android/car/user/CarUserManager.java
@@ -377,6 +377,19 @@
}
/**
+ * Check if user hal supports user association.
+ *
+ * @hide
+ */
+ public boolean isUserHalUserAssociationSupported() {
+ try {
+ return mService.isUserHalUserAssociationSupported();
+ } catch (RemoteException e) {
+ return handleRemoteExceptionFromCarService(e, false);
+ }
+ }
+
+ /**
* Gets the user authentication types associated with this manager's user.
*
* @hide
diff --git a/evs/apps/default/RenderPixelCopy.cpp b/evs/apps/default/RenderPixelCopy.cpp
index 186269f..dde2d2f 100644
--- a/evs/apps/default/RenderPixelCopy.cpp
+++ b/evs/apps/default/RenderPixelCopy.cpp
@@ -90,7 +90,7 @@
if (mStreamHandler->newFrameAvailable()) {
const BufferDesc& srcBuffer = mStreamHandler->getNewFrame();
const AHardwareBuffer_Desc* pSrcDesc =
- reinterpret_cast<const AHardwareBuffer_Desc *>(&tgtBuffer.buffer.description);
+ reinterpret_cast<const AHardwareBuffer_Desc *>(&srcBuffer.buffer.description);
// Lock our source buffer for reading (current expectation are for this to be NV21 format)
sp<android::GraphicBuffer> src = new android::GraphicBuffer(srcBuffer.buffer.nativeHandle,
@@ -101,35 +101,37 @@
pSrcDesc->layers,
pSrcDesc->usage,
pSrcDesc->stride);
+
unsigned char* srcPixels = nullptr;
src->lock(GRALLOC_USAGE_SW_READ_OFTEN, (void**)&srcPixels);
- if (!srcPixels) {
+ if (srcPixels != nullptr) {
+ // Make sure we don't run off the end of either buffer
+ const unsigned width = std::min(pTgtDesc->width,
+ pSrcDesc->width);
+ const unsigned height = std::min(pTgtDesc->height,
+ pSrcDesc->height);
+
+ if (pSrcDesc->format == HAL_PIXEL_FORMAT_YCRCB_420_SP) { // 420SP == NV21
+ copyNV21toRGB32(width, height,
+ srcPixels,
+ tgtPixels, pTgtDesc->stride);
+ } else if (pSrcDesc->format == HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12
+ copyYV12toRGB32(width, height,
+ srcPixels,
+ tgtPixels, pTgtDesc->stride);
+ } else if (pSrcDesc->format == HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV
+ copyYUYVtoRGB32(width, height,
+ srcPixels, pSrcDesc->stride,
+ tgtPixels, pTgtDesc->stride);
+ } else if (pSrcDesc->format == pTgtDesc->format) { // 32bit RGBA
+ copyMatchedInterleavedFormats(width, height,
+ srcPixels, pSrcDesc->stride,
+ tgtPixels, pTgtDesc->stride,
+ tgtBuffer.pixelSize);
+ }
+ } else {
LOG(ERROR) << "Failed to get pointer into src image data";
- }
-
- // Make sure we don't run off the end of either buffer
- const unsigned width = std::min(pTgtDesc->width,
- pSrcDesc->width);
- const unsigned height = std::min(pTgtDesc->height,
- pSrcDesc->height);
-
- if (pSrcDesc->format == HAL_PIXEL_FORMAT_YCRCB_420_SP) { // 420SP == NV21
- copyNV21toRGB32(width, height,
- srcPixels,
- tgtPixels, pTgtDesc->stride);
- } else if (pSrcDesc->format == HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12
- copyYV12toRGB32(width, height,
- srcPixels,
- tgtPixels, pTgtDesc->stride);
- } else if (pSrcDesc->format == HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV
- copyYUYVtoRGB32(width, height,
- srcPixels, pSrcDesc->stride,
- tgtPixels, pTgtDesc->stride);
- } else if (pSrcDesc->format == pTgtDesc->format) { // 32bit RGBA
- copyMatchedInterleavedFormats(width, height,
- srcPixels, pSrcDesc->stride,
- tgtPixels, pTgtDesc->stride,
- tgtBuffer.pixelSize);
+ success = false;
}
mStreamHandler->doneWithFrame(srcBuffer);
diff --git a/service/src/com/android/car/CarPowerManagementService.java b/service/src/com/android/car/CarPowerManagementService.java
index aef68cd..da88468 100644
--- a/service/src/com/android/car/CarPowerManagementService.java
+++ b/service/src/com/android/car/CarPowerManagementService.java
@@ -512,6 +512,9 @@
case HalCallback.STATUS_WRONG_HAL_RESPONSE:
switchUserOnResumeUserHalFallback("wrong response");
return;
+ case HalCallback.STATUS_HAL_NOT_SUPPORTED:
+ switchUserOnResumeUserHalFallback("Hal not supported");
+ return;
case HalCallback.STATUS_OK:
if (response == null) {
switchUserOnResumeUserHalFallback("no response");
diff --git a/service/src/com/android/car/hal/UserHalService.java b/service/src/com/android/car/hal/UserHalService.java
index 645f563..2c88aed 100644
--- a/service/src/com/android/car/hal/UserHalService.java
+++ b/service/src/com/android/car/hal/UserHalService.java
@@ -81,14 +81,24 @@
private static final String TAG = UserHalService.class.getSimpleName();
private static final String UNSUPPORTED_MSG = "Vehicle HAL does not support user management";
+ private static final String USER_ASSOCIATION_UNSUPPORTED_MSG =
+ "Vehicle HAL does not support user association";
private static final int[] SUPPORTED_PROPERTIES = new int[]{
- INITIAL_USER_INFO,
- SWITCH_USER,
CREATE_USER,
+ INITIAL_USER_INFO,
+ REMOVE_USER,
+ SWITCH_USER,
USER_IDENTIFICATION_ASSOCIATION
};
+ private static final int[] CORE_PROPERTIES = new int[]{
+ CREATE_USER,
+ INITIAL_USER_INFO,
+ REMOVE_USER,
+ SWITCH_USER,
+ };
+
private static final boolean DBG = false;
private final Object mLock = new Object();
@@ -208,11 +218,29 @@
}
/**
- * Checks if the Vehicle HAL supports user management.
+ * Checks if the Vehicle HAL supports core user management actions.
*/
public boolean isSupported() {
synchronized (mLock) {
- return mProperties != null;
+ if (mProperties == null) return false;
+
+ for (int i = 0; i < CORE_PROPERTIES.length; i++) {
+ if (mProperties.get(CORE_PROPERTIES[i]) == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Checks if the Vehicle HAL supports core user management actions.
+ */
+ public boolean isUserAssociationSupported() {
+ synchronized (mLock) {
+ if (mProperties == null) return false;
+ if (mProperties.get(USER_IDENTIFICATION_ASSOCIATION) == null) return false;
+ return true;
}
}
@@ -221,6 +249,12 @@
Preconditions.checkState(isSupported(), UNSUPPORTED_MSG);
}
+ @GuardedBy("mLock")
+ private void checkUserAssociationSupportedLocked() {
+ Preconditions.checkState(isUserAssociationSupported(), USER_ASSOCIATION_UNSUPPORTED_MSG);
+ }
+
+
/**
* Calls HAL to asynchronously get info about the initial user.
*
@@ -372,7 +406,7 @@
public void postSwitchResponse(@NonNull SwitchUserRequest request) {
EventLog.writeEvent(EventLogTags.CAR_USER_HAL_POST_SWITCH_USER_REQ, request.requestId,
request.targetUser.userId, request.usersInfo.currentUser.userId);
- if (DBG) Log.d(TAG, "postSwitchResponse(" + request.targetUser.userId + ")");
+ if (DBG) Log.d(TAG, "postSwitchResponse(" + request + ")");
VehiclePropValue propRequest;
synchronized (mLock) {
@@ -438,6 +472,9 @@
public UserIdentificationResponse getUserAssociation(
@NonNull UserIdentificationGetRequest request) {
Objects.requireNonNull(request, "request cannot be null");
+ synchronized (mLock) {
+ checkUserAssociationSupportedLocked();
+ }
// Check that it doesn't have dupes
SparseBooleanArray types = new SparseBooleanArray(request.numberAssociationTypes);
@@ -509,10 +546,10 @@
*/
public void setUserAssociation(int timeoutMs, @NonNull UserIdentificationSetRequest request,
@NonNull HalCallback<UserIdentificationResponse> callback) {
- if (DBG) Log.d(TAG, "setUserAssociation(" + request + ")");
Preconditions.checkArgumentPositive(timeoutMs, "timeout must be positive");
Objects.requireNonNull(request, "request cannot be null");
Objects.requireNonNull(callback, "callback cannot be null");
+ if (DBG) Log.d(TAG, "setUserAssociation(" + request + ")");
// Check that it doesn't have dupes
SparseBooleanArray types = new SparseBooleanArray(request.numberAssociations);
@@ -526,7 +563,7 @@
VehiclePropValue propRequest;
int requestId;
synchronized (mLock) {
- checkSupportedLocked();
+ checkUserAssociationSupportedLocked();
if (hasPendingRequestLocked(UserIdentificationResponse.class, callback)) return;
requestId = request.requestId = getNextRequestId();
propRequest = UserHalHelper.toVehiclePropValue(request);
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index 8e08b28..b9c54a0 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -134,6 +134,8 @@
public static final String BUNDLE_INITIAL_INFO_ACTION =
CarUserServiceConstants.BUNDLE_INITIAL_INFO_ACTION;
+ public static final String VEHICLE_HAL_NOT_SUPPORTED = "Vehicle Hal not supported.";
+
private final Context mContext;
private final CarUserManagerHelper mCarUserManagerHelper;
private final IActivityManager mAm;
@@ -627,6 +629,10 @@
timeoutMs);
Objects.requireNonNull(receiver, "receiver cannot be null");
checkManageUsersPermission("getInitialInfo");
+ if (!isUserHalSupported()) {
+ sendResult(receiver, HalCallback.STATUS_HAL_NOT_SUPPORTED, null);
+ return;
+ }
UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
mHal.getInitialUserInfo(requestType, timeoutMs, usersInfo, (status, resp) -> {
Bundle resultData = null;
@@ -729,6 +735,10 @@
mHalTimeoutMs);
Objects.requireNonNull(callback, "callback cannot be null");
checkManageUsersPermission("getInitialUserInfo");
+ if (!isUserHalSupported()) {
+ callback.onResponse(HalCallback.STATUS_HAL_NOT_SUPPORTED, null);
+ return;
+ }
UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
mHal.getInitialUserInfo(requestType, mHalTimeoutMs, usersInfo, callback);
}
@@ -790,6 +800,22 @@
return;
}
+ // If User Hal is not supported, just android user switch.
+ if (!isUserHalSupported()) {
+ try {
+ if (mAm.switchUser(targetUserId)) {
+ sendUserSwitchResult(receiver, UserSwitchResult.STATUS_SUCCESSFUL);
+ return;
+ }
+ } catch (RemoteException e) {
+ // ignore
+ Log.w(TAG_USER,
+ "error while switching user " + targetUser.toFullString(), e);
+ }
+ sendUserSwitchResult(receiver, UserSwitchResult.STATUS_ANDROID_FAILURE);
+ return;
+ }
+
synchronized (mLockUser) {
if (Log.isLoggable(TAG_USER, Log.DEBUG)) {
Log.d(TAG_USER, "switchUser(" + targetUserId + "): currentuser=" + currentUser
@@ -937,10 +963,13 @@
return logAndGetResults(userId, UserRemovalResult.STATUS_ANDROID_FAILURE);
}
- RemoveUserRequest request = new RemoveUserRequest();
- request.removedUserInfo = halUser;
- request.usersInfo = usersInfo;
- mHal.removeUser(request);
+ if (isUserHalSupported()) {
+ RemoveUserRequest request = new RemoveUserRequest();
+ request.removedUserInfo = halUser;
+ request.usersInfo = usersInfo;
+ mHal.removeUser(request);
+ }
+
return logAndGetResults(userId, UserRemovalResult.STATUS_SUCCESSFUL);
}
@@ -994,6 +1023,11 @@
return;
}
+ if (!isUserHalSupported()) {
+ sendUserCreationResult(receiver, UserCreationResult.STATUS_SUCCESSFUL, newUser, null);
+ return;
+ }
+
CreateUserRequest request = new CreateUserRequest();
request.usersInfo = UserHalHelper.newUsersInfo(mUserManager);
if (!TextUtils.isEmpty(name)) {
@@ -1066,6 +1100,10 @@
@Override
public UserIdentificationAssociationResponse getUserIdentificationAssociation(int[] types) {
+ if (!isUserHalUserAssociationSupported()) {
+ return UserIdentificationAssociationResponse.forFailure(VEHICLE_HAL_NOT_SUPPORTED);
+ }
+
Preconditions.checkArgument(!ArrayUtils.isEmpty(types), "must have at least one type");
checkManageUsersPermission("getUserIdentificationAssociation");
@@ -1101,6 +1139,12 @@
@Override
public void setUserIdentificationAssociation(int timeoutMs, int[] types, int[] values,
AndroidFuture<UserIdentificationAssociationResponse> result) {
+ if (!isUserHalUserAssociationSupported()) {
+ result.complete(
+ UserIdentificationAssociationResponse.forFailure(VEHICLE_HAL_NOT_SUPPORTED));
+ return;
+ }
+
Preconditions.checkArgument(!ArrayUtils.isEmpty(types), "must have at least one type");
Preconditions.checkArgument(!ArrayUtils.isEmpty(values), "must have at least one value");
if (types.length != values.length) {
@@ -1246,7 +1290,10 @@
mRequestIdForUserSwitchInProcess = requestId;
}
}
+
private void postSwitchHalResponse(int requestId, @UserIdInt int targetUserId) {
+ if (!isUserHalSupported()) return;
+
UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
EventLog.writeEvent(EventLogTags.CAR_USER_SVC_POST_SWITCH_USER_REQ, requestId,
targetUserId, usersInfo.currentUser.userId);
@@ -1276,6 +1323,14 @@
}
/**
+ * Checks if the User HAL user association is supported.
+ */
+ @Override
+ public boolean isUserHalUserAssociationSupported() {
+ return mHal.isUserAssociationSupported();
+ }
+
+ /**
* Sets a callback which is invoked before user switch.
*
* <p>
@@ -1655,6 +1710,8 @@
}
}
+ if (!isUserHalSupported()) return;
+
// switch HAL user
UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager, fromUserId);
SwitchUserRequest request = createUserSwitchRequest(toUserId, usersInfo);
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/homepage/contextualcards/deviceinfo/EmergencyInfoSlice.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/homepage/contextualcards/deviceinfo/EmergencyInfoSlice.java
index d7ef7cd..5454a68 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/homepage/contextualcards/deviceinfo/EmergencyInfoSlice.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/homepage/contextualcards/deviceinfo/EmergencyInfoSlice.java
@@ -27,6 +27,7 @@
import androidx.slice.builders.SliceAction;
import com.android.car.developeroptions.R;
+import com.android.car.developeroptions.Utils;
import com.android.car.developeroptions.accounts.EmergencyInfoPreferenceController;
import com.android.car.developeroptions.slices.CustomSliceRegistry;
import com.android.car.developeroptions.slices.CustomSliceable;
@@ -61,7 +62,8 @@
@Override
public Intent getIntent() {
- return new Intent(EmergencyInfoPreferenceController.getIntentAction(mContext));
+ return new Intent(EmergencyInfoPreferenceController.getIntentAction(mContext))
+ .setPackage(Utils.SETTINGS_PACKAGE_NAME);
}
@Override
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java b/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java
index e6a81b6..a75239a 100644
--- a/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hal/UserHalServiceTest.java
@@ -146,8 +146,12 @@
@Before
public void setFixtures() {
mUserHalService = spy(new UserHalService(mVehicleHal, mHandler));
- // Needs at least one property, otherwise isSupported() will return false
- mUserHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO)));
+ // Needs at least one property, otherwise isSupported() and isUserAssociationSupported()
+ // will return false
+ mUserHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
+ newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
+ newSubscribableConfig(SWITCH_USER),
+ newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
mUser0.userId = 0;
mUser0.flags = 100;
@@ -169,42 +173,91 @@
}
@Test
- public void testTakeSupportedProperties_unsupportedOnly() {
+ public void testTakeSupportedProperties_supportedNoProperties() {
// Cannot use mUserHalService because it's already set with supported properties
UserHalService myHalService = new UserHalService(mVehicleHal);
- myHalService.takeProperties(Collections.EMPTY_LIST);
+ myHalService.takeProperties(Collections.emptyList());
assertThat(myHalService.isSupported()).isFalse();
+ assertThat(myHalService.isUserAssociationSupported()).isFalse();
+ }
+
+ @Test
+ public void testTakeSupportedProperties_supportedFewProperties() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+ myHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
+ newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER)));
+
+ assertThat(myHalService.isSupported()).isFalse();
+ assertThat(myHalService.isUserAssociationSupported()).isFalse();
+ }
+
+ @Test
+ public void testTakeSupportedProperties_supportedAllCoreProperties() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+ myHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
+ newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
+ newSubscribableConfig(SWITCH_USER)));
+
+ assertThat(myHalService.isSupported()).isTrue();
+ assertThat(myHalService.isUserAssociationSupported()).isFalse();
+ }
+
+ @Test
+ public void testTakeSupportedProperties_supportedAllProperties() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+ myHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
+ newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
+ newSubscribableConfig(SWITCH_USER),
+ newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
+
+ assertThat(myHalService.isSupported()).isTrue();
+ assertThat(myHalService.isUserAssociationSupported()).isTrue();
}
@Test
public void testTakeSupportedPropertiesAndInit() {
// Cannot use mUserHalService because it's already set with supported properties
UserHalService myHalService = new UserHalService(mVehicleHal);
-
VehiclePropConfig unsupportedConfig = newConfig(CURRENT_GEAR);
- VehiclePropConfig userInfoConfig = newSubscribableConfig(INITIAL_USER_INFO);
- List<VehiclePropConfig> input = Arrays.asList(unsupportedConfig, userInfoConfig);
- myHalService.takeProperties(input);
- assertThat(mUserHalService.isSupported()).isTrue();
+
+ myHalService.takeProperties(Arrays.asList(newSubscribableConfig(INITIAL_USER_INFO),
+ newSubscribableConfig(CREATE_USER), newSubscribableConfig(REMOVE_USER),
+ newSubscribableConfig(SWITCH_USER), unsupportedConfig,
+ newSubscribableConfig(USER_IDENTIFICATION_ASSOCIATION)));
+
// Ideally there should be 2 test methods (one for takeSupportedProperties() and one for
// init()), but on "real life" VehicleHal calls these 2 methods in sequence, and the latter
// depends on the properties set by the former, so it's ok to test both here...
myHalService.init();
verify(mVehicleHal).subscribeProperty(myHalService, INITIAL_USER_INFO);
+ verify(mVehicleHal).subscribeProperty(myHalService, CREATE_USER);
+ verify(mVehicleHal).subscribeProperty(myHalService, REMOVE_USER);
+ verify(mVehicleHal).subscribeProperty(myHalService, SWITCH_USER);
+ verify(mVehicleHal).subscribeProperty(myHalService, USER_IDENTIFICATION_ASSOCIATION);
}
@Test
public void testSupportedProperties() {
- assertThat(mUserHalService.getAllSupportedProperties()).asList().containsAllOf(
- INITIAL_USER_INFO,
- CREATE_USER,
- SWITCH_USER,
+ assertThat(mUserHalService.getAllSupportedProperties()).asList().containsExactly(
+ INITIAL_USER_INFO, CREATE_USER, REMOVE_USER, SWITCH_USER,
USER_IDENTIFICATION_ASSOCIATION);
}
@Test
+ public void testGetUserInfo_noHalSupported() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+
+ assertThrows(IllegalStateException.class, () -> myHalService.getInitialUserInfo(COLD_BOOT,
+ TIMEOUT_MS, mUsersInfo, noOpCallback()));
+ }
+
+ @Test
public void testGetUserInfo_invalidTimeout() {
assertThrows(IllegalArgumentException.class, () ->
mUserHalService.getInitialUserInfo(COLD_BOOT, 0, mUsersInfo, noOpCallback()));
@@ -415,6 +468,16 @@
}
@Test
+ public void testSwitchUser_noHalSupported() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+
+ assertThrows(IllegalStateException.class,
+ () -> myHalService.switchUser(createUserSwitchRequest(mUser10, mUsersInfo),
+ TIMEOUT_MS, noOpCallback()));
+ }
+
+ @Test
public void testSwitchUser_invalidTimeout() {
assertThrows(IllegalArgumentException.class, () -> mUserHalService
.switchUser(createUserSwitchRequest(mUser10, mUsersInfo), 0, noOpCallback()));
@@ -645,6 +708,15 @@
}
@Test
+ public void testPostSwitchResponse_noHalSupported() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+
+ assertThrows(IllegalStateException.class,
+ () -> myHalService.postSwitchResponse(new SwitchUserRequest()));
+ }
+
+ @Test
public void testPostSwitchResponse_noUsersInfo() {
SwitchUserRequest request = createUserSwitchRequest(mUser10, null);
request.requestId = 42;
@@ -686,6 +758,15 @@
}
@Test
+ public void testRemoveUser_noHalSupported() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+
+ assertThrows(IllegalStateException.class,
+ () -> myHalService.removeUser(new RemoveUserRequest()));
+ }
+
+ @Test
public void testRemoveUser_nullRequest() {
RemoveUserRequest request = null;
@@ -735,6 +816,15 @@
}
@Test
+ public void testLegacyUserSwitch_noHalSupported() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+
+ assertThrows(IllegalStateException.class,
+ () -> myHalService.legacyUserSwitch(new SwitchUserRequest()));
+ }
+
+ @Test
public void testLegacyUserSwitch_noUsersInfo() {
SwitchUserRequest request = new SwitchUserRequest();
request.messageType = SwitchUserMessageType.ANDROID_SWITCH;
@@ -762,6 +852,15 @@
}
@Test
+ public void testCreateUser_noHalSupported() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+
+ assertThrows(IllegalStateException.class,
+ () -> myHalService.createUser(new CreateUserRequest(), TIMEOUT_MS, noOpCallback()));
+ }
+
+ @Test
public void testCreateUser_noRequest() {
assertThrows(NullPointerException.class, () -> mUserHalService
.createUser(null, TIMEOUT_MS, noOpCallback()));
@@ -842,7 +941,7 @@
}
@Test
- public void testUserCreate_success() throws Exception {
+ public void testCreateUser_success() throws Exception {
VehiclePropValue propResponse =
UserHalHelper.createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER);
propResponse.value.int32Values.add(CreateUserStatus.SUCCESS);
@@ -870,7 +969,7 @@
}
@Test
- public void testUserCreate_failure() throws Exception {
+ public void testCreateUser_failure() throws Exception {
VehiclePropValue propResponse =
UserHalHelper.createPropRequest(CREATE_USER, REQUEST_ID_PLACE_HOLDER);
propResponse.value.int32Values.add(CreateUserStatus.FAILURE);
@@ -937,6 +1036,15 @@
}
@Test
+ public void testGetUserAssociation_noHalSupported() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+
+ assertThrows(IllegalStateException.class,
+ () -> myHalService.getUserAssociation(new UserIdentificationGetRequest()));
+ }
+
+ @Test
public void testGetUserAssociation_nullRequest() {
assertThrows(NullPointerException.class, () -> mUserHalService.getUserAssociation(null));
}
@@ -1046,6 +1154,15 @@
}
@Test
+ public void testSetUserAssociation_noHalSupported() {
+ // Cannot use mUserHalService because it's already set with supported properties
+ UserHalService myHalService = new UserHalService(mVehicleHal);
+
+ assertThrows(IllegalStateException.class, () -> myHalService.setUserAssociation(TIMEOUT_MS,
+ new UserIdentificationSetRequest(), noOpCallback()));
+ }
+
+ @Test
public void testSetUserAssociation_invalidTimeout() {
UserIdentificationSetRequest request = new UserIdentificationSetRequest();
assertThrows(IllegalArgumentException.class, () ->
@@ -1518,4 +1635,4 @@
+ mExtraCalls);
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/carservice_unit_test/src/com/android/car/user/CarUserManagerUnitTest.java b/tests/carservice_unit_test/src/com/android/car/user/CarUserManagerUnitTest.java
index 1003957..3d38ac4 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/CarUserManagerUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/CarUserManagerUnitTest.java
@@ -369,6 +369,22 @@
assertThat(result.getErrorMessage()).isEqualTo("D'OH!");
}
+ @Test
+ public void testIsUserHalUserAssociation() throws Exception {
+ when(mService.isUserHalUserAssociationSupported()).thenReturn(false).thenReturn(true);
+
+ assertThat(mMgr.isUserHalUserAssociationSupported()).isFalse();
+ assertThat(mMgr.isUserHalUserAssociationSupported()).isTrue();
+ }
+
+ @Test
+ public void testIsUserHalUserAssociation_remoteException() throws Exception {
+ doThrow(new RemoteException("D'OH!")).when(mService).isUserHalUserAssociationSupported();
+ mockHandleRemoteExceptionFromCarServiceWithDefaultValue(mCar);
+
+ assertThat(mMgr.isUserHalUserAssociationSupported()).isFalse();
+ }
+
private void expectServiceSwitchUserSucceeds(@UserIdInt int userId,
@UserSwitchResult.Status int status, @Nullable String errorMessage)
throws RemoteException {
diff --git a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
index 79d3282..ee5c311 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/CarUserServiceTest.java
@@ -219,6 +219,8 @@
doReturn(mMockedDrawable).when(mMockedDrawable).mutate();
doReturn(1).when(mMockedDrawable).getIntrinsicWidth();
doReturn(1).when(mMockedDrawable).getIntrinsicHeight();
+ mockUserHalSupported(true);
+ mockUserHalUserAssociationSupported(true);
doReturn(Optional.of(mAsyncCallTimeoutMs)).when(() -> CarProperties.user_hal_timeout());
mCarUserService =
new CarUserService(
@@ -245,6 +247,31 @@
}
@Test
+ public void testOnUserLifecycleEvent_legacyUserSwitch_halCalled() throws Exception {
+ // Arrange
+ mockExistingUsers();
+
+ // Act
+ sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
+
+ // Verify
+ verify(mUserHal).legacyUserSwitch(any());
+ }
+
+ @Test
+ public void testOnUserLifecycleEvent_legacyUserSwitch_halnotSupported() throws Exception {
+ // Arrange
+ mockExistingUsers();
+ mockUserHalSupported(false);
+
+ // Act
+ sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
+
+ // Verify
+ verify(mUserHal, never()).legacyUserSwitch(any());
+ }
+
+ @Test
public void testOnUserLifecycleEvent_notifyListener() throws Exception {
// Arrange
mCarUserService.addUserLifecycleListener(mUserLifecycleListener);
@@ -721,6 +748,19 @@
}
@Test
+ public void testRemoveUser_halNotSupported() throws Exception {
+ mockExistingUsersAndCurrentUser(mAdminUser);
+ int removeUserId = mRegularUser.id;
+ mockUserHalSupported(false);
+ when(mMockedUserManager.removeUser(removeUserId)).thenReturn(true);
+
+ UserRemovalResult result = mCarUserService.removeUser(removeUserId);
+
+ assertThat(result.getStatus()).isEqualTo(UserRemovalResult.STATUS_SUCCESSFUL);
+ verify(mUserHal, never()).removeUser(any());
+ }
+
+ @Test
public void testRemoveUser_androidFailure() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int targetUserId = mRegularUser.id;
@@ -756,6 +796,36 @@
}
@Test
+ public void testSwitchUser_halNotSupported_success() throws Exception {
+ mockExistingUsersAndCurrentUser(mAdminUser);
+ mockUserHalSupported(false);
+ mockAmSwitchUser(mRegularUser, true);
+
+ mCarUserService.switchUser(mRegularUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertThat(getUserSwitchResult().getStatus())
+ .isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+ verify(mUserHal, never()).switchUser(any(), anyInt(), any());
+
+ // update current user due to successful user switch
+ mockCurrentUser(mRegularUser);
+ sendUserUnlockedEvent(mRegularUser.id);
+ assertNoPostSwitch();
+ }
+
+ @Test
+ public void testSwitchUser_halNotSupported_failure() throws Exception {
+ mockExistingUsersAndCurrentUser(mAdminUser);
+ mockUserHalSupported(false);
+
+ mCarUserService.switchUser(mRegularUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertThat(getUserSwitchResult().getStatus())
+ .isEqualTo(UserSwitchResult.STATUS_ANDROID_FAILURE);
+ verify(mUserHal, never()).switchUser(any(), anyInt(), any());
+ }
+
+ @Test
public void testSwitchUser_HalSuccessAndroidSuccess() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int requestId = 42;
@@ -1206,6 +1276,22 @@
}
@Test
+ public void testCreateUser_halNotSupported_success() throws Exception {
+ mockUserHalSupported(false);
+ mockExistingUsersAndCurrentUser(mAdminUser);
+ int userId = mGuestUser.id;
+ mockUmCreateUser(mMockedUserManager, "dude", UserManager.USER_TYPE_FULL_GUEST,
+ UserInfo.FLAG_EPHEMERAL, userId);
+
+ mCarUserService.createUser("dude", UserManager.USER_TYPE_FULL_GUEST,
+ UserInfo.FLAG_EPHEMERAL, mAsyncCallTimeoutMs, mUserCreationFuture);
+
+ UserCreationResult result = getUserCreationResult();
+ assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_SUCCESSFUL);
+ verify(mUserHal, never()).createUser(any(), anyInt(), any());
+ }
+
+ @Test
public void testCreateUser_success() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int userId = mGuestUser.id;
@@ -1362,6 +1448,17 @@
assertUserName(resultData, newUserName);
}
+ @Test
+ public void testGetUserInfo_halNotSupported() throws Exception {
+ mockExistingUsersAndCurrentUser(mAdminUser);
+ mockUserHalSupported(false);
+
+ mCarUserService.getInitialUserInfo(mGetUserInfoRequestType, mAsyncCallTimeoutMs, mReceiver);
+
+ verify(mUserHal, never()).getInitialUserInfo(anyInt(), anyInt(), any(), any());
+ assertThat(mReceiver.getResultCode()).isEqualTo(HalCallback.STATUS_HAL_NOT_SUPPORTED);
+ }
+
/**
* Tests the {@code getUserInfo()} that's used by other services.
*/
@@ -1383,6 +1480,17 @@
}
@Test
+ public void testGetInitialUserInfo_halNotSupported_callback() throws Exception {
+ int requestType = 42;
+ mockUserHalSupported(false);
+ HalCallback<InitialUserInfoResponse> callback = (s, r) -> { };
+
+ mCarUserService.getInitialUserInfo(requestType, callback);
+
+ verify(mUserHal, never()).getInitialUserInfo(anyInt(), anyInt(), any(), any());
+ }
+
+ @Test
public void testGetInitialUserInfo_invalidPermission() throws Exception {
mockManageUsersPermission(android.Manifest.permission.MANAGE_USERS, false);
assertThrows(SecurityException.class,
@@ -1440,8 +1548,6 @@
public void testGetUserIdentificationAssociation_service_returnNull() throws Exception {
mockCurrentUserForBinderCalls();
- // Not mocking service call, so it will return null
-
UserIdentificationAssociationResponse response = mCarUserService
.getUserIdentificationAssociation(new int[] { 108 });
@@ -1451,6 +1557,19 @@
}
@Test
+ public void testGetUserIdentificationAssociation_halNotSupported() throws Exception {
+ mockUserHalUserAssociationSupported(false);
+
+ UserIdentificationAssociationResponse response = mCarUserService
+ .getUserIdentificationAssociation(new int[] { });
+
+ assertThat(response.isSuccess()).isFalse();
+ assertThat(response.getValues()).isNull();
+ assertThat(response.getErrorMessage()).isEqualTo(CarUserService.VEHICLE_HAL_NOT_SUPPORTED);
+ verify(mUserHal, never()).getUserAssociation(any());
+ }
+
+ @Test
public void testGetUserIdentificationAssociation_ok() throws Exception {
UserInfo currentUser = mockCurrentUserForBinderCalls();
@@ -1517,6 +1636,22 @@
}
@Test
+ public void testSetUserIdentificationAssociation_halNotSupported() throws Exception {
+ int[] types = new int[] { 1, 2, 3 };
+ int[] values = new int[] { 10, 20, 30 };
+ mockUserHalUserAssociationSupported(false);
+
+ mCarUserService.setUserIdentificationAssociation(mAsyncCallTimeoutMs, types, values,
+ mUserAssociationRespFuture);
+ UserIdentificationAssociationResponse response = getUserAssociationRespResult();
+
+ assertThat(response.isSuccess()).isFalse();
+ assertThat(response.getValues()).isNull();
+ assertThat(response.getErrorMessage()).isEqualTo(CarUserService.VEHICLE_HAL_NOT_SUPPORTED);
+ verify(mUserHal, never()).setUserAssociation(anyInt(), any(), any());
+ }
+
+ @Test
public void testSetUserIdentificationAssociation_halFailedWithErrorMessage() throws Exception {
mockCurrentUserForBinderCalls();
mockHalSetUserIdentificationAssociationFailure("D'OH!");
@@ -1823,6 +1958,15 @@
anyInt(), anyInt(), eq(true)));
}
+ private void mockUserHalSupported(boolean result) {
+ when(mUserHal.isSupported()).thenReturn(result);
+ }
+
+ private void mockUserHalUserAssociationSupported(boolean result) {
+ when(mUserHal.isUserAssociationSupported()).thenReturn(result);
+ }
+
+
/**
* Asserts a {@link UsersInfo} that was created based on {@link #mockCurrentUsers(UserInfo)}.
*/
diff --git a/user/car-user-lib/src/android/car/userlib/HalCallback.java b/user/car-user-lib/src/android/car/userlib/HalCallback.java
index 55528a3..784d01e 100644
--- a/user/car-user-lib/src/android/car/userlib/HalCallback.java
+++ b/user/car-user-lib/src/android/car/userlib/HalCallback.java
@@ -35,6 +35,7 @@
int STATUS_HAL_RESPONSE_TIMEOUT = 3;
int STATUS_WRONG_HAL_RESPONSE = 4;
int STATUS_CONCURRENT_OPERATION = 5;
+ int STATUS_HAL_NOT_SUPPORTED = 6;
/** @hide */
@IntDef(prefix = { "STATUS_" }, value = {
@@ -42,7 +43,8 @@
STATUS_HAL_SET_TIMEOUT,
STATUS_HAL_RESPONSE_TIMEOUT,
STATUS_WRONG_HAL_RESPONSE,
- STATUS_CONCURRENT_OPERATION
+ STATUS_CONCURRENT_OPERATION,
+ STATUS_HAL_NOT_SUPPORTED
})
@Retention(RetentionPolicy.SOURCE)
@interface HalCallbackStatus{}