1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bedstead.nene.users; 18 19 import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME; 20 import static com.android.bedstead.nene.users.UserType.SECONDARY_USER_TYPE_NAME; 21 import static com.android.bedstead.nene.users.Users.SYSTEM_USER_ID; 22 23 import android.os.Build; 24 25 import androidx.annotation.CheckResult; 26 import androidx.annotation.Nullable; 27 28 import com.android.bedstead.nene.TestApis; 29 import com.android.bedstead.nene.exceptions.AdbException; 30 import com.android.bedstead.nene.exceptions.NeneException; 31 import com.android.bedstead.nene.utils.ShellCommand; 32 import com.android.bedstead.nene.utils.ShellCommandUtils; 33 34 import java.util.UUID; 35 36 /** 37 * Builder for creating a new Android User. 38 */ 39 public class UserBuilder { 40 41 private String mName; 42 private @Nullable UserType mType; 43 private @Nullable UserReference mParent; 44 UserBuilder()45 UserBuilder() { 46 } 47 48 /** 49 * Set the user's name. 50 */ 51 @CheckResult name(String name)52 public UserBuilder name(String name) { 53 if (name == null) { 54 throw new NullPointerException(); 55 } 56 mName = name; 57 return this; 58 } 59 60 /** 61 * Set the {@link UserType}. 62 * 63 * <p>Defaults to android.os.usertype.full.SECONDARY 64 */ 65 @CheckResult type(UserType type)66 public UserBuilder type(UserType type) { 67 if (type == null) { 68 // We don't want to allow null to be passed in explicitly as that would cause subtle 69 // bugs when chaining with .supportedType() which can return null 70 throw new NullPointerException("Can not set type to null"); 71 } 72 mType = type; 73 return this; 74 } 75 76 /** 77 * Set the parent of the new user. 78 * 79 * <p>This should only be set if the {@link #type(UserType)} is a profile. 80 */ 81 @CheckResult parent(UserReference parent)82 public UserBuilder parent(UserReference parent) { 83 mParent = parent; 84 return this; 85 } 86 87 /** Create the user. */ create()88 public UserReference create() { 89 if (mName == null) { 90 mName = UUID.randomUUID().toString(); 91 } 92 93 ShellCommand.Builder commandBuilder = ShellCommand.builder("pm create-user"); 94 95 if (mType != null) { 96 if (mType.baseType().contains(UserType.BaseType.SYSTEM)) { 97 throw new NeneException( 98 "Can not create additional system users " + this); 99 } 100 101 if (mType.baseType().contains(UserType.BaseType.PROFILE)) { 102 if (mParent == null) { 103 throw new NeneException("When creating a profile, the parent user must be" 104 + " specified"); 105 } 106 107 commandBuilder.addOption("--profileOf", mParent.id()); 108 } else if (mParent != null) { 109 throw new NeneException("A parent should only be specified when create profiles"); 110 } 111 112 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { 113 if (mType.name().equals(MANAGED_PROFILE_TYPE_NAME)) { 114 if (mParent.id() != SYSTEM_USER_ID) { 115 // On R, this error will be thrown when we execute the command 116 throw new NeneException( 117 "Can not create managed profiles of users other than the " 118 + "system user" 119 ); 120 } 121 122 commandBuilder.addOperand("--managed"); 123 } else if (!mType.name().equals(SECONDARY_USER_TYPE_NAME)) { 124 // This shouldn't be reachable as before R we can't fetch a list of user types 125 // so the only supported ones are system/managed profile/secondary 126 throw new NeneException( 127 "Can not create users of type " + mType + " on this device"); 128 } 129 } else { 130 commandBuilder.addOption("--user-type", mType.name()); 131 } 132 } 133 134 commandBuilder.addOperand(mName); 135 136 // Expected success string is e.g. "Success: created user id 14" 137 try { 138 int userId = 139 commandBuilder.validate(ShellCommandUtils::startsWithSuccess) 140 .executeAndParseOutput( 141 (output) -> Integer.parseInt(output.split("id ")[1].trim())); 142 return TestApis.users().find(userId); 143 } catch (AdbException e) { 144 throw new NeneException("Could not create user " + this, e); 145 } 146 } 147 148 /** 149 * Create the user and start it. 150 * 151 * <p>Equivalent of calling {@link #create()} and then {@link User#start()}. 152 */ createAndStart()153 public UserReference createAndStart() { 154 return create().start(); 155 } 156 157 @Override toString()158 public String toString() { 159 return new StringBuilder("UserBuilder{") 160 .append("name=").append(mName) 161 .append(", type=").append(mType) 162 .append("}") 163 .toString(); 164 } 165 } 166