From 04f9d971cca85c520e836bcecfdededa25227ef8 Mon Sep 17 00:00:00 2001 From: Mayank Garg Date: Wed, 13 Oct 2021 13:58:04 -0700 Subject: [PATCH] Implementation for new createUser call Bug: 199446283 CTS-Coverage-Bug: 199446283 Test: m Change-Id: Ib15377cdac667b3ab6842aa894bb0890e341d610 --- core/api/system-current.txt | 23 +++ core/api/system-lint-baseline.txt | 4 + core/java/android/os/NewUserRequest.java | 170 ++++++++++++++++++++++ core/java/android/os/NewUserResponse.java | 59 ++++++++ core/java/android/os/UserManager.java | 33 +++++ 5 files changed, 289 insertions(+) mode change 100755 => 100644 core/api/system-current.txt create mode 100644 core/java/android/os/NewUserRequest.java create mode 100644 core/java/android/os/NewUserResponse.java diff --git a/core/api/system-current.txt b/core/api/system-current.txt old mode 100755 new mode 100644 index 4d6c1c8b174df..a6f92760ccef7 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -8677,6 +8677,28 @@ package android.os { method public boolean hasSingleFileDescriptor(); } + public final class NewUserRequest { + method @Nullable public String getName(); + method @NonNull public String getUserType(); + method public boolean isAdmin(); + method public boolean isEphemeral(); + } + + public static final class NewUserRequest.Builder { + ctor public NewUserRequest.Builder(); + method @NonNull public android.os.NewUserRequest build(); + method @NonNull public android.os.NewUserRequest.Builder setAdmin(); + method @NonNull public android.os.NewUserRequest.Builder setEphemeral(); + method @NonNull public android.os.NewUserRequest.Builder setName(@Nullable String); + method @NonNull public android.os.NewUserRequest.Builder setUserType(@NonNull String); + } + + public final class NewUserResponse { + method public int getOperationResult(); + method @Nullable public android.os.UserHandle getUser(); + method public boolean isSuccessful(); + } + public interface Parcelable { field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0 field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1 @@ -8913,6 +8935,7 @@ package android.os { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean canHaveRestrictedProfile(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void clearSeedAccountData(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle createProfile(@NonNull String, @NonNull String, @NonNull java.util.Set) throws android.os.UserManager.UserOperationException; + method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.NewUserResponse createUser(@NonNull android.os.NewUserRequest); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List getAllProfiles(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public java.util.List getEnabledProfiles(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle); diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index b435acfccde85..716f43e1d68ad 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -31,6 +31,10 @@ KotlinKeyword: android.app.Notification#when: +MissingGetterMatchingBuilder: android.os.NewUserRequest.Builder#setAdmin(): + android.os.NewUserRequest does not declare a `getAdmin()` method matching method android.os.NewUserRequest.Builder.setAdmin() +MissingGetterMatchingBuilder: android.os.NewUserRequest.Builder#setEphemeral(): + android.os.NewUserRequest does not declare a `getEphemeral()` method matching method android.os.NewUserRequest.Builder.setEphemeral() MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUid(int): android.security.keystore.KeyGenParameterSpec does not declare a `getUid()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUid(int) MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation): diff --git a/core/java/android/os/NewUserRequest.java b/core/java/android/os/NewUserRequest.java new file mode 100644 index 0000000000000..2ebc01f2d3d09 --- /dev/null +++ b/core/java/android/os/NewUserRequest.java @@ -0,0 +1,170 @@ +/* + * 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 android.os; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; + +/** + * Contains necessary information to create user using + * {@link UserManager#createUser(NewUserRequest)}. + * + * @hide + */ +@SystemApi +public final class NewUserRequest { + @Nullable + private final String mName; + private final boolean mAdmin; + private final boolean mEphemeral; + @NonNull + private final String mUserType; + + private NewUserRequest(Builder builder) { + mName = builder.mName; + mAdmin = builder.mAdmin; + mEphemeral = builder.mEphemeral; + mUserType = builder.mUserType; + } + + /** + * Gets the user name. + */ + @Nullable + public String getName() { + return mName; + } + + /** + * Is user Ephemenral? + * + *

Ephemeral user will be removed after leaving the foreground. + */ + public boolean isEphemeral() { + return mEphemeral; + } + + /** + * Is user Admin? + * + *

Admin user is with administrative privileges and such user can create and + * delete users. + */ + public boolean isAdmin() { + return mAdmin; + } + + /** + * Gets user type. + * + *

Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and + * {@link USER_TYPE_FULL_GUEST} + */ + @NonNull + public String getUserType() { + return mUserType; + } + + @Override + public String toString() { + return String.format( + "NewUserRequest- UserName:%s, userType:%s, IsAdmin:%s, IsEphemeral:%s.", mName, + mUserType, mAdmin, mEphemeral); + } + + /** + * Builder for building {@link NewUserRequest} + */ + public static final class Builder { + + private String mName; + private boolean mAdmin; + private boolean mEphemeral; + private String mUserType = UserManager.USER_TYPE_FULL_SECONDARY; + + /** + * Sets user name. + */ + @NonNull + public Builder setName(@Nullable String name) { + mName = name; + return this; + } + + /** + * Sets user as admin. + * + *

Admin user is with administrative privileges and such user can create + * and delete users. + */ + @NonNull + public Builder setAdmin() { + mAdmin = true; + return this; + } + + /** + * Sets user as ephemeral. + * + *

Ephemeral user will be removed after leaving the foreground. + */ + @NonNull + public Builder setEphemeral() { + mEphemeral = true; + return this; + } + + /** + * Sets user type. + *

+ * Supported types are {@link UserManager.USER_TYPE_FULL_SECONDARY} and + * {@link UserManager.USER_TYPE_FULL_GUEST}. Default value is + * {@link UserManager.USER_TYPE_FULL_SECONDARY}. + */ + @NonNull + public Builder setUserType(@NonNull String type) { + mUserType = type; + return this; + } + + /** + * Builds {@link NewUserRequest} + * + * @throws IllegalStateException if builder is configured with incompatible properties and + * it is not possible to create such user. For example - a guest admin user. + */ + @NonNull + public NewUserRequest build() { + checkIfPropertiesAreCompatible(); + return new NewUserRequest(this); + } + + private void checkIfPropertiesAreCompatible() { + // Conditions which can't be true simultaneously + // A guest user can't be admin user + if (mAdmin && mUserType == UserManager.USER_TYPE_FULL_GUEST) { + throw new IllegalStateException("A guest user can't be admin."); + } + + // check for only supported user types + if (mUserType != UserManager.USER_TYPE_FULL_SECONDARY + && mUserType != UserManager.USER_TYPE_FULL_GUEST) { + throw new IllegalStateException("Unsupported user type: " + mUserType); + } + } + } +} diff --git a/core/java/android/os/NewUserResponse.java b/core/java/android/os/NewUserResponse.java new file mode 100644 index 0000000000000..3869559dc3c67 --- /dev/null +++ b/core/java/android/os/NewUserResponse.java @@ -0,0 +1,59 @@ +/* + * 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 android.os; + +import android.annotation.Nullable; +import android.annotation.SystemApi; + +/** + * Contains the response of the call {@link UserManager#createUser(NewUserRequest)}. + * + * @hide + */ +@SystemApi +public final class NewUserResponse { + + private final @Nullable UserHandle mUser; + private final @UserManager.UserOperationResult int mOperationResult; + + NewUserResponse(@Nullable UserHandle user, + @UserManager.UserOperationResult int operationResult) { + mUser = user; + mOperationResult = operationResult; + } + + /** + * Is user creation successful? + */ + public boolean isSuccessful() { + return mUser != null; + } + + // TODO(b/199446283): If UserHandle.NULL is systemAPI, that can be returned here instead of null + /** + * Gets the created user handle. + */ + public @Nullable UserHandle getUser() { + return mUser; + } + + /** + * Gets operation results. + */ + public @UserManager.UserOperationResult int getOperationResult() { + return mOperationResult; + } +} diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 447102b2f28d2..94375c0e949fa 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -3148,6 +3148,39 @@ public class UserManager { } } + /** + * Creates a user with the specified {@link NewUserRequest}. + * + * @param newUserRequest specify the user information + * + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS, + Manifest.permission.CREATE_USERS}) + public @NonNull NewUserResponse createUser(@NonNull NewUserRequest newUserRequest) { + UserInfo user = null; + int operationResult = USER_OPERATION_ERROR_UNKNOWN; + try { + user = createUser(newUserRequest.getName(), newUserRequest.getUserType(), + determineFlagsForUserCreation(newUserRequest)); + } catch (UserOperationException e) { + Log.w(TAG, "Exception while creating user " + newUserRequest, e); + operationResult = e.getUserOperationResult(); + } + if (user == null) { + return new NewUserResponse(null, operationResult); + } + return new NewUserResponse(user.getUserHandle(), USER_OPERATION_SUCCESS); + } + + private int determineFlagsForUserCreation(NewUserRequest newUserRequest) { + int flags = 0; + if (newUserRequest.isAdmin()) flags |= UserInfo.FLAG_ADMIN; + if (newUserRequest.isEphemeral()) flags |= UserInfo.FLAG_EPHEMERAL; + return flags; + } + /** * Pre-creates a user of the specified type. Default user restrictions will be applied. *