Merge "Do not use "system_*" colors" into sc-v2-dev
diff --git a/car-lib/src/android/car/ICarUserService.aidl b/car-lib/src/android/car/ICarUserService.aidl
index bee3bba..80a8e3d 100644
--- a/car-lib/src/android/car/ICarUserService.aidl
+++ b/car-lib/src/android/car/ICarUserService.aidl
@@ -30,7 +30,8 @@
AndroidFuture<UserCreationResult> createDriver(@nullable String name, boolean admin);
UserInfo createPassenger(@nullable String name, int driverId);
void switchDriver(int driverId, in AndroidFuture<UserSwitchResult> receiver);
- void switchUser(int tagerUserId, int timeoutMs, in AndroidFuture<UserSwitchResult> receiver);
+ void switchUser(int targetUserId, int timeoutMs, in AndroidFuture<UserSwitchResult> receiver);
+ void logoutUser(int timeoutMs, in AndroidFuture<UserSwitchResult> receiver);
void setUserSwitchUiCallback(in IResultReceiver callback);
void createUser(@nullable String name, String userType, int flags, int timeoutMs,
in AndroidFuture<UserCreationResult> receiver);
diff --git a/car-lib/src/android/car/user/CarUserManager.java b/car-lib/src/android/car/user/CarUserManager.java
index db1b6e6..cdf5c5a 100644
--- a/car-lib/src/android/car/user/CarUserManager.java
+++ b/car-lib/src/android/car/user/CarUserManager.java
@@ -378,6 +378,40 @@
}
}
+ /**
+ * Logouts the current user (if it was switched to by a device admin).
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS})
+ public AsyncFuture<UserSwitchResult> logoutUser() {
+ int uid = myUid();
+ try {
+ AndroidFuture<UserSwitchResult> future = new AndroidFuture<UserSwitchResult>() {
+ @Override
+ protected void onCompleted(UserSwitchResult result, Throwable err) {
+ if (result != null) {
+ EventLog.writeEvent(EventLogTags.CAR_USER_MGR_LOGOUT_USER_RESP, uid,
+ result.getStatus(), result.getErrorMessage());
+ } else {
+ Log.w(TAG, "logoutUser() failed: " + err);
+ }
+ super.onCompleted(result, err);
+ }
+ };
+ EventLog.writeEvent(EventLogTags.CAR_USER_MGR_LOGOUT_USER_REQ, uid);
+ mService.logoutUser(HAL_TIMEOUT_MS, future);
+ return new AndroidAsyncFuture<>(future);
+ } catch (SecurityException e) {
+ throw e;
+ } catch (RemoteException | RuntimeException e) {
+ AsyncFuture<UserSwitchResult> future =
+ newSwitchResuiltForFailure(UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE);
+ return handleExceptionFromCarService(e, future);
+ }
+ }
+
private AndroidAsyncFuture<UserSwitchResult> newSwitchResuiltForFailure(
@UserSwitchResult.Status int status) {
AndroidFuture<UserSwitchResult> future = new AndroidFuture<>();
diff --git a/car-lib/src/android/car/user/UserSwitchResult.java b/car-lib/src/android/car/user/UserSwitchResult.java
index 695511d..e0aadbc 100644
--- a/car-lib/src/android/car/user/UserSwitchResult.java
+++ b/car-lib/src/android/car/user/UserSwitchResult.java
@@ -96,6 +96,12 @@
CommonResults.LAST_COMMON_STATUS + 4;
/**
+ * When logout was called but the current user was not switched by a device admin.
+ */
+ public static final int STATUS_NOT_LOGGED_IN =
+ CommonResults.LAST_COMMON_STATUS + 5;
+
+ /**
* Gets the user switch result status.
*
* @return either {@link UserSwitchResult#STATUS_SUCCESSFUL},
@@ -106,8 +112,9 @@
* {@link UserSwitchResult#STATUS_UX_RESTRICTION_FAILURE},
* {@link UserSwitchResult#STATUS_OK_USER_ALREADY_IN_FOREGROUND},
* {@link UserSwitchResult#STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO},
- * {@link UserSwitchResult#STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST}, or
- * {@link UserSwitchResult#STATUS_NOT_SWITCHABLE}.
+ * {@link UserSwitchResult#STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST},
+ * {@link UserSwitchResult#STATUS_NOT_SWITCHABLE}, or
+ * {@link UserSwitchResult#STATUS_NOT_LOGGED_IN}.
*/
private final @Status int mStatus;
@@ -124,8 +131,7 @@
-
- // Code below generated by codegen v1.0.18.
+ // Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -149,7 +155,8 @@
STATUS_OK_USER_ALREADY_IN_FOREGROUND,
STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO,
STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST,
- STATUS_NOT_SWITCHABLE
+ STATUS_NOT_SWITCHABLE,
+ STATUS_NOT_LOGGED_IN
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
@@ -179,6 +186,8 @@
return "STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST";
case STATUS_NOT_SWITCHABLE:
return "STATUS_NOT_SWITCHABLE";
+ case STATUS_NOT_LOGGED_IN:
+ return "STATUS_NOT_LOGGED_IN";
default: return Integer.toHexString(value);
}
}
@@ -197,8 +206,9 @@
* {@link UserSwitchResult#STATUS_UX_RESTRICTION_FAILURE},
* {@link UserSwitchResult#STATUS_OK_USER_ALREADY_IN_FOREGROUND},
* {@link UserSwitchResult#STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO},
- * {@link UserSwitchResult#STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST}, or
- * {@link UserSwitchResult#STATUS_NOT_SWITCHABLE}.
+ * {@link UserSwitchResult#STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST},
+ * {@link UserSwitchResult#STATUS_NOT_SWITCHABLE}, or
+ * {@link UserSwitchResult#STATUS_NOT_LOGGED_IN}.
* @param errorMessage
* Gets the error message, if any.
* @hide
@@ -218,7 +228,8 @@
&& !(mStatus == STATUS_OK_USER_ALREADY_IN_FOREGROUND)
&& !(mStatus == STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO)
&& !(mStatus == STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST)
- && !(mStatus == STATUS_NOT_SWITCHABLE)) {
+ && !(mStatus == STATUS_NOT_SWITCHABLE)
+ && !(mStatus == STATUS_NOT_LOGGED_IN)) {
throw new java.lang.IllegalArgumentException(
"status was " + mStatus + " but must be one of: "
+ "STATUS_SUCCESSFUL(" + STATUS_SUCCESSFUL + "), "
@@ -230,7 +241,8 @@
+ "STATUS_OK_USER_ALREADY_IN_FOREGROUND(" + STATUS_OK_USER_ALREADY_IN_FOREGROUND + "), "
+ "STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO(" + STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO + "), "
+ "STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST(" + STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST + "), "
- + "STATUS_NOT_SWITCHABLE(" + STATUS_NOT_SWITCHABLE + ")");
+ + "STATUS_NOT_SWITCHABLE(" + STATUS_NOT_SWITCHABLE + "), "
+ + "STATUS_NOT_LOGGED_IN(" + STATUS_NOT_LOGGED_IN + ")");
}
this.mErrorMessage = errorMessage;
@@ -249,8 +261,9 @@
* {@link UserSwitchResult#STATUS_UX_RESTRICTION_FAILURE},
* {@link UserSwitchResult#STATUS_OK_USER_ALREADY_IN_FOREGROUND},
* {@link UserSwitchResult#STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO},
- * {@link UserSwitchResult#STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST}, or
- * {@link UserSwitchResult#STATUS_NOT_SWITCHABLE}.
+ * {@link UserSwitchResult#STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST},
+ * {@link UserSwitchResult#STATUS_NOT_SWITCHABLE}, or
+ * {@link UserSwitchResult#STATUS_NOT_LOGGED_IN}.
*/
@DataClass.Generated.Member
public @Status int getStatus() {
@@ -316,7 +329,8 @@
&& !(mStatus == STATUS_OK_USER_ALREADY_IN_FOREGROUND)
&& !(mStatus == STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO)
&& !(mStatus == STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST)
- && !(mStatus == STATUS_NOT_SWITCHABLE)) {
+ && !(mStatus == STATUS_NOT_SWITCHABLE)
+ && !(mStatus == STATUS_NOT_LOGGED_IN)) {
throw new java.lang.IllegalArgumentException(
"status was " + mStatus + " but must be one of: "
+ "STATUS_SUCCESSFUL(" + STATUS_SUCCESSFUL + "), "
@@ -328,7 +342,8 @@
+ "STATUS_OK_USER_ALREADY_IN_FOREGROUND(" + STATUS_OK_USER_ALREADY_IN_FOREGROUND + "), "
+ "STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO(" + STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO + "), "
+ "STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST(" + STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST + "), "
- + "STATUS_NOT_SWITCHABLE(" + STATUS_NOT_SWITCHABLE + ")");
+ + "STATUS_NOT_SWITCHABLE(" + STATUS_NOT_SWITCHABLE + "), "
+ + "STATUS_NOT_LOGGED_IN(" + STATUS_NOT_LOGGED_IN + ")");
}
this.mErrorMessage = errorMessage;
@@ -351,10 +366,10 @@
};
@DataClass.Generated(
- time = 1603921276651L,
- codegenVersion = "1.0.18",
+ time = 1642629779623L,
+ codegenVersion = "1.0.23",
sourceFile = "packages/services/Car/car-lib/src/android/car/user/UserSwitchResult.java",
- inputSignatures = "public static final int STATUS_SUCCESSFUL\npublic static final int STATUS_ANDROID_FAILURE\npublic static final int STATUS_HAL_FAILURE\npublic static final int STATUS_HAL_INTERNAL_FAILURE\npublic static final int STATUS_INVALID_REQUEST\npublic static final int STATUS_OK_USER_ALREADY_IN_FOREGROUND\npublic static final int STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO\npublic static final int STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST\npublic static final int STATUS_NOT_SWITCHABLE\nprivate final @android.car.user.UserSwitchResult.Status int mStatus\nprivate final @android.annotation.Nullable java.lang.String mErrorMessage\npublic @java.lang.Override boolean isSuccess()\nclass UserSwitchResult extends java.lang.Object implements [android.os.Parcelable, android.car.user.OperationResult]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+ inputSignatures = "public static final int STATUS_SUCCESSFUL\npublic static final int STATUS_ANDROID_FAILURE\npublic static final int STATUS_HAL_FAILURE\npublic static final int STATUS_HAL_INTERNAL_FAILURE\npublic static final int STATUS_INVALID_REQUEST\npublic static final int STATUS_UX_RESTRICTION_FAILURE\npublic static final int STATUS_OK_USER_ALREADY_IN_FOREGROUND\npublic static final int STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO\npublic static final int STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST\npublic static final int STATUS_NOT_SWITCHABLE\npublic static final int STATUS_NOT_LOGGED_IN\nprivate final @android.car.user.UserSwitchResult.Status int mStatus\nprivate final @android.annotation.Nullable java.lang.String mErrorMessage\npublic @java.lang.Override boolean isSuccess()\nclass UserSwitchResult extends java.lang.Object implements [android.os.Parcelable, android.car.user.OperationResult]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
@Deprecated
private void __metadata() {}
diff --git a/car-lib/src/com/android/car/internal/common/EventLogTags.logtags b/car-lib/src/com/android/car/internal/common/EventLogTags.logtags
index ce07e2b..2fe1a08 100644
--- a/car-lib/src/com/android/car/internal/common/EventLogTags.logtags
+++ b/car-lib/src/com/android/car/internal/common/EventLogTags.logtags
@@ -95,6 +95,8 @@
150127 car_user_svc_stop_user_req (user_id|1)
150128 car_user_svc_stop_user_resp (user_id|1),(result|1)
150129 car_user_svc_initial_user_info_req_complete (request_type|1)
+150130 car_user_svc_logout_user_req (user_id|1),(timeout|1)
+150131 car_user_svc_logout_user_resp (hal_callback_status|1),(user_switch_status|1),(error_message|3)
150140 car_user_hal_initial_user_info_req (request_id|1),(request_type|1),(timeout|1)
150141 car_user_hal_initial_user_info_resp (request_id|1),(status|1),(action|1),(user_id|1),(flags|1),(safe_name|3),(user_locales|3)
@@ -126,6 +128,8 @@
150183 car_user_mgr_remove_user_resp (uid|1),(status|1)
150184 car_user_mgr_notify_lifecycle_listener (number_listeners|1),(event_type|1),(from_user_id|1),(to_user_id|1)
150185 car_user_mgr_pre_create_user_req (uid|1)
+150186 car_user_mgr_logout_user_req (uid|1)
+150187 car_user_mgr_logout_user_resp (uid|1),(status|1),(error_message|3)
#### Device policy related tags (range 150200 - 150299)
150200 car_dp_mgr_remove_user_req (uid|1),(user_id|1)
diff --git a/car-test-lib/src/android/car/test/mocks/AndroidMockitoHelper.java b/car-test-lib/src/android/car/test/mocks/AndroidMockitoHelper.java
index 29b1471..ce608ea 100644
--- a/car-test-lib/src/android/car/test/mocks/AndroidMockitoHelper.java
+++ b/car-test-lib/src/android/car/test/mocks/AndroidMockitoHelper.java
@@ -212,6 +212,15 @@
}
/**
+ * Mocks a call to {@code UserManager#mockUmHasUserRestrictionForUser(String, UserHandle)} that
+ * returns {@code value}.
+ */
+ public static void mockUmHasUserRestrictionForUser(@NonNull UserManager um,
+ @NonNull UserHandle user, @NonNull String restrictionKey, boolean value) {
+ when(um.hasUserRestrictionForUser(restrictionKey, user)).thenReturn(value);
+ }
+
+ /**
* Mocks a call to {@link ServiceManager#getService(name)}.
*
* <p><b>Note: </b>it must be made inside a
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/Android.bp b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/Android.bp
new file mode 100644
index 0000000..1511086
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+android_app {
+ name: "CarUiPortraitMediaCommonRRO",
+ resource_dirs: ["res"],
+ platform_apis: true,
+ aaptflags: [
+ "--no-resource-deduping",
+ "--no-resource-removal"
+ ],
+}
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/AndroidManifest.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/AndroidManifest.xml
new file mode 100644
index 0000000..e9dd47b
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.car.media.common.caruiportrait.rro">
+ <application android:hasCode="false"/>
+ <overlay android:priority="20"
+ android:targetName="CarMediaCommon"
+ android:targetPackage="com.android.car.media"
+ android:resourcesMap="@xml/overlays"
+ android:isStatic="true" />
+</manifest>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/values/config.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/values/config.xml
new file mode 100644
index 0000000..f83f442
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/values/config.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<resources>
+ <!-- Intent used to launch the app selector as popup.
+ This should be overlaid to match the actual launcher included in the car. -->
+ <string name="launcher_popup_intent" translatable="false">
+ </string>
+
+ <!-- Intent used to launch the app selector full screen.
+ This should be overlaid to match the actual launcher included in the car. -->
+ <string name="launcher_intent" translatable="false">
+ </string>
+
+</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/xml/overlays.xml b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/xml/overlays.xml
new file mode 100644
index 0000000..93f1f92
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitMediaCommonRRO/res/xml/overlays.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<overlay>
+ <item target="string/launcher_popup_intent" value="@string/launcher_popup_intent"/>
+ <item target="string/launcher_intent" value="@string/launcher_intent"/>
+</overlay>
diff --git a/car_product/car_ui_portrait/rro/car_ui_portrait_rro.mk b/car_product/car_ui_portrait/rro/car_ui_portrait_rro.mk
index 5b06ed2..de7a46c 100644
--- a/car_product/car_ui_portrait/rro/car_ui_portrait_rro.mk
+++ b/car_product/car_ui_portrait/rro/car_ui_portrait_rro.mk
@@ -23,6 +23,7 @@
CarUiPortraitDialerRRO \
CarUiPortraitSettingsRRO \
CarUiPortraitMediaRRO \
+ CarUiPortraitMediaCommonRRO \
CarUiPortraitLauncherRRO \
CarUiPortraitNotificationRRO \
CarUiPortraitCarServiceRRO \
diff --git a/cpp/powerpolicy/server/src/PolicyManager.cpp b/cpp/powerpolicy/server/src/PolicyManager.cpp
index 2681f51..32366c5 100644
--- a/cpp/powerpolicy/server/src/PolicyManager.cpp
+++ b/cpp/powerpolicy/server/src/PolicyManager.cpp
@@ -122,6 +122,7 @@
PowerComponent::BLUETOOTH,
PowerComponent::WIFI,
PowerComponent::LOCATION,
+ PowerComponent::MICROPHONE,
PowerComponent::CPU};
const std::unordered_set<PowerComponent> kNoUserInteractionConfigurableComponents =
{PowerComponent::BLUETOOTH, PowerComponent::NFC, PowerComponent::TRUSTED_DEVICE_DETECTION};
diff --git a/packages/CarManagedProvisioning/res/values/themes.xml b/packages/CarManagedProvisioning/res/values/themes.xml
index ba4337c..114272b 100644
--- a/packages/CarManagedProvisioning/res/values/themes.xml
+++ b/packages/CarManagedProvisioning/res/values/themes.xml
@@ -15,20 +15,7 @@
limitations under the License.
-->
<resources>
- <style name="Theme.CarManagedProvisioning" parent="Theme.Car.Dark.NoActionBar">
- <!-- Explicitly set this attribute -->
- <item name="android:textAllCaps">false</item>
- <!-- TODO(b/204577686) Button style is not customizable -->
- <item name="android:buttonStyle">@style/Button</item>
- <item name="buttonStyle">@style/Button</item>
- <item name="android:borderlessButtonStyle">@style/Button.Borderless</item>
- <item name="borderlessButtonStyle">@style/Button.Borderless</item>
-
- </style>
- <style name="Button" parent="Widget.Car.Button.DarkText">
- <item name="android:maxWidth">@dimen/primary_button_max_width</item>
- </style>
- <style name="Button.Borderless"
- parent="Widget.Car.Button.Borderless.Colored">
+ <style name="Theme.CarManagedProvisioning"
+ parent="Theme.Car.SetupWizard.NoActionBar.Accent">
</style>
</resources>
\ No newline at end of file
diff --git a/service/src/com/android/car/CarShellCommand.java b/service/src/com/android/car/CarShellCommand.java
index d70151d..c973374 100644
--- a/service/src/com/android/car/CarShellCommand.java
+++ b/service/src/com/android/car/CarShellCommand.java
@@ -156,6 +156,7 @@
private static final String COMMAND_INJECT_CUSTOM_INPUT = "inject-custom-input";
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_LOGOUT_USER = "logout-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";
@@ -229,6 +230,8 @@
CREATE_OR_MANAGE_USERS_PERMISSIONS);
USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_SWITCH_USER,
CREATE_OR_MANAGE_USERS_PERMISSIONS);
+ USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_LOGOUT_USER,
+ CREATE_OR_MANAGE_USERS_PERMISSIONS);
USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_REMOVE_USER,
CREATE_OR_MANAGE_USERS_PERMISSIONS);
USER_BUILD_COMMAND_TO_PERMISSIONS_MAP.put(COMMAND_CREATE_USER,
@@ -547,6 +550,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 response.");
+ pw.printf("\t%s [--timeout TIMEOUT_MS]\n", COMMAND_LOGOUT_USER);
+ pw.println("\t Logout the current user (if the user was switched toby a device admin).");
+ pw.println("\t The --timeout option defines how long to wait for the UserHal 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,");
@@ -936,6 +943,9 @@
case COMMAND_SWITCH_USER:
switchUser(args, writer);
break;
+ case COMMAND_LOGOUT_USER:
+ logoutUser(args, writer);
+ break;
case COMMAND_REMOVE_USER:
removeUser(args, writer);
break;
@@ -1453,6 +1463,12 @@
}
CarUserManager carUserManager = getCarUserManager(mContext);
AsyncFuture<UserSwitchResult> future = carUserManager.switchUser(targetUserId);
+
+ showUserSwitchResult(writer, future, timeout);
+ }
+
+ private void showUserSwitchResult(IndentingPrintWriter writer,
+ AsyncFuture<UserSwitchResult> future, int timeout) {
UserSwitchResult result = waitForFuture(writer, future, timeout);
if (result == null) return;
writer.printf("UserSwitchResult: status=%s",
@@ -1464,6 +1480,30 @@
writer.println();
}
+ private void logoutUser(String[] args, IndentingPrintWriter writer) {
+ int timeout = DEFAULT_HAL_TIMEOUT_MS + DEFAULT_CAR_USER_SERVICE_TIMEOUT_MS;
+
+ if (args.length > 1) {
+ for (int i = 1; i < args.length; i++) {
+ String arg = args[i];
+ switch (arg) {
+ case "--timeout":
+ timeout = Integer.parseInt(args[++i]);
+ break;
+ default:
+ writer.println("Invalid option at index " + i + ": " + arg);
+ return;
+ }
+ }
+ }
+
+ Slog.d(TAG, "logoutUser(): timeout=" + timeout);
+
+ CarUserManager carUserManager = getCarUserManager(mContext);
+ AsyncFuture<UserSwitchResult> future = carUserManager.logoutUser();
+ showUserSwitchResult(writer, future, timeout);
+ }
+
private void createUser(String[] args, IndentingPrintWriter writer) {
int timeout = DEFAULT_HAL_TIMEOUT_MS + DEFAULT_CAR_USER_SERVICE_TIMEOUT_MS;
int flags = 0;
diff --git a/service/src/com/android/car/power/PolicyReader.java b/service/src/com/android/car/power/PolicyReader.java
index afd8648..5bdd885 100644
--- a/service/src/com/android/car/power/PolicyReader.java
+++ b/service/src/com/android/car/power/PolicyReader.java
@@ -123,7 +123,7 @@
PowerComponent.TRUSTED_DEVICE_DETECTION));
private static final int[] SUSPEND_TO_RAM_DISABLED_COMPONENTS = {
PowerComponent.AUDIO, PowerComponent.BLUETOOTH, PowerComponent.WIFI,
- PowerComponent.LOCATION
+ PowerComponent.LOCATION, PowerComponent.MICROPHONE, PowerComponent.CPU
};
private static final CarPowerPolicy POWER_POLICY_ALL_ON;
private static final CarPowerPolicy POWER_POLICY_INITIAL_ON;
diff --git a/service/src/com/android/car/user/CarUserService.java b/service/src/com/android/car/user/CarUserService.java
index 54d4d07..a13c971 100644
--- a/service/src/com/android/car/user/CarUserService.java
+++ b/service/src/com/android/car/user/CarUserService.java
@@ -31,6 +31,7 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManager;
import android.car.CarOccupantZoneManager;
import android.car.CarOccupantZoneManager.OccupantTypeEnum;
import android.car.CarOccupantZoneManager.OccupantZoneInfo;
@@ -164,6 +165,7 @@
private final Context mContext;
private final IActivityManager mAm;
private final UserManager mUserManager;
+ private final DevicePolicyManager mDpm;
private final int mMaxRunningUsers;
private final InitialUserSetter mInitialUserSetter;
private final boolean mEnablePassengerSupport;
@@ -302,14 +304,14 @@
@NonNull UserManager userManager,
@NonNull IActivityManager am, int maxRunningUsers,
@NonNull CarUxRestrictionsManagerService uxRestrictionService) {
- this(context, hal, userManager, am, maxRunningUsers,
- /* initialUserSetter= */ null, /* userPreCreator= */ null, uxRestrictionService,
- null);
+ this(context, hal, userManager, context.getSystemService(DevicePolicyManager.class), am,
+ maxRunningUsers, /* initialUserSetter= */ null, /* userPreCreator= */ null,
+ uxRestrictionService, /* handler= */null);
}
@VisibleForTesting
CarUserService(@NonNull Context context, @NonNull UserHalService hal,
- @NonNull UserManager userManager,
+ @NonNull UserManager userManager, @NonNull DevicePolicyManager dpm,
@NonNull IActivityManager am, int maxRunningUsers,
@Nullable InitialUserSetter initialUserSetter,
@Nullable UserPreCreator userPreCreator,
@@ -323,6 +325,7 @@
mAm = am;
mMaxRunningUsers = maxRunningUsers;
mUserManager = userManager;
+ mDpm = dpm;
mLastPassengerId = UserHandle.USER_NULL;
mHandler = handler == null ? new Handler(mHandlerThread.getLooper()) : handler;
mInitialUserSetter =
@@ -562,13 +565,15 @@
if (UserHelperLite.isHeadlessSystemUser(driverId)) {
// System user doesn't associate with real person, can not be switched to.
Slog.w(TAG, "switching to system user in headless system user mode is not allowed");
- sendUserSwitchResult(receiver, UserSwitchResult.STATUS_INVALID_REQUEST);
+ sendUserSwitchResult(receiver, EventLogTags.CAR_USER_SVC_SWITCH_USER_RESP,
+ UserSwitchResult.STATUS_INVALID_REQUEST);
return;
}
int userSwitchable = mUserManager.getUserSwitchability();
if (userSwitchable != UserManager.SWITCHABILITY_STATUS_OK) {
Slog.w(TAG, "current process is not allowed to switch user");
- sendUserSwitchResult(receiver, UserSwitchResult.STATUS_INVALID_REQUEST);
+ sendUserSwitchResult(receiver, EventLogTags.CAR_USER_SVC_SWITCH_USER_RESP,
+ UserSwitchResult.STATUS_INVALID_REQUEST);
return;
}
switchUser(driverId, mHalTimeoutMs, receiver);
@@ -1064,14 +1069,47 @@
UserInfo targetUser = mUserManager.getUserInfo(targetUserId);
Preconditions.checkArgument(targetUser != null, "Target user doesn't exist");
if (mUserManager.getUserSwitchability() != UserManager.SWITCHABILITY_STATUS_OK) {
- sendUserSwitchResult(receiver, UserSwitchResult.STATUS_NOT_SWITCHABLE);
+ sendUserSwitchResult(receiver, EventLogTags.CAR_USER_SVC_SWITCH_USER_RESP,
+ UserSwitchResult.STATUS_NOT_SWITCHABLE);
return;
}
- mHandler.post(() -> handleSwitchUser(targetUser, timeoutMs, receiver));
+ mHandler.post(() -> handleSwitchUser(targetUser, timeoutMs, receiver,
+ /* isLogout= */ false));
+ }
+
+ @Override
+ public void logoutUser(int timeoutMs, @NonNull AndroidFuture<UserSwitchResult> receiver) {
+ checkManageOrCreateUsersPermission("logoutUser");
+ Objects.requireNonNull(receiver);
+
+ int logoutUserId = mDpm.getLogoutUserId();
+ EventLog.writeEvent(EventLogTags.CAR_USER_SVC_LOGOUT_USER_REQ, logoutUserId, timeoutMs);
+
+ if (logoutUserId == UserHandle.USER_NULL) {
+ Slog.w(TAG, "logoutUser() called when current user is not logged in");
+ sendUserSwitchResult(receiver, EventLogTags.CAR_USER_SVC_LOGOUT_USER_RESP,
+ UserSwitchResult.STATUS_NOT_LOGGED_IN);
+ return;
+ }
+
+ UserInfo targetUser = mUserManager.getUserInfo(logoutUserId);
+ if (targetUser == null) {
+ // It shouldn't happen, but according to the Law of Descartes-Murphy:
+ // "I shouldn't, therefore I will!"
+ Slog.wtf(TAG, "logout failed because user " + logoutUserId + " doesn't exist");
+ sendUserSwitchResult(receiver, EventLogTags.CAR_USER_SVC_LOGOUT_USER_RESP,
+ UserSwitchResult.STATUS_ANDROID_FAILURE);
+ return;
+ }
+
+ mHandler.post(() -> handleSwitchUser(targetUser, timeoutMs, receiver,
+ /* isLogout= */ true));
}
private void handleSwitchUser(@NonNull UserInfo targetUser, int timeoutMs,
- @NonNull AndroidFuture<UserSwitchResult> receiver) {
+ @NonNull AndroidFuture<UserSwitchResult> receiver, boolean isLogout) {
+ int eventLogTag = isLogout ? EventLogTags.CAR_USER_SVC_LOGOUT_USER_RESP
+ : EventLogTags.CAR_USER_SVC_SWITCH_USER_RESP;
int currentUser = ActivityManager.getCurrentUser();
int targetUserId = targetUser.id;
if (currentUser == targetUserId) {
@@ -1079,12 +1117,13 @@
Slog.d(TAG, "Current user is same as requested target user: " + targetUserId);
}
int resultStatus = UserSwitchResult.STATUS_OK_USER_ALREADY_IN_FOREGROUND;
- sendUserSwitchResult(receiver, resultStatus);
+ sendUserSwitchResult(receiver, eventLogTag, resultStatus);
return;
}
if (isUxRestricted()) {
- sendUserSwitchResult(receiver, UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE);
+ sendUserSwitchResult(receiver, eventLogTag,
+ UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE);
return;
}
@@ -1092,20 +1131,23 @@
if (!isUserHalSupported()) {
try {
if (mAm.switchUser(targetUserId)) {
- sendUserSwitchResult(receiver, UserSwitchResult.STATUS_SUCCESSFUL);
+ sendUserSwitchResult(receiver, eventLogTag, UserSwitchResult.STATUS_SUCCESSFUL);
+ clearLogoutUserIfNeeded(isLogout);
return;
}
} catch (RemoteException e) {
// ignore
Slog.w(TAG, "error while switching user " + targetUser.toFullString(), e);
}
- sendUserSwitchResult(receiver, UserSwitchResult.STATUS_ANDROID_FAILURE);
+ Slog.w(TAG, "failed to switch to user " + targetUser.toFullString() + " using AM");
+ sendUserSwitchResult(receiver, eventLogTag, UserSwitchResult.STATUS_ANDROID_FAILURE);
return;
}
synchronized (mLockUser) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Slog.d(TAG, "switchUser(" + targetUserId + "): currentuser=" + currentUser
+ Slog.d(TAG, "handleSwitchUser(" + targetUserId + "): currentuser=" + currentUser
+ + ", isLogout=" + isLogout
+ ", mUserIdForUserSwitchInProcess=" + mUserIdForUserSwitchInProcess);
}
@@ -1122,7 +1164,7 @@
}
int resultStatus = UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO;
- sendUserSwitchResult(receiver, resultStatus);
+ sendUserSwitchResult(receiver, eventLogTag, resultStatus);
return;
} else {
mUserIdForUserSwitchInProcess = targetUserId;
@@ -1133,6 +1175,9 @@
UsersInfo usersInfo = UserHalHelper.newUsersInfo(mUserManager);
SwitchUserRequest request = createUserSwitchRequest(targetUserId, usersInfo);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Slog.d(TAG, "calling mHal.switchUser(" + request + ")");
+ }
mHal.switchUser(request, timeoutMs, (halCallbackStatus, resp) -> {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Slog.d(TAG, "switch response: status="
@@ -1147,7 +1192,7 @@
Slog.w(TAG, "invalid callback status ("
+ UserHalHelper.halCallbackStatusToString(halCallbackStatus)
+ ") for response " + resp);
- sendUserSwitchResult(receiver, resultStatus);
+ sendUserSwitchResult(receiver, eventLogTag, resultStatus);
mUserIdForUserSwitchInProcess = UserHandle.USER_NULL;
return;
}
@@ -1162,7 +1207,7 @@
}
resultStatus =
UserSwitchResult.STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST;
- sendUserSwitchResult(receiver, resultStatus);
+ sendUserSwitchResult(receiver, eventLogTag, resultStatus);
mUserIdForUserSwitchInProcess = UserHandle.USER_NULL;
return;
}
@@ -1175,6 +1220,7 @@
if (switched) {
sendUserSwitchUiCallback(targetUserId);
resultStatus = UserSwitchResult.STATUS_SUCCESSFUL;
+ clearLogoutUserIfNeeded(isLogout);
mRequestIdForUserSwitchInProcess = resp.requestId;
} else {
resultStatus = UserSwitchResult.STATUS_ANDROID_FAILURE;
@@ -1199,10 +1245,20 @@
mUserIdForUserSwitchInProcess = UserHandle.USER_NULL;
}
}
- sendUserSwitchResult(receiver, halCallbackStatus, resultStatus, resp.errorMessage);
+ sendUserSwitchResult(receiver, eventLogTag, halCallbackStatus, resultStatus,
+ resp.errorMessage);
});
}
+ private void clearLogoutUserIfNeeded(boolean isLogout) {
+ if (!isLogout) return;
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Slog.d(TAG, "Calling DPM to clear logout user");
+ }
+ mDpm.clearLogoutUser();
+ }
+
@Override
public void removeUser(@UserIdInt int userId, AndroidFuture<UserRemovalResult> receiver) {
removeUser(userId, /* hasCallerRestrictions= */ false, receiver);
@@ -1404,15 +1460,23 @@
EventLog.writeEvent(EventLogTags.CAR_USER_SVC_CREATE_USER_REQ,
UserHelperLite.safeName(name), userType, flags, timeoutMs,
hasCallerRestrictions ? 1 : 0);
- mHandler.post(() -> handleCreateUser(name, userType, flags, timeoutMs, receiver,
- hasCallerRestrictions));
+ UserHandle callingUser = Binder.getCallingUserHandle();
+ if (mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_ADD_USER, callingUser)) {
+ Slogf.w(TAG, "Cannot create user because calling user %s has the '%s' restriction",
+ callingUser, UserManager.DISALLOW_ADD_USER);
+ sendUserCreationResultFailure(receiver, UserCreationResult.STATUS_ANDROID_FAILURE);
+ return;
+ }
+
+ mHandler.post(() -> handleCreateUser(name, userType, flags, timeoutMs, receiver,
+ callingUser.getIdentifier(), hasCallerRestrictions));
}
private void handleCreateUser(@Nullable String name, @NonNull String userType,
@UserInfoFlag int flags, int timeoutMs,
@NonNull AndroidFuture<UserCreationResult> receiver,
- boolean hasCallerRestrictions) {
+ @UserIdInt int callingUserId, boolean hasCallerRestrictions) {
if (hasCallerRestrictions) {
// Restrictions:
// - type/flag can only be normal user, admin, or guest
@@ -1441,7 +1505,6 @@
}
- int callingUserId = Binder.getCallingUserHandle().getIdentifier();
UserInfo callingUser = mUserManager.getUserInfo(callingUserId);
if (!callingUser.isAdmin() && (flags & UserInfo.FLAG_ADMIN) == UserInfo.FLAG_ADMIN) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -1688,20 +1751,18 @@
}
private void sendUserSwitchResult(@NonNull AndroidFuture<UserSwitchResult> receiver,
- @UserSwitchResult.Status int userSwitchStatus) {
- sendUserSwitchResult(receiver, HalCallback.STATUS_INVALID, userSwitchStatus,
+ int eventLogTag, @UserSwitchResult.Status int userSwitchStatus) {
+ sendUserSwitchResult(receiver, HalCallback.STATUS_INVALID, eventLogTag, userSwitchStatus,
/* errorMessage= */ null);
}
private void sendUserSwitchResult(@NonNull AndroidFuture<UserSwitchResult> receiver,
- @HalCallback.HalCallbackStatus int halCallbackStatus,
+ int eventLogTag, @HalCallback.HalCallbackStatus int halCallbackStatus,
@UserSwitchResult.Status int userSwitchStatus, @Nullable String errorMessage) {
if (errorMessage != null) {
- EventLog.writeEvent(EventLogTags.CAR_USER_SVC_SWITCH_USER_RESP, halCallbackStatus,
- userSwitchStatus, errorMessage);
+ EventLog.writeEvent(eventLogTag, halCallbackStatus, userSwitchStatus, errorMessage);
} else {
- EventLog.writeEvent(EventLogTags.CAR_USER_SVC_SWITCH_USER_RESP, halCallbackStatus,
- userSwitchStatus);
+ EventLog.writeEvent(eventLogTag, halCallbackStatus, userSwitchStatus);
}
receiver.complete(new UserSwitchResult(userSwitchStatus, errorMessage));
}
@@ -2245,8 +2306,7 @@
TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
t.traceBegin("onUserSwitching-" + toUserId);
- // Switch HAL users if user switch is not requested by CarUserService
- notifyHalLegacySwitch(fromUserId, toUserId);
+ notifyLegacyUserSwitch(fromUserId, toUserId);
mInitialUserSetter.setLastActiveUser(toUserId);
@@ -2260,17 +2320,24 @@
t.traceEnd();
}
- private void notifyHalLegacySwitch(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
+ private void notifyLegacyUserSwitch(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
synchronized (mLockUser) {
if (mUserIdForUserSwitchInProcess != UserHandle.USER_NULL) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Slog.d(TAG, "notifyHalLegacySwitch(" + fromUserId + ", " + toUserId
+ Slogf.d(TAG, "notifyLegacyUserSwitch(" + fromUserId + ", " + toUserId
+ "): not needed, normal switch for " + mUserIdForUserSwitchInProcess);
}
return;
}
}
+ sendUserSwitchUiCallback(toUserId);
+
+ // Switch HAL users if user switch is not requested by CarUserService
+ notifyHalLegacySwitch(fromUserId, toUserId);
+ }
+
+ private void notifyHalLegacySwitch(@UserIdInt int fromUserId, @UserIdInt int toUserId) {
if (!isUserHalSupported()) return;
// switch HAL user
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/device_policy.xml b/tests/EmbeddedKitchenSinkApp/res/layout/device_policy.xml
index fdb29fa..6c6600d 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/device_policy.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/device_policy.xml
@@ -13,182 +13,178 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical" >
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:orientation="vertical">
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
<!-- Current user info -->
- <LinearLayout
- android:layout_width="match_parent"
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Current User"/>
+ <com.google.android.car.kitchensink.users.UserInfoView
+ android:id="@+id/current_user"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"/>
+
+ <!-- Existing users... -->
+ <com.google.android.car.kitchensink.users.ExistingUsersView
+ android:layout_marginTop="80dp"
+ android:id="@+id/current_users"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <!-- ...and actions on them -->
+ <LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Current User"/>
- <com.google.android.car.kitchensink.users.UserInfoView
- android:id="@+id/current_user"
- android:layout_width="wrap_content" android:layout_height="wrap_content"/>
-
- <!-- Existing users... -->
- <com.google.android.car.kitchensink.users.ExistingUsersView
- android:layout_marginTop="80dp"
- android:id="@+id/current_users"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <!-- ...and actions on them -->
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <Button
- android:id="@+id/remove_user"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Remove"/>
- <Button
- android:id="@+id/start_user_in_background"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Start In Background"/>
- <Button
- android:id="@+id/stop_user"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Stop"/>
- </LinearLayout>
-
- <!-- New user section -->
- <TextView
- android:layout_marginTop="80dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="New User"/>
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Name: "/>
- <EditText
- android:id="@+id/new_user_name"
- android:layout_width="150dp"
- android:layout_height="wrap_content"
- android:maxLength="25"
- android:text=""/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Is admin? "/>
- <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:id="@+id/new_user_is_admin"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Is guest? "/>
- <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:id="@+id/new_user_is_guest"/>
- </LinearLayout>
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <Button
- android:id="@+id/create_user"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Create"/>
- </LinearLayout>
-
- <!-- Non user-related actions -->
- <TextView
- android:layout_marginTop="80dp"
+ android:orientation="horizontal" >
+ <Button
+ android:id="@+id/remove_user"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="Other actions"/>
- <LinearLayout android:layout_width="match_parent"
+ android:text="Remove"/>
+ <Button
+ android:id="@+id/start_user_in_background"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Password"/>
- <EditText
- android:id="@+id/password"
- android:layout_width="150dp"
- android:layout_height="wrap_content"
- android:maxLength="10"
- android:text=""/>
- <Button
- android:id="@+id/reset_password"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Reset"/>
- <Button
- android:id="@+id/lock_now"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Lock Now"/>
- </LinearLayout>
-
- <LinearLayout android:layout_width="match_parent"
+ android:text="Start In Background"/>
+ <Button
+ android:id="@+id/stop_user"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Wipe data"/>
- <EditText
- android:id="@+id/wipe_data_flags"
- android:layout_width="150dp"
- android:layout_height="wrap_content"
- android:maxLength="10"
- android:text=""/>
- <Button
- android:id="@+id/wipe_data"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Do it"/>
- </LinearLayout>
-
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Lock tasks"/>
- <Button
- android:id="@+id/check_lock_tasks"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Check"/>
- <Button
- android:id="@+id/start_lock_tasks"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Start"/>
- <Button
- android:id="@+id/stop_lock_tasks"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Stop"/>
- </LinearLayout>
-
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Add admin"/>
- <Spinner
- android:id="@+id/device_admin_apps"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- <Button
- android:id="@+id/set_device_admin_app"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Set"/>
- </LinearLayout>
+ android:text="Stop"/>
</LinearLayout>
- </ScrollView>
-</LinearLayout>
+
+ <!-- New user section -->
+ <TextView
+ android:layout_marginTop="80dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="New User"/>
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Name: "/>
+ <EditText
+ android:id="@+id/new_user_name"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content"
+ android:maxLength="25"
+ android:text=""/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Is admin? "/>
+ <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:id="@+id/new_user_is_admin"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Is guest? "/>
+ <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:id="@+id/new_user_is_guest"/>
+ </LinearLayout>
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <Button
+ android:id="@+id/create_user"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Create"/>
+ </LinearLayout>
+
+ <!-- Non user-related actions -->
+ <TextView
+ android:layout_marginTop="80dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Other actions"/>
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Password"/>
+ <EditText
+ android:id="@+id/password"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content"
+ android:maxLength="10"
+ android:text=""/>
+ <Button
+ android:id="@+id/reset_password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Reset"/>
+ <Button
+ android:id="@+id/lock_now"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Lock Now"/>
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Wipe data"/>
+ <EditText
+ android:id="@+id/wipe_data_flags"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content"
+ android:maxLength="10"
+ android:text=""/>
+ <Button
+ android:id="@+id/wipe_data"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Do it"/>
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Lock tasks"/>
+ <Button
+ android:id="@+id/check_lock_tasks"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Check"/>
+ <Button
+ android:id="@+id/start_lock_tasks"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Start"/>
+ <Button
+ android:id="@+id/stop_lock_tasks"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Stop"/>
+ </LinearLayout>
+
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Add admin"/>
+ <Spinner
+ android:id="@+id/device_admin_apps"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <Button
+ android:id="@+id/set_device_admin_app"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Set"/>
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/user.xml b/tests/EmbeddedKitchenSinkApp/res/layout/user.xml
index 3707bc4..2a97de7 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/user.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/user.xml
@@ -13,112 +13,123 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
-
- <!-- Current user info -->
+<!--
<ScrollView
android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Current User"/>
- <com.google.android.car.kitchensink.users.UserInfoView
- android:id="@+id/current_user"
- android:layout_width="wrap_content" android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content">
+-->
- <!-- Actions on current user -->
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Is admin? "/>
- <CheckBox
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/is_admin"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Associate Key Fob?"/>
- <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:id="@+id/is_associated_key_fob"/>
- </LinearLayout>
-
- <!-- Existing users... -->
- <com.google.android.car.kitchensink.users.ExistingUsersView
- android:layout_marginTop="80dp"
- android:id="@+id/current_users"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <!-- ...and actions on them -->
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <Button
- android:id="@+id/switch_user"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Switch"/>
- <Button
- android:id="@+id/remove_user"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Remove"/>
- <Button
- android:id="@+id/lock_user_data"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Lock Data"/>
- </LinearLayout>
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
- <!-- New user section -->
- <TextView
- android:layout_marginTop="80dp"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="New User"/>
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
+ <!-- Current user info -->
<TextView
android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Name: "/>
- <EditText
- android:id="@+id/new_user_name"
- android:layout_width="150dp"
android:layout_height="wrap_content"
- android:maxLength="25"
- android:text=""/>
- <TextView
+ android:text="Current User "/>
+ <com.google.android.car.kitchensink.users.UserInfoView
+ android:id="@+id/current_user"
+ android:layout_width="wrap_content" android:layout_height="wrap_content"/>
+
+ <!-- Actions on current user -->
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Is admin? "/>
+ <CheckBox
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/is_admin"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Associate Key Fob?"/>
+ <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:id="@+id/is_associated_key_fob"/>
+ </LinearLayout>
+
+ <!-- Existing users... -->
+ <com.google.android.car.kitchensink.users.ExistingUsersView
+ android:layout_marginTop="80dp"
+ android:id="@+id/current_users"
android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Is admin? "/>
- <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:id="@+id/new_user_is_admin"/>
+ android:layout_height="wrap_content" />
+
+ <!-- ...and actions on them -->
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <Button
+ android:id="@+id/switch_user"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Switch"/>
+ <Button
+ android:id="@+id/remove_user"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Remove"/>
+ <Button
+ android:id="@+id/lock_user_data"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Lock Data"/>
+ </LinearLayout>
+
+ <!-- New user section -->
<TextView
+ android:layout_marginTop="80dp"
android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Is guest? "/>
- <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:id="@+id/new_user_is_guest"/>
+ android:layout_height="wrap_content" android:text="New User"/>
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Name: "/>
+ <EditText
+ android:id="@+id/new_user_name"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content"
+ android:maxLength="25"
+ android:text=""/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Is admin? "/>
+ <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:id="@+id/new_user_is_admin"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Is guest? "/>
+ <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:id="@+id/new_user_is_guest"/>
+ </LinearLayout>
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Extra flags: "/>
+ <EditText
+ android:id="@+id/new_user_flags"
+ android:layout_width="30dp"
+ android:layout_height="wrap_content"
+ android:maxLength="3"
+ android:inputType="numberDecimal"
+ android:text=""/>
+ <Button
+ android:id="@+id/create_user"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" android:text="Create"/>
+ </LinearLayout>
+
</LinearLayout>
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Extra flags: "/>
- <EditText
- android:id="@+id/new_user_flags"
- android:layout_width="30dp"
- android:layout_height="wrap_content"
- android:maxLength="3"
- android:inputType="numberDecimal"
- android:text=""/>
- <Button
- android:id="@+id/create_user"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:text="Create"/>
- </LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/user_restrictions.xml b/tests/EmbeddedKitchenSinkApp/res/layout/user_restrictions.xml
index 3c57e71..210fb86 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/user_restrictions.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/user_restrictions.xml
@@ -17,12 +17,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
- <ListView
- android:id="@+id/user_restrictions_list"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:scrollbars="vertical"/>
-
<Button
android:id="@+id/apply_button"
android:layout_width="wrap_content"
@@ -30,4 +24,9 @@
android:padding="@dimen/users_button_padding"
android:textSize="@dimen/users_button_text_size"
android:text="@string/users_apply_button" />
+ <ListView
+ android:id="@+id/user_restrictions_list"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:scrollbars="vertical"/>
</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionsFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionsFragment.java
index 1ce98d0..4e8a3e0 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionsFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/users/UserRestrictionsFragment.java
@@ -31,6 +31,7 @@
import com.google.android.car.kitchensink.R;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -83,17 +84,27 @@
UserManager userManager = getUserManager();
// Iterate through all of the user restrictions and set their values
+ List<String> restrictions = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
UserRestrictionListItem item = (UserRestrictionListItem) adapter.getItem(i);
- userManager.setUserRestriction(item.getKey(), item.getIsChecked());
+ String restriction = item.getKey();
+ boolean added = item.getIsChecked();
+ userManager.setUserRestriction(restriction, added);
+ if (added) {
+ restrictions.add(restriction);
+ }
}
-
- Toast.makeText(
- getContext(), "User restrictions have been set!", Toast.LENGTH_SHORT)
- .show();
+ toast("%d restrictions (%s) have been set on user %d!", restrictions.size(),
+ restrictions, getContext().getUserId());
});
}
+ private void toast(String format, Object...args) {
+ String msg = String.format(format, args);
+ Log.i(TAG, msg);
+ Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
+ }
+
private List<UserRestrictionListItem> createUserRestrictionItems() {
int userId = getContext().getUserId();
UserHandle user = UserHandle.of(userId);
diff --git a/tests/carservice_unit_test/src/android/car/test/mocks/AndroidMockitoHelperTest.java b/tests/carservice_unit_test/src/android/car/test/mocks/AndroidMockitoHelperTest.java
index b495220..c824df7 100644
--- a/tests/carservice_unit_test/src/android/car/test/mocks/AndroidMockitoHelperTest.java
+++ b/tests/carservice_unit_test/src/android/car/test/mocks/AndroidMockitoHelperTest.java
@@ -26,6 +26,7 @@
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetSystemUser;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserHandles;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserInfo;
+import static android.car.test.mocks.AndroidMockitoHelper.mockUmHasUserRestrictionForUser;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmIsHeadlessSystemUserMode;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmIsUserRunning;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmRemoveUserOrSetEphemeral;
@@ -62,6 +63,8 @@
private static final int TEST_USER_ID = 100;
+ private final UserHandle mTestUserHandle = UserHandle.of(TEST_USER_ID);
+
private UserInfo mTestUser;
private StaticMockitoSession mMockSession;
@@ -208,6 +211,16 @@
assertThat(visitor.mVisited).isEqualTo(mTestUser);
}
+ @Test
+ public void testMockUmHasUserRestrictionForUser() {
+ VisitorImpl<UserInfo> visitor = new VisitorImpl<>();
+ mockUmHasUserRestrictionForUser(mMockedUserManager, mTestUserHandle, "no_Homers_club",
+ /* value= */ true);
+
+ assertThat(mMockedUserManager.hasUserRestrictionForUser("no_Homers_club", mTestUserHandle))
+ .isTrue();
+ }
+
private static final class VisitorImpl<T> implements Visitor<T> {
T mVisited;
diff --git a/tests/carservice_unit_test/src/com/android/car/power/PolicyReaderUnitTest.java b/tests/carservice_unit_test/src/com/android/car/power/PolicyReaderUnitTest.java
index b9501be..c850e2f 100644
--- a/tests/carservice_unit_test/src/com/android/car/power/PolicyReaderUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/power/PolicyReaderUnitTest.java
@@ -66,6 +66,7 @@
private static final String POLICY_GROUP_ID_MIXED = "mixed_policy_group";
private static final String NO_USER_INTERACTION_POLICY_ID =
"system_power_policy_no_user_interaction";
+ private static final String SUSPEND_TO_RAM_POLICY_ID = "system_power_policy_suspend_to_ram";
private static final CarPowerPolicy POLICY_OTHER_OFF = new CarPowerPolicy(POLICY_ID_OTHER_OFF,
new int[]{WIFI},
@@ -94,6 +95,10 @@
new int[]{BLUETOOTH, WIFI, CELLULAR, ETHERNET, NFC, CPU},
new int[]{AUDIO, MEDIA, DISPLAY, PROJECTION, INPUT, VOICE_INTERACTION,
VISUAL_INTERACTION, TRUSTED_DEVICE_DETECTION, LOCATION, MICROPHONE});
+ private static final CarPowerPolicy SYSTEM_POWER_POLICY_SUSPEND_TO_RAM =
+ new CarPowerPolicy(SUSPEND_TO_RAM_POLICY_ID,
+ new int[]{},
+ new int[]{AUDIO, BLUETOOTH, WIFI, LOCATION, MICROPHONE, CPU});
private final Resources mResources =
InstrumentationRegistry.getInstrumentation().getTargetContext().getResources();
@@ -107,7 +112,13 @@
@Test
public void testSystemPowerPolicyNoUserInteraction() throws Exception {
- assertSystemPowerPolicy(SYSTEM_POWER_POLICY_NO_USER_INTERACTION);
+ assertSystemPowerPolicy(NO_USER_INTERACTION_POLICY_ID,
+ SYSTEM_POWER_POLICY_NO_USER_INTERACTION);
+ }
+
+ @Test
+ public void testSystemPowerPolicySuspendToRam() throws Exception {
+ assertSystemPowerPolicy(SUSPEND_TO_RAM_POLICY_ID, SYSTEM_POWER_POLICY_SUSPEND_TO_RAM);
}
@Test
@@ -115,8 +126,7 @@
readPowerPolicyXml(R.raw.valid_power_policy);
assertValidPolicyPart();
- assertValidPolicyPart();
- assertSystemPowerPolicy(SYSTEM_POWER_POLICY_MODIFIED);
+ assertSystemPowerPolicy(NO_USER_INTERACTION_POLICY_ID, SYSTEM_POWER_POLICY_MODIFIED);
}
@Test
@@ -125,7 +135,7 @@
assertValidPolicyPart();
assertNoPolicyGroupPart();
- assertSystemPowerPolicy(SYSTEM_POWER_POLICY_MODIFIED);
+ assertSystemPowerPolicy(NO_USER_INTERACTION_POLICY_ID, SYSTEM_POWER_POLICY_MODIFIED);
}
@Test
@@ -134,7 +144,8 @@
assertValidPolicyPart();
assertValidPolicyGroupPart();
- assertSystemPowerPolicy(SYSTEM_POWER_POLICY_NO_USER_INTERACTION);
+ assertSystemPowerPolicy(NO_USER_INTERACTION_POLICY_ID,
+ SYSTEM_POWER_POLICY_NO_USER_INTERACTION);
}
@Test
@@ -143,7 +154,8 @@
assertValidPolicyPart();
assertNoPolicyGroupPart();
- assertSystemPowerPolicy(SYSTEM_POWER_POLICY_NO_USER_INTERACTION);
+ assertSystemPowerPolicy(NO_USER_INTERACTION_POLICY_ID,
+ SYSTEM_POWER_POLICY_NO_USER_INTERACTION);
}
@Test
@@ -152,7 +164,7 @@
assertNoPolicyPart();
assertNoPolicyGroupPart();
- assertSystemPowerPolicy(SYSTEM_POWER_POLICY_MODIFIED);
+ assertSystemPowerPolicy(NO_USER_INTERACTION_POLICY_ID, SYSTEM_POWER_POLICY_MODIFIED);
}
@Test
@@ -241,9 +253,9 @@
VehicleApPowerStateReport.ON)).isNull();
}
- private void assertSystemPowerPolicy(CarPowerPolicy expectedSystemPolicy) throws Exception {
- CarPowerPolicy systemPolicy = mPolicyReader.getPreemptivePowerPolicy(
- NO_USER_INTERACTION_POLICY_ID);
+ private void assertSystemPowerPolicy(String policyId, CarPowerPolicy expectedSystemPolicy)
+ throws Exception {
+ CarPowerPolicy systemPolicy = mPolicyReader.getPreemptivePowerPolicy(policyId);
assertThat(systemPolicy).isNotNull();
assertPolicyIdentical(systemPolicy, expectedSystemPolicy);
}
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 92182fd..14c3ddc 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
@@ -211,14 +211,14 @@
@Test
public void testSwitchUser_success() throws Exception {
- expectServiceSwitchUserSucceeds(11, UserSwitchResult.STATUS_SUCCESSFUL, "D'OH!");
+ expectServiceSwitchUserSucceeds(11, UserSwitchResult.STATUS_SUCCESSFUL);
AsyncFuture<UserSwitchResult> future = mMgr.switchUser(11);
assertThat(future).isNotNull();
UserSwitchResult result = getResult(future);
assertThat(result.getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
- assertThat(result.getErrorMessage()).isEqualTo("D'OH!");
+ assertThat(result.getErrorMessage()).isNull();
}
@Test
@@ -247,6 +247,43 @@
}
@Test
+ public void testLogoutUser_success() throws Exception {
+ expectServiceLogoutUserSucceeds(UserSwitchResult.STATUS_SUCCESSFUL);
+
+ AsyncFuture<UserSwitchResult> future = mMgr.logoutUser();
+
+ assertThat(future).isNotNull();
+ UserSwitchResult result = getResult(future);
+ assertThat(result.getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+ assertThat(result.getErrorMessage()).isNull();
+ }
+
+ @Test
+ public void testLogoutUser_remoteException() throws Exception {
+ expectServiceLogoutUserFails(new RemoteException("D'OH!"));
+ mockHandleRemoteExceptionFromCarServiceWithDefaultValue(mCar);
+
+ AsyncFuture<UserSwitchResult> future = mMgr.logoutUser();
+
+ assertThat(future).isNotNull();
+ UserSwitchResult result = getResult(future);
+ assertThat(result.getStatus()).isEqualTo(UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE);
+ assertThat(result.getErrorMessage()).isNull();
+ }
+
+ @Test
+ public void testLogoutUser_runtimeException() throws Exception {
+ expectServiceLogoutUserFails(new RuntimeException("D'OH!"));
+
+ AsyncFuture<UserSwitchResult> future = mMgr.logoutUser();
+
+ assertThat(future).isNotNull();
+ UserSwitchResult result = getResult(future);
+ assertThat(result.getStatus()).isEqualTo(UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE);
+ assertThat(result.getErrorMessage()).isNull();
+ }
+
+ @Test
public void testRemoveUser_success() throws Exception {
expectServiceRemoveUserSucceeds(100);
@@ -589,13 +626,12 @@
}
private void expectServiceSwitchUserSucceeds(@UserIdInt int userId,
- @UserSwitchResult.Status int status, @Nullable String errorMessage)
- throws RemoteException {
+ @UserSwitchResult.Status int status) throws RemoteException {
doAnswer((invocation) -> {
@SuppressWarnings("unchecked")
AndroidFuture<UserSwitchResult> future =
(AndroidFuture<UserSwitchResult>) invocation.getArguments()[2];
- future.complete(new UserSwitchResult(status, errorMessage));
+ future.complete(new UserSwitchResult(status, /* errorMessage= */ null));
return null;
}).when(mService).switchUser(eq(userId), anyInt(), notNull());
}
@@ -604,6 +640,21 @@
doThrow(e).when(mService).switchUser(eq(userId), anyInt(), notNull());
}
+ private void expectServiceLogoutUserSucceeds(@UserSwitchResult.Status int status)
+ throws RemoteException {
+ doAnswer((invocation) -> {
+ @SuppressWarnings("unchecked")
+ AndroidFuture<UserSwitchResult> future =
+ (AndroidFuture<UserSwitchResult>) invocation.getArguments()[1];
+ future.complete(new UserSwitchResult(status, /* errorMessage= */ null));
+ return null;
+ }).when(mService).logoutUser(anyInt(), notNull());
+ }
+
+ private void expectServiceLogoutUserFails(Exception e) throws Exception {
+ doThrow(e).when(mService).logoutUser(anyInt(), notNull());
+ }
+
private void expectServiceRemoveUserSucceeds(@UserIdInt int userId) throws RemoteException {
doAnswer((invocation) -> {
@SuppressWarnings("unchecked")
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 32e8b8d..571b604 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
@@ -19,6 +19,7 @@
import static android.car.test.mocks.AndroidMockitoHelper.mockUmCreateUser;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserInfo;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUsers;
+import static android.car.test.mocks.AndroidMockitoHelper.mockUmHasUserRestrictionForUser;
import static android.car.test.mocks.AndroidMockitoHelper.mockUmRemoveUserOrSetEphemeral;
import static android.car.test.mocks.JavaMockitoHelper.getResult;
import static android.car.test.util.UserTestingHelper.UserInfoBuilder;
@@ -57,12 +58,14 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.IActivityManager;
+import android.app.admin.DevicePolicyManager;
import android.car.CarOccupantZoneManager.OccupantTypeEnum;
import android.car.CarOccupantZoneManager.OccupantZoneInfo;
import android.car.drivingstate.CarUxRestrictions;
import android.car.drivingstate.ICarUxRestrictionsChangeListener;
import android.car.settings.CarSettings;
import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.car.test.mocks.AbstractExtendedMockitoTestCase.ExpectWtf;
import android.car.test.mocks.BlockingAnswer;
import android.car.test.util.BlockingResultReceiver;
import android.car.testapi.BlockingUserLifecycleListener;
@@ -105,11 +108,14 @@
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.RemoveResult;
+import android.os.UserManager.UserSwitchabilityResult;
import android.sysprop.CarProperties;
+import android.util.DebugUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -171,6 +177,7 @@
@Mock private UserHalService mUserHal;
@Mock private IActivityManager mMockedIActivityManager;
@Mock private UserManager mMockedUserManager;
+ @Mock private DevicePolicyManager mMockedDevicePolicyManager;
@Mock private Resources mMockedResources;
@Mock private Drawable mMockedDrawable;
@Mock private InitialUserSetter mInitialUserSetter;
@@ -222,6 +229,9 @@
getClass().getSimpleName());
private final Handler mHandler = new Handler(mHandlerThread.getLooper());
+ private final @UserIdInt int mAdminUserId = mAdminUser.id;
+ private final @UserIdInt int mGuestUserId = mGuestUser.id;
+
@Override
protected void onSessionBuilder(CustomMockitoSessionBuilder builder) {
builder
@@ -787,6 +797,8 @@
mCarUserService.switchDriver(mRegularUser.id, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus())
.isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+
+ assertLogoutUserNotCleared();
}
@Test
@@ -801,15 +813,16 @@
.isEqualTo(UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE);
verifyNoUserSwitch();
assertNoHalUserSwitch();
+ assertLogoutUserNotCleared();
}
@Test
public void testSwitchDriver_IfUserSwitchIsNotAllowed() throws Exception {
- when(mMockedUserManager.getUserSwitchability())
- .thenReturn(UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED);
+ mockUmGetUserSwitchability(UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED);
mCarUserService.switchDriver(mRegularUser.id, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus())
.isEqualTo(UserSwitchResult.STATUS_INVALID_REQUEST);
+ assertLogoutUserNotCleared();
}
@Test
@@ -820,6 +833,7 @@
mCarUserService.switchDriver(mAdminUser.id, mUserSwitchFuture);
assertThat(getUserSwitchResult().getStatus())
.isEqualTo(UserSwitchResult.STATUS_OK_USER_ALREADY_IN_FOREGROUND);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1144,13 +1158,12 @@
public void testSwitchUser_noUserSwitchability() throws Exception {
UserInfo currentUser = mAdminUser;
mockExistingUsersAndCurrentUser(mExistingUsers, currentUser);
- doReturn(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED).when(mMockedUserManager)
- .getUserSwitchability();
+ mockUmGetUserSwitchability(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED);
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
- assertThat(getUserSwitchResult().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_NOT_SWITCHABLE);
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_NOT_SWITCHABLE);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1159,10 +1172,10 @@
switchUser(mAdminUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
- assertThat(getUserSwitchResult().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_OK_USER_ALREADY_IN_FOREGROUND);
-
+ assertUserSwitchResult(getUserSwitchResult(),
+ UserSwitchResult.STATUS_OK_USER_ALREADY_IN_FOREGROUND);
verifyNoUserSwitch();
+ assertLogoutUserNotCleared();
}
@Test
@@ -1173,15 +1186,14 @@
switchUser(mRegularUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
- assertThat(getUserSwitchResult().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_SUCCESSFUL);
verify(mUserHal, never()).switchUser(any(), anyInt(), any());
-
// update current user due to successful user switch
mockCurrentUser(mRegularUser);
sendUserUnlockedEvent(mRegularUser.id);
assertNoHalUserSwitch();
assertNoPostSwitch();
+ assertLogoutUserNotCleared();
}
@Test
@@ -1191,9 +1203,9 @@
switchUser(mRegularUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
- assertThat(getUserSwitchResult().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_ANDROID_FAILURE);
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_ANDROID_FAILURE);
assertNoHalUserSwitch();
+ assertLogoutUserNotCleared();
}
@Test
@@ -1207,12 +1219,12 @@
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
- assertThat(getUserSwitchResult().getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
-
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_SUCCESSFUL);
// update current user due to successful user switch
mockCurrentUser(mGuestUser);
sendUserUnlockedEvent(mGuestUser.id);
assertPostSwitch(requestId, mGuestUser.id, mGuestUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1226,9 +1238,9 @@
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
- assertThat(getUserSwitchResult().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_ANDROID_FAILURE);
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_ANDROID_FAILURE);
assertPostSwitch(requestId, mAdminUser.id, mGuestUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1241,9 +1253,10 @@
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
UserSwitchResult result = getUserSwitchResult();
- assertThat(result.getStatus()).isEqualTo(UserSwitchResult.STATUS_HAL_FAILURE);
+ assertUserSwitchResult(result.getStatus(), UserSwitchResult.STATUS_HAL_FAILURE);
assertThat(result.getErrorMessage()).isEqualTo(mSwitchUserResponse.errorMessage);
verifyNoUserSwitch();
+ assertLogoutUserNotCleared();
}
@Test
@@ -1255,9 +1268,9 @@
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
- assertThat(getUserSwitchResult().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE);
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE);
verifyNoUserSwitch();
+ assertLogoutUserNotCleared();
}
@Test
@@ -1268,10 +1281,11 @@
initService();
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
- assertThat(getUserSwitchResult().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE);
+ assertUserSwitchResult(getUserSwitchResult(),
+ UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE);
assertNoHalUserSwitch();
verifyNoUserSwitch();
+ assertLogoutUserNotCleared();
}
@Test
@@ -1286,19 +1300,20 @@
// Should be ok first time...
ICarUxRestrictionsChangeListener listener = initService();
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
- assertThat(getUserSwitchResult().getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_SUCCESSFUL);
// ...but then fail after the state changed
mockCurrentUser(mGuestUser);
updateUxRestrictions(listener, /* restricted= */ true); // changed state
switchUser(mAdminUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture2);
- assertThat(getUserSwitchResult2().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE);
+ assertUserSwitchResult(getUserSwitchResult2(),
+ UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE);
// Verify only initial call succeeded (if second was also called the mocks, verify() would
// fail because it was called more than once()
assertHalSwitchAnyUser();
verifyAnyUserSwitch();
+ assertLogoutUserNotCleared();
}
@Test
@@ -1321,12 +1336,12 @@
mockAmSwitchUser(mRegularUser, true);
switchUser(mRegularUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture2);
- assertThat(getUserSwitchResult().getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
- assertThat(getUserSwitchResult2().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult2(), UserSwitchResult.STATUS_SUCCESSFUL);
assertNoPostSwitch();
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1351,11 +1366,11 @@
mockCurrentUser(mRegularUser);
sendUserUnlockedEvent(mRegularUser.id);
- assertThat(getUserSwitchResult2().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult2(), UserSwitchResult.STATUS_SUCCESSFUL);
assertPostSwitch(newRequestId, mRegularUser.id, mRegularUser.id);
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1376,11 +1391,11 @@
mockAmSwitchUser(mRegularUser, true);
switchUser(mRegularUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture2);
- assertThat(getUserSwitchResult2().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult2(), UserSwitchResult.STATUS_SUCCESSFUL);
assertNoPostSwitch();
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1404,11 +1419,11 @@
mockCurrentUser(mRegularUser);
sendUserUnlockedEvent(mRegularUser.id);
- assertThat(getUserSwitchResult2().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult2(), UserSwitchResult.STATUS_SUCCESSFUL);
assertPostSwitch(newRequestId, mRegularUser.id, mRegularUser.id);
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1435,14 +1450,13 @@
sendUserUnlockedEvent(mRegularUser.id);
blockingAnswer.unblock();
- UserSwitchResult result = getUserSwitchResult();
- assertThat(result.getStatus())
- .isEqualTo(UserSwitchResult.STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST);
- assertThat(getUserSwitchResult2().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult(),
+ UserSwitchResult.STATUS_TARGET_USER_ABANDONED_DUE_TO_A_NEW_REQUEST);
+ assertUserSwitchResult(getUserSwitchResult2(), UserSwitchResult.STATUS_SUCCESSFUL);
assertPostSwitch(newRequestId, mRegularUser.id, mRegularUser.id);
assertHalSwitch(mAdminUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mRegularUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1457,10 +1471,11 @@
// calling another user switch before unlock
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture2);
- assertThat(getUserSwitchResult2().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO);
+ assertUserSwitchResult(getUserSwitchResult2(),
+ UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO);
assertNoPostSwitch();
assertHalSwitch(mAdminUser.id, mGuestUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1476,11 +1491,12 @@
// calling another user switch before unlock
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture2);
- assertThat(getUserSwitchResult().getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
- assertThat(getUserSwitchResult2().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO);
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult2(),
+ UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO);
assertNoPostSwitch();
assertHalSwitch(mAdminUser.id, mGuestUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1502,16 +1518,18 @@
mockCurrentUser(mGuestUser);
sendUserUnlockedEvent(mGuestUser.id);
- assertThat(getUserSwitchResult().getStatus()).isEqualTo(UserSwitchResult.STATUS_SUCCESSFUL);
- assertThat(getUserSwitchResult2().getStatus())
- .isEqualTo(UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO);
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_SUCCESSFUL);
+ assertUserSwitchResult(getUserSwitchResult2(),
+ UserSwitchResult.STATUS_TARGET_USER_ALREADY_BEING_SWITCHED_TO);
assertPostSwitch(requestId, mGuestUser.id, mGuestUser.id);
assertHalSwitch(mAdminUser.id, mGuestUser.id);
+ assertLogoutUserNotCleared();
}
@Test
public void testSwitchUser_InvalidPermission() throws Exception {
mockManageUsersPermission(android.Manifest.permission.MANAGE_USERS, false);
+
assertThrows(SecurityException.class, () -> mCarUserService
.switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture));
}
@@ -1519,10 +1537,18 @@
@Test
public void testLegacyUserSwitch_ok() throws Exception {
mockExistingUsers(mExistingUsers);
+ int targetUserId = mRegularUser.id;
+ int sourceUserId = mAdminUser.id;
- sendUserSwitchingEvent(mAdminUser.id, mRegularUser.id);
+ mockCallerUid(Binder.getCallingUid(), true);
+ mCarUserService.setUserSwitchUiCallback(mSwitchUserUiReceiver);
- verify(mUserHal).legacyUserSwitch(isSwitchUserRequest(0, mAdminUser.id, mRegularUser.id));
+ sendUserSwitchingEvent(sourceUserId, targetUserId);
+
+ verify(mUserHal).legacyUserSwitch(
+ isSwitchUserRequest(/* requestId= */ 0, sourceUserId, targetUserId));
+ verify(mSwitchUserUiReceiver).send(targetUserId, null);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1534,13 +1560,18 @@
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, true);
- switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
+ int targetUserId = mGuestUser.id;
+ mockCallerUid(Binder.getCallingUid(), true);
+ mCarUserService.setUserSwitchUiCallback(mSwitchUserUiReceiver);
+ switchUser(targetUserId, mAsyncCallTimeoutMs, mUserSwitchFuture);
// Act - trigger legacy switch
- sendUserSwitchingEvent(mAdminUser.id, mGuestUser.id);
+ sendUserSwitchingEvent(mAdminUser.id, targetUserId);
// Assert
verify(mUserHal, never()).legacyUserSwitch(any());
+ verify(mSwitchUserUiReceiver).send(targetUserId, null);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1549,12 +1580,13 @@
int callerId = Binder.getCallingUid();
mockCallerUid(callerId, true);
int requestId = 42;
+ mCarUserService.setUserSwitchUiCallback(mSwitchUserUiReceiver);
+
mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
mSwitchUserResponse.requestId = requestId;
mockHalSwitch(mAdminUser.id, mGuestUser, mSwitchUserResponse);
mockAmSwitchUser(mGuestUser, true);
- mCarUserService.setUserSwitchUiCallback(mSwitchUserUiReceiver);
switchUser(mGuestUser.id, mAsyncCallTimeoutMs, mUserSwitchFuture);
// update current user due to successful user switch
@@ -1581,6 +1613,7 @@
sendUserUnlockedEvent(mRegularUser.id);
assertPostSwitch(requestId, mRegularUser.id, mRegularUser.id);
+ assertLogoutUserNotCleared();
}
@Test
@@ -1592,6 +1625,148 @@
mCarUserService.switchAndroidUserFromHal(requestId, mRegularUser.id);
assertPostSwitch(requestId, mAdminUser.id, mRegularUser.id);
+ assertLogoutUserNotCleared();
+ }
+
+ @Test
+ public void testLogoutUser_currentUserNotSwitchedByDeviceAdmin() throws Exception {
+ mockNoLogoutUserId();
+
+ logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_NOT_LOGGED_IN);
+ assertLogoutUserNotCleared();
+ }
+
+ @Test
+ @ExpectWtf
+ public void testLogoutUser_logoutUserDoesntExist() throws Exception {
+ mockLogoutUserId(mAdminUserId);
+ // No need to mock um.getUser(mAdminUserId) - it will return null by default
+
+ logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_ANDROID_FAILURE);
+ assertLogoutUserNotCleared();
+ }
+
+ @Test
+ public void testLogoutUser_halNotSupported_noUserSwitchability() throws Exception {
+ mockLogoutUser(mAdminUser);
+ mockUserHalSupported(false);
+ mockAmSwitchUser(mAdminUser, true);
+
+ mockUmGetUserSwitchability(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED);
+
+ logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_SUCCESSFUL);
+ assertLogoutUserCleared();
+ }
+
+ @Test
+ public void testLogoutUser_halNotSupported_success() throws Exception {
+ mockLogoutUser(mAdminUser);
+ mockUserHalSupported(false);
+ mockAmSwitchUser(mAdminUser, true);
+
+ logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_SUCCESSFUL);
+ assertLogoutUserCleared();
+ }
+
+ @Test
+ public void testLogoutUser_halNotSupported_failure() throws Exception {
+ mockLogoutUser(mAdminUser);
+ mockUserHalSupported(false);
+ // Don't need to call mockAmSwitchUser() because it returns false by default
+
+ logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_ANDROID_FAILURE);
+ assertLogoutUserNotCleared();
+ }
+
+ @Test
+ public void testLogoutUser_halSuccessAndroidSuccess() throws Exception {
+ mockExistingUsersAndCurrentUser(mGuestUser);
+ mockLogoutUser(mAdminUser);
+ mockUserHalSupported(true);
+ int requestId = 42;
+ mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
+ mSwitchUserResponse.requestId = requestId;
+ mockHalSwitch(mGuestUserId, mAdminUser, mSwitchUserResponse);
+ mockAmSwitchUser(mAdminUser, true);
+
+ logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_SUCCESSFUL);
+ assertLogoutUserCleared();
+
+ // update current user due to successful user switch
+ mockCurrentUser(mAdminUser);
+ sendUserUnlockedEvent(mAdminUserId);
+ assertPostSwitch(requestId, mAdminUserId, mAdminUserId);
+ }
+
+ @Test
+ public void testLogoutUser_halSuccessAndroidFailure() throws Exception {
+ mockExistingUsersAndCurrentUser(mGuestUser);
+ mockLogoutUser(mAdminUser);
+ mockUserHalSupported(true);
+ int requestId = 42;
+ mSwitchUserResponse.status = SwitchUserStatus.SUCCESS;
+ mSwitchUserResponse.requestId = requestId;
+ mockHalSwitch(mGuestUserId, mAdminUser, mSwitchUserResponse);
+ // Don't need to call mockAmSwitchUser() because it returns false by default
+
+ logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertUserSwitchResult(getUserSwitchResult(), UserSwitchResult.STATUS_ANDROID_FAILURE);
+ assertLogoutUserNotCleared();
+ assertPostSwitch(requestId, mGuestUserId, mAdminUserId);
+ }
+
+ @Test
+ public void testLogoutUser_halFailure() throws Exception {
+ mockExistingUsersAndCurrentUser(mGuestUser);
+ mockLogoutUser(mAdminUser);
+ mockUserHalSupported(true);
+ mSwitchUserResponse.status = SwitchUserStatus.FAILURE;
+ mSwitchUserResponse.errorMessage = "Error Message";
+ mockHalSwitch(mGuestUserId, mAdminUser, mSwitchUserResponse);
+
+ logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ UserSwitchResult result = getUserSwitchResult();
+ assertUserSwitchResult(result, UserSwitchResult.STATUS_HAL_FAILURE);
+ assertThat(result.getErrorMessage()).isEqualTo(mSwitchUserResponse.errorMessage);
+ assertLogoutUserNotCleared();
+ verifyNoUserSwitch();
+ }
+
+ @Test
+ public void testLogoutUser_failUxRestrictedOnInit() throws Exception {
+ mockGetUxRestrictions(/*restricted= */ true);
+ mockLogoutUser(mAdminUser);
+
+ initService();
+ logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture);
+
+ assertUserSwitchResult(getUserSwitchResult(),
+ UserSwitchResult.STATUS_UX_RESTRICTION_FAILURE);
+ assertLogoutUserNotCleared();
+ assertNoHalUserSwitch();
+ verifyNoUserSwitch();
+ }
+
+ @Test
+ public void testLogoutUser_InvalidPermission() throws Exception {
+ mockManageUsersPermission(android.Manifest.permission.MANAGE_USERS, false);
+
+ assertThrows(SecurityException.class, () -> mCarUserService
+ .logoutUser(mAsyncCallTimeoutMs, mUserSwitchFuture));
}
@Test
@@ -1713,6 +1888,22 @@
}
@Test
+ public void testCreateUser_disallowAddUser() throws Exception {
+ mockUmHasUserRestrictionForUser(mMockedUserManager, Process.myUserHandle(),
+ UserManager.DISALLOW_ADD_USER, /* value= */ true);
+ mockUmCreateUser(mMockedUserManager, "dude", UserManager.USER_TYPE_FULL_SECONDARY,
+ /* flags= */ 0, 42);
+
+ createUser("dude", UserManager.USER_TYPE_FULL_SECONDARY, /* flags= */ 0,
+ mAsyncCallTimeoutMs, mUserCreationFuture, NO_CALLER_RESTRICTIONS);
+
+ UserCreationResult result = getUserCreationResult();
+ assertThat(result.getStatus()).isEqualTo(UserCreationResult.STATUS_ANDROID_FAILURE);
+ assertNoHalUserCreation();
+ assertNoHalUserRemoval();
+ }
+
+ @Test
public void testCreateUser_success() throws Exception {
mockExistingUsersAndCurrentUser(mAdminUser);
int userId = mGuestUser.id;
@@ -2390,6 +2581,11 @@
waitForHandlerThreadToFinish();
}
+ private void logoutUser(int timeoutMs, @NonNull AndroidFuture<UserSwitchResult> receiver) {
+ mCarUserService.logoutUser(timeoutMs, receiver);
+ waitForHandlerThreadToFinish();
+ }
+
private void removeUser(@UserIdInt int userId,
@NonNull AndroidFuture<UserRemovalResult> userRemovalFuture) {
mCarUserService.removeUser(userId, userRemovalFuture);
@@ -2449,6 +2645,7 @@
mMockContext,
mUserHal,
mMockedUserManager,
+ mMockedDevicePolicyManager,
mMockedIActivityManager,
/* maxRunningUsers= */ 3,
mInitialUserSetter,
@@ -2507,6 +2704,10 @@
when(mMockedIActivityManager.switchUser(user.id)).thenReturn(result);
}
+ private void mockUmGetUserSwitchability(@UserSwitchabilityResult int result) {
+ when(mMockedUserManager.getUserSwitchability()).thenReturn(result);
+ }
+
private void mockRemoveUser(@NonNull UserInfo user) {
mockRemoveUser(user, UserManager.REMOVE_RESULT_REMOVED);
}
@@ -2770,6 +2971,19 @@
when(mCarUxRestrictionService.getCurrentUxRestrictions()).thenReturn(restrictions);
}
+ private void mockNoLogoutUserId() {
+ mockLogoutUserId(UserHandle.USER_NULL);
+ }
+
+ private void mockLogoutUser(UserInfo user) {
+ mockLogoutUserId(user.id);
+ mockUmGetUserInfo(mMockedUserManager, user);
+ }
+
+ private void mockLogoutUserId(@UserIdInt int userId) {
+ when(mMockedDevicePolicyManager.getLogoutUserId()).thenReturn(userId);
+ }
+
/**
* Asserts a {@link UsersInfo} that was created based on {@link #mockCurrentUsers(UserInfo)}.
*/
@@ -2899,6 +3113,14 @@
}
}
+ private void assertLogoutUserCleared() {
+ verify(mMockedDevicePolicyManager).clearLogoutUser();
+ }
+
+ private void assertLogoutUserNotCleared() {
+ verify(mMockedDevicePolicyManager, never()).clearLogoutUser();
+ }
+
@NonNull
private static SwitchUserRequest isSwitchUserRequest(int requestId,
@UserIdInt int currentUserId, @UserIdInt int targetUserId) {
@@ -2963,6 +3185,21 @@
CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING);
}
+ private void assertUserSwitchResult(UserSwitchResult result, int expected) {
+ assertUserSwitchResult(result.getStatus(), expected);
+ }
+
+ private void assertUserSwitchResult(int actual, int expected) {
+ assertWithMessage("user switch result (where %s=%s and %s=%s)",
+ expected, userSwitchResultToString(expected),
+ actual, userSwitchResultToString(actual))
+ .that(actual).isEqualTo(expected);
+ }
+
+ private static String userSwitchResultToString(int status) {
+ return DebugUtils.constantToString(UserSwitchResult.class, "STATUS_", status);
+ }
+
@NonNull
private static UserIdentificationGetRequest isUserIdentificationGetRequest(
@NonNull UserInfo user, @NonNull int[] types) {