Merge "Handle power cycle change in car watchdog daemon" into rvc-dev
diff --git a/service/src/com/android/car/hal/UserHalService.java b/service/src/com/android/car/hal/UserHalService.java
index 8d7fac6..595ada5 100644
--- a/service/src/com/android/car/hal/UserHalService.java
+++ b/service/src/com/android/car/hal/UserHalService.java
@@ -134,6 +134,10 @@
mHandler.sendMessage(obtainMessage(
UserHalService::handleOnInitialUserInfoResponse, this, value));
break;
+ case SWITCH_USER:
+ mHandler.sendMessage(obtainMessage(
+ UserHalService::handleOnSwicthUserResponse, this, value));
+ break;
default:
Slog.w(TAG, "received unsupported event from HAL: " + value);
}
@@ -188,7 +192,7 @@
* {@link android.hardware.automotive.vehicle.V2_0.InitialUserInfoRequestType}).
* @param timeoutMs how long to wait (in ms) for the property change event.
* @param usersInfo current state of Android users.
- * @param callback callback to handle the response.
+ * @param callback to handle the response.
*
* @throws IllegalStateException if the HAL does not support user management (callers should
* call {@link #isSupported()} first to avoid this exception).
@@ -209,17 +213,9 @@
checkSupportedLocked();
if (hasPendingRequestLocked(InitialUserInfoResponse.class, callback)) return;
requestId = mNextRequestId++;
- // TODO(b/150413515): use helper method to convert request to prop value
propRequest.value.int32Values.add(requestId);
propRequest.value.int32Values.add(requestType);
- propRequest.value.int32Values.add(usersInfo.currentUser.userId);
- propRequest.value.int32Values.add(usersInfo.currentUser.flags);
- propRequest.value.int32Values.add(usersInfo.numberUsers);
- for (int i = 0; i < usersInfo.numberUsers; i++) {
- UserInfo userInfo = usersInfo.existingUsers.get(i);
- propRequest.value.int32Values.add(userInfo.userId);
- propRequest.value.int32Values.add(userInfo.flags);
- }
+ addUsersInfo(propRequest, usersInfo);
setTimestamp(propRequest);
addPendingRequestLocked(requestId, InitialUserInfoResponse.class, callback);
}
@@ -238,18 +234,67 @@
}
/**
- * TODO(b/150409110): javadoc it :-)
+ * Calls HAL to asynchronously switch user.
+ *
+ * @param targetInfo target user for user switching
+ * @param timeoutMs how long to wait (in ms) for the property change event.
+ * @param usersInfo current state of Android users.
+ * @param callback to handle the response.
+ *
+ * @throws IllegalStateException if the HAL does not support user management (callers should
+ * call {@link #isSupported()} first to avoid this exception).
*/
public void switchUser(@NonNull UserInfo targetInfo, int timeoutMs,
@NonNull UsersInfo usersInfo, @NonNull HalCallback<SwitchUserResponse> callback) {
if (DBG) Log.d(TAG, "switchUser(" + targetInfo + ")");
+ Preconditions.checkArgumentPositive(timeoutMs, "timeout must be positive");
+ Objects.requireNonNull(usersInfo);
+ // TODO(b/150413515): use helper method to convert request to prop value and check usersInfo
+ // is valid
+ Objects.requireNonNull(callback);
- // TODO(b/150409110): implement
- SwitchUserResponse response = new SwitchUserResponse();
- response.messageType = SwitchUserMessageType.VEHICLE_RESPONSE;
- response.status = SwitchUserStatus.SUCCESS;
- response.requestId = mNextRequestId++;
- callback.onResponse(HalCallback.STATUS_OK, response);
+ VehiclePropValue propRequest = new VehiclePropValue();
+ propRequest.prop = SWITCH_USER;
+ int requestId;
+ synchronized (mLock) {
+ checkSupportedLocked();
+ if (hasPendingRequestLocked(SwitchUserResponse.class, callback)) return;
+ requestId = mNextRequestId++;
+ // TODO(b/150413515): use helper method to convert request to prop value
+ propRequest.value.int32Values.add(requestId);
+ propRequest.value.int32Values.add(SwitchUserMessageType.ANDROID_SWITCH);
+ propRequest.value.int32Values.add(targetInfo.userId);
+ propRequest.value.int32Values.add(targetInfo.flags);
+ addUsersInfo(propRequest, usersInfo);
+ setTimestamp(propRequest);
+ addPendingRequestLocked(requestId, SwitchUserResponse.class, callback);
+ }
+
+ mHandler.sendMessageDelayed(
+ obtainMessage(UserHalService::handleCheckIfRequestTimedOut, this, requestId)
+ .setWhat(requestId),
+ timeoutMs);
+
+ try {
+ if (DBG) Log.d(TAG, "Calling hal.set(): " + propRequest);
+ mHal.set(propRequest);
+ } catch (ServiceSpecificException e) {
+ handleRemovePendingRequest(requestId);
+ Log.w(TAG, "Failed to set ANDROID SWITCH", e);
+ callback.onResponse(HalCallback.STATUS_HAL_SET_TIMEOUT, null);
+ }
+ }
+
+ private static void addUsersInfo(VehiclePropValue propRequest, @NonNull UsersInfo usersInfo) {
+ // TODO(b/150419600) it should be moved to UserHalHelper and tested
+ propRequest.value.int32Values.add(usersInfo.currentUser.userId);
+ propRequest.value.int32Values.add(usersInfo.currentUser.flags);
+ propRequest.value.int32Values.add(usersInfo.numberUsers);
+ for (int i = 0; i < usersInfo.numberUsers; i++) {
+ UserInfo userInfo = usersInfo.existingUsers.get(i);
+ propRequest.value.int32Values.add(userInfo.userId);
+ propRequest.value.int32Values.add(userInfo.flags);
+ }
}
@GuardedBy("mLock")
@@ -345,6 +390,36 @@
callback.onResponse(HalCallback.STATUS_OK, response);
}
+ private void handleOnSwicthUserResponse(VehiclePropValue value) {
+ int requestId = value.value.int32Values.get(0);
+ HalCallback<SwitchUserResponse> callback =
+ handleGetPendingCallback(requestId, SwitchUserResponse.class);
+ if (callback == null) {
+ Log.w(TAG, "no callback for requestId " + requestId + ": " + value);
+ return;
+ }
+ handleRemovePendingRequest(requestId);
+ SwitchUserResponse response = new SwitchUserResponse();
+ response.requestId = requestId;
+ response.messageType = value.value.int32Values.get(1);
+ if (response.messageType != SwitchUserMessageType.VEHICLE_RESPONSE) {
+ Log.e(TAG, "invalid message type (" + response.messageType + ") from HAL: " + value);
+ callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null);
+ return;
+ }
+ response.status = value.value.int32Values.get(2);
+ if (response.status == SwitchUserStatus.SUCCESS
+ || response.status == SwitchUserStatus.FAILURE) {
+ if (DBG) {
+ Log.d(TAG, "replying to request " + requestId + " with " + response);
+ }
+ callback.onResponse(HalCallback.STATUS_OK, response);
+ } else {
+ Log.e(TAG, "invalid status (" + response.status + ") from HAL: " + value);
+ callback.onResponse(HalCallback.STATUS_WRONG_HAL_RESPONSE, null);
+ }
+ }
+
private <T> HalCallback<T> handleGetPendingCallback(int requestId, Class<T> clazz) {
Pair<Class<?>, HalCallback<?>> pair = getPendingCallback(requestId);
if (pair == null) return null;
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/system_feature_fragment.xml b/tests/EmbeddedKitchenSinkApp/res/layout/system_feature_fragment.xml
new file mode 100644
index 0000000..45d1dbb
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/system_feature_fragment.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ListView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/sys_features_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</ListView>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index 6e36cf3..4b5b568 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -64,6 +64,7 @@
import com.google.android.car.kitchensink.sensor.SensorsTestFragment;
import com.google.android.car.kitchensink.storagelifetime.StorageLifetimeFragment;
import com.google.android.car.kitchensink.storagevolumes.StorageVolumesFragment;
+import com.google.android.car.kitchensink.systemfeatures.SystemFeaturesFragment;
import com.google.android.car.kitchensink.touch.TouchTestFragment;
import com.google.android.car.kitchensink.users.UsersFragment;
import com.google.android.car.kitchensink.vehiclectrl.VehicleCtrlFragment;
@@ -187,6 +188,7 @@
new FragmentMenuEntry("sensors", SensorsTestFragment.class),
new FragmentMenuEntry("storage lifetime", StorageLifetimeFragment.class),
new FragmentMenuEntry("storage volumes", StorageVolumesFragment.class),
+ new FragmentMenuEntry("system features", SystemFeaturesFragment.class),
new FragmentMenuEntry("touch test", TouchTestFragment.class),
new FragmentMenuEntry("users", UsersFragment.class),
new FragmentMenuEntry("vehicle ctrl", VehicleCtrlFragment.class),
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/systemfeatures/SystemFeaturesFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/systemfeatures/SystemFeaturesFragment.java
new file mode 100644
index 0000000..f3d51b2
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/systemfeatures/SystemFeaturesFragment.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.car.kitchensink.systemfeatures;
+
+import android.annotation.Nullable;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * Shows system features as available by PackageManager
+ */
+public class SystemFeaturesFragment extends Fragment {
+ private static final String TAG = "CAR.SYSFEATURES.KS";
+
+ private ListView mSysFeaturesList;
+ private PackageManager mPackageManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mPackageManager = Objects.requireNonNull(
+ getContext().getPackageManager());
+
+ super.onCreate(savedInstanceState);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ mSysFeaturesList = (ListView) inflater.inflate(R.layout.system_feature_fragment,
+ container, false);
+ return mSysFeaturesList;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refresh();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private void refresh() {
+ final FeatureInfo[] features = mPackageManager.getSystemAvailableFeatures();
+ if (features != null) {
+ final String[] descriptions = Arrays.stream(features)
+ .filter(fi -> fi != null && fi.name != null)
+ .sorted(Comparator.<FeatureInfo, String>comparing(fi -> fi.name)
+ .thenComparing(fi -> fi.version))
+ .map(fi -> String.format("%s (v=%d)", fi.name, fi.version))
+ .toArray(String[]::new);
+ mSysFeaturesList.setAdapter(new ArrayAdapter<>(getContext(),
+ android.R.layout.simple_list_item_1, descriptions));
+ } else {
+ Log.e(TAG, "no features available on this device!");
+ }
+ }
+}
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 159efa1..622c66b 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
@@ -35,6 +35,9 @@
import android.car.userlib.UserHalHelper;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponse;
import android.hardware.automotive.vehicle.V2_0.InitialUserInfoResponseAction;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserMessageType;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserResponse;
+import android.hardware.automotive.vehicle.V2_0.SwitchUserStatus;
import android.hardware.automotive.vehicle.V2_0.UserFlags;
import android.hardware.automotive.vehicle.V2_0.UserInfo;
import android.hardware.automotive.vehicle.V2_0.UsersInfo;
@@ -69,22 +72,21 @@
private static final String TAG = UserHalServiceTest.class.getSimpleName();
/**
- * Timeout passed to {@link UserHalService#getInitialUserInfo(int, int, UsersInfo, HalCallback)}
- * calls.
+ * Timeout passed to {@link UserHalService} methods
*/
- private static final int INITIAL_USER_TIMEOUT_MS = 20;
+ private static final int TIMEOUT_MS = 20;
/**
* Timeout for {@link GenericHalCallback#assertCalled()} for tests where the HAL is supposed to
* return something - it's a short time so it doesn't impact the test duration.
*/
- private static final int INITIAL_USER_CALLBACK_TIMEOUT_SUCCESS = INITIAL_USER_TIMEOUT_MS + 50;
+ private static final int CALLBACK_TIMEOUT_SUCCESS = TIMEOUT_MS + 50;
/**
* Timeout for {@link GenericHalCallback#assertCalled()} for tests where the HAL is not supposed
* to return anything - it's a slightly longer to make sure the test doesn't fail prematurely.
*/
- private static final int INITIAL_USER_CALLBACK_TIMEOUT_TIMEOUT = INITIAL_USER_TIMEOUT_MS + 500;
+ private static final int CALLBACK_TIMEOUT_TIMEOUT = TIMEOUT_MS + 500;
// Used when crafting a reqquest property - the real value will be set by the mock.
private static final int REQUEST_ID_PLACE_HOLDER = 42;
@@ -167,7 +169,7 @@
@Test
public void testGetUserInfo_noUsersInfo() {
assertThrows(NullPointerException.class,
- () -> mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, null,
+ () -> mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, null,
(i, r) -> {
}));
}
@@ -175,7 +177,7 @@
@Test
public void testGetUserInfo_noCallback() {
assertThrows(NullPointerException.class,
- () -> mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS,
+ () -> mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS,
mUsersInfo, null));
}
@@ -184,8 +186,8 @@
replySetPropertyWithTimeoutException(INITIAL_USER_INFO);
GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
- INITIAL_USER_CALLBACK_TIMEOUT_TIMEOUT);
- mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, mUsersInfo,
+ CALLBACK_TIMEOUT_TIMEOUT);
+ mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, mUsersInfo,
callback);
callback.assertCalled();
@@ -193,15 +195,15 @@
assertThat(callback.response).isNull();
// Make sure the pending request was removed
- SystemClock.sleep(INITIAL_USER_CALLBACK_TIMEOUT_TIMEOUT);
+ SystemClock.sleep(CALLBACK_TIMEOUT_TIMEOUT);
callback.assertNotCalledAgain();
}
@Test
public void testGetUserInfo_halDidNotReply() throws Exception {
GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
- INITIAL_USER_CALLBACK_TIMEOUT_TIMEOUT);
- mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, mUsersInfo,
+ CALLBACK_TIMEOUT_TIMEOUT);
+ mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, mUsersInfo,
callback);
callback.assertCalled();
@@ -212,12 +214,12 @@
@Test
public void testGetUserInfo_secondCallFailWhilePending() throws Exception {
GenericHalCallback<InitialUserInfoResponse> callback1 = new GenericHalCallback<>(
- INITIAL_USER_CALLBACK_TIMEOUT_TIMEOUT);
+ CALLBACK_TIMEOUT_TIMEOUT);
GenericHalCallback<InitialUserInfoResponse> callback2 = new GenericHalCallback<>(
- INITIAL_USER_CALLBACK_TIMEOUT_TIMEOUT);
- mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, mUsersInfo,
+ CALLBACK_TIMEOUT_TIMEOUT);
+ mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, mUsersInfo,
callback1);
- mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, mUsersInfo,
+ mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, mUsersInfo,
callback2);
callback1.assertCalled();
@@ -240,8 +242,8 @@
/* rightRequestId= */ false);
GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
- INITIAL_USER_CALLBACK_TIMEOUT_TIMEOUT);
- mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, mUsersInfo,
+ CALLBACK_TIMEOUT_TIMEOUT);
+ mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, mUsersInfo,
callback);
callback.assertCalled();
@@ -261,8 +263,8 @@
INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
- INITIAL_USER_CALLBACK_TIMEOUT_SUCCESS);
- mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, mUsersInfo,
+ CALLBACK_TIMEOUT_SUCCESS);
+ mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, mUsersInfo,
callback);
callback.assertCalled();
@@ -287,8 +289,8 @@
INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
- INITIAL_USER_CALLBACK_TIMEOUT_SUCCESS);
- mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, mUsersInfo,
+ CALLBACK_TIMEOUT_SUCCESS);
+ mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, mUsersInfo,
callback);
callback.assertCalled();
@@ -320,8 +322,8 @@
INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
- INITIAL_USER_CALLBACK_TIMEOUT_SUCCESS);
- mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, mUsersInfo,
+ CALLBACK_TIMEOUT_SUCCESS);
+ mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, mUsersInfo,
callback);
callback.assertCalled();
@@ -355,8 +357,8 @@
INITIAL_USER_INFO, propResponse, /* rightRequestId= */ true);
GenericHalCallback<InitialUserInfoResponse> callback = new GenericHalCallback<>(
- INITIAL_USER_CALLBACK_TIMEOUT_SUCCESS);
- mUserHalService.getInitialUserInfo(COLD_BOOT, INITIAL_USER_TIMEOUT_MS, mUsersInfo,
+ CALLBACK_TIMEOUT_SUCCESS);
+ mUserHalService.getInitialUserInfo(COLD_BOOT, TIMEOUT_MS, mUsersInfo,
callback);
callback.assertCalled();
@@ -381,6 +383,204 @@
testGetUserInfo_successDefault();
}
+ @Test
+ public void testSwitchUser_invalidTimeout() {
+ assertThrows(IllegalArgumentException.class,
+ () -> mUserHalService.switchUser(mUser10, 0, mUsersInfo, (i, r) -> {
+ }));
+ assertThrows(IllegalArgumentException.class,
+ () -> mUserHalService.switchUser(mUser10, -1, mUsersInfo, (i, r) -> {
+ }));
+ }
+
+ @Test
+ public void testSwitchUser_noUsersInfo() {
+ assertThrows(NullPointerException.class,
+ () -> mUserHalService.switchUser(mUser10, TIMEOUT_MS, null,
+ (i, r) -> {
+ }));
+ }
+
+ @Test
+ public void testSwitchUser_noCallback() {
+ assertThrows(NullPointerException.class,
+ () -> mUserHalService.switchUser(mUser10, TIMEOUT_MS,
+ mUsersInfo, null));
+ }
+
+ @Test
+ public void testSwitchUser_halSetTimedOut() throws Exception {
+ replySetPropertyWithTimeoutException(SWITCH_USER);
+
+ GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
+ CALLBACK_TIMEOUT_TIMEOUT);
+ mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+
+ callback.assertCalled();
+ assertCallbackStatus(callback, HalCallback.STATUS_HAL_SET_TIMEOUT);
+ assertThat(callback.response).isNull();
+
+ // Make sure the pending request was removed
+ SystemClock.sleep(CALLBACK_TIMEOUT_TIMEOUT);
+ callback.assertNotCalledAgain();
+ }
+
+ @Test
+ public void testSwitchUser_halDidNotReply() throws Exception {
+ GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
+ CALLBACK_TIMEOUT_TIMEOUT);
+ mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+
+ callback.assertCalled();
+ assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
+ assertThat(callback.response).isNull();
+ }
+
+ @Test
+ public void testSwitchUser_halReplyWithWrongRequestId() throws Exception {
+ // TODO(b/150419600): use helper method to convert prop value to proper req
+ VehiclePropValue propResponse = new VehiclePropValue();
+ propResponse.prop = SWITCH_USER;
+ propResponse.value.int32Values.add(REQUEST_ID_PLACE_HOLDER);
+
+ replySetPropertyWithOnChangeEvent(SWITCH_USER, propResponse,
+ /* rightRequestId= */ false);
+
+ GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
+ CALLBACK_TIMEOUT_TIMEOUT);
+ mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+
+ callback.assertCalled();
+ assertCallbackStatus(callback, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
+ assertThat(callback.response).isNull();
+ }
+
+ @Test
+ public void testSwitchUser_halReturnedInvalidMessageType() throws Exception {
+ // TODO(b/150419600): use helper method to convert prop value to proper req
+ VehiclePropValue propResponse = new VehiclePropValue();
+ propResponse.prop = SWITCH_USER;
+ propResponse.value.int32Values.add(REQUEST_ID_PLACE_HOLDER);
+ propResponse.value.int32Values.add(SwitchUserMessageType.VEHICLE_REQUEST);
+ propResponse.value.int32Values.add(SwitchUserStatus.SUCCESS);
+
+ AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
+ SWITCH_USER, propResponse, /* rightRequestId= */ true);
+
+ GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
+ CALLBACK_TIMEOUT_SUCCESS);
+ mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+
+ callback.assertCalled();
+
+ // Make sure the arguments were properly converted
+ assertSwitchUserSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
+
+ // Assert response
+ assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
+ assertThat(callback.response).isNull();
+ }
+
+ @Test
+ public void testUserSwitch_success() throws Exception {
+ // TODO(b/150419600): use helper method to convert prop value to proper req
+ VehiclePropValue propResponse = new VehiclePropValue();
+ propResponse.prop = SWITCH_USER;
+ propResponse.value.int32Values.add(REQUEST_ID_PLACE_HOLDER);
+ propResponse.value.int32Values.add(SwitchUserMessageType.VEHICLE_RESPONSE);
+ propResponse.value.int32Values.add(SwitchUserStatus.SUCCESS);
+
+ AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
+ SWITCH_USER, propResponse, /* rightRequestId= */ true);
+
+ GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
+ CALLBACK_TIMEOUT_SUCCESS);
+ mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+
+ callback.assertCalled();
+
+ // Make sure the arguments were properly converted
+ assertSwitchUserSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
+
+ // Assert response
+ assertCallbackStatus(callback, HalCallback.STATUS_OK);
+ SwitchUserResponse actualResponse = callback.response;
+ assertThat(actualResponse.status).isEqualTo(SwitchUserStatus.SUCCESS);
+ assertThat(actualResponse.messageType).isEqualTo(SwitchUserMessageType.VEHICLE_RESPONSE);
+ }
+
+ @Test
+ public void testUserSwitch_failure() throws Exception {
+ // TODO(b/150419600): use helper method to convert prop value to proper req
+ VehiclePropValue propResponse = new VehiclePropValue();
+ propResponse.prop = SWITCH_USER;
+ propResponse.value.int32Values.add(REQUEST_ID_PLACE_HOLDER);
+ propResponse.value.int32Values.add(SwitchUserMessageType.VEHICLE_RESPONSE);
+ propResponse.value.int32Values.add(SwitchUserStatus.FAILURE);
+
+ AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
+ SWITCH_USER, propResponse, /* rightRequestId= */ true);
+
+ GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
+ CALLBACK_TIMEOUT_SUCCESS);
+ mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+
+ callback.assertCalled();
+
+ // Make sure the arguments were properly converted
+ assertSwitchUserSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
+
+ // Assert response
+ assertCallbackStatus(callback, HalCallback.STATUS_OK);
+ SwitchUserResponse actualResponse = callback.response;
+ assertThat(actualResponse.status).isEqualTo(SwitchUserStatus.FAILURE);
+ assertThat(actualResponse.messageType).isEqualTo(SwitchUserMessageType.VEHICLE_RESPONSE);
+ }
+
+ @Test
+ public void testSwitchUser_secondCallFailWhilePending() throws Exception {
+ GenericHalCallback<SwitchUserResponse> callback1 = new GenericHalCallback<>(
+ CALLBACK_TIMEOUT_TIMEOUT);
+ GenericHalCallback<SwitchUserResponse> callback2 = new GenericHalCallback<>(
+ CALLBACK_TIMEOUT_TIMEOUT);
+ mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback1);
+ mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback2);
+
+ callback1.assertCalled();
+ assertCallbackStatus(callback1, HalCallback.STATUS_HAL_RESPONSE_TIMEOUT);
+ assertThat(callback1.response).isNull();
+
+ callback2.assertCalled();
+ assertCallbackStatus(callback2, HalCallback.STATUS_CONCURRENT_OPERATION);
+ assertThat(callback1.response).isNull();
+ }
+
+ @Test
+ public void testSwitchUser_halReturnedInvalidStatus() throws Exception {
+ // TODO(b/150419600): use helper method to convert prop value to proper req
+ VehiclePropValue propResponse = new VehiclePropValue();
+ propResponse.prop = SWITCH_USER;
+ propResponse.value.int32Values.add(REQUEST_ID_PLACE_HOLDER);
+ propResponse.value.int32Values.add(SwitchUserMessageType.VEHICLE_RESPONSE);
+ propResponse.value.int32Values.add(/*status =*/ 110); // an invalid status
+
+ AtomicReference<VehiclePropValue> reqCaptor = replySetPropertyWithOnChangeEvent(
+ SWITCH_USER, propResponse, /* rightRequestId= */ true);
+
+ GenericHalCallback<SwitchUserResponse> callback = new GenericHalCallback<>(
+ CALLBACK_TIMEOUT_SUCCESS);
+ mUserHalService.switchUser(mUser10, TIMEOUT_MS, mUsersInfo, callback);
+
+ callback.assertCalled();
+
+ // Make sure the arguments were properly converted
+ assertSwitchUserSetRequest(reqCaptor.get(), SwitchUserMessageType.ANDROID_SWITCH, mUser10);
+
+ // Assert response
+ assertCallbackStatus(callback, HalCallback.STATUS_WRONG_HAL_RESPONSE);
+ assertThat(callback.response).isNull();
+ }
+
/**
* Asserts the given {@link UsersInfo} is properly represented in the {@link VehiclePropValue}.
*
@@ -455,7 +655,17 @@
assertUsersInfo(req, mUsersInfo, 2);
}
- private void assertCallbackStatus(GenericHalCallback<InitialUserInfoResponse> callback,
+ private void assertSwitchUserSetRequest(VehiclePropValue req, int messageType,
+ UserInfo targetUserInfo) {
+ assertThat(req.value.int32Values.get(1)).isEqualTo(messageType);
+ assertWithMessage("targetuser.id mismatch").that(req.value.int32Values.get(2))
+ .isEqualTo(targetUserInfo.userId);
+ assertWithMessage("targetuser.flags mismatch").that(req.value.int32Values.get(3))
+ .isEqualTo(targetUserInfo.flags);
+ assertUsersInfo(req, mUsersInfo, 4);
+ }
+
+ private void assertCallbackStatus(GenericHalCallback callback,
int expectedStatus) {
int actualStatus = callback.status;
if (actualStatus == expectedStatus) return;