blob: 31e26850e77e80f7eda91503a3753887a2c695a8 [file] [log] [blame]
/*
* 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 com.android.bedstead.nene.users;
import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME;
import static com.android.bedstead.nene.users.UserType.SECONDARY_USER_TYPE_NAME;
import static com.android.bedstead.nene.users.Users.SYSTEM_USER_ID;
import android.os.Build;
import androidx.annotation.CheckResult;
import androidx.annotation.Nullable;
import com.android.bedstead.nene.exceptions.AdbException;
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.utils.ShellCommand;
import com.android.bedstead.nene.utils.ShellCommandUtils;
import java.util.UUID;
/**
* Builder for creating a new Android User.
*/
public class UserBuilder {
private String mName;
private @Nullable UserType mType;
private @Nullable UserReference mParent;
UserBuilder() {
}
/**
* Set the user's name.
*/
@CheckResult
public UserBuilder name(String name) {
if (name == null) {
throw new NullPointerException();
}
mName = name;
return this;
}
/**
* Set the {@link UserType}.
*
* <p>Defaults to android.os.usertype.full.SECONDARY
*/
@CheckResult
public UserBuilder type(UserType type) {
if (type == null) {
// We don't want to allow null to be passed in explicitly as that would cause subtle
// bugs when chaining with .supportedType() which can return null
throw new NullPointerException("Can not set type to null");
}
mType = type;
return this;
}
/**
* Set the parent of the new user.
*
* <p>This should only be set if the {@link #type(UserType)} is a profile.
*/
@CheckResult
public UserBuilder parent(UserReference parent) {
mParent = parent;
return this;
}
/** Create the user. */
public UserReference create() {
if (mName == null) {
mName = UUID.randomUUID().toString();
}
ShellCommand.Builder commandBuilder = ShellCommand.builder("pm create-user");
if (mType != null) {
if (mType.baseType().contains(UserType.BaseType.SYSTEM)) {
throw new NeneException(
"Can not create additional system users " + this);
}
if (mType.baseType().contains(UserType.BaseType.PROFILE)) {
if (mParent == null) {
throw new NeneException("When creating a profile, the parent user must be"
+ " specified");
}
commandBuilder.addOption("--profileOf", mParent.id());
} else if (mParent != null) {
throw new NeneException("A parent should only be specified when create profiles");
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
if (mType.name().equals(MANAGED_PROFILE_TYPE_NAME)) {
if (mParent.id() != SYSTEM_USER_ID) {
// On R, this error will be thrown when we execute the command
throw new NeneException(
"Can not create managed profiles of users other than the "
+ "system user"
);
}
commandBuilder.addOperand("--managed");
} else if (!mType.name().equals(SECONDARY_USER_TYPE_NAME)) {
// This shouldn't be reachable as before R we can't fetch a list of user types
// so the only supported ones are system/managed profile/secondary
throw new NeneException(
"Can not create users of type " + mType + " on this device");
}
} else {
commandBuilder.addOption("--user-type", mType.name());
}
}
commandBuilder.addOperand(mName);
// Expected success string is e.g. "Success: created user id 14"
try {
int userId =
commandBuilder.validate(ShellCommandUtils::startsWithSuccess)
.executeAndParseOutput(
(output) -> Integer.parseInt(output.split("id ")[1].trim()));
return new UserReference(userId);
} catch (AdbException e) {
throw new NeneException("Could not create user " + this, e);
}
}
/**
* Create the user and start it.
*
* <p>Equivalent of calling {@link #create()} and then {@link User#start()}.
*/
public UserReference createAndStart() {
return create().start();
}
@Override
public String toString() {
return new StringBuilder("UserBuilder{")
.append("name=").append(mName)
.append(", type=").append(mType)
.append("}")
.toString();
}
}