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 import android.util.Log; 25 26 import androidx.annotation.CheckResult; 27 import androidx.annotation.Nullable; 28 29 import com.android.bedstead.nene.TestApis; 30 import com.android.bedstead.nene.exceptions.AdbException; 31 import com.android.bedstead.nene.exceptions.NeneException; 32 import com.android.bedstead.nene.utils.ShellCommand; 33 import com.android.bedstead.nene.utils.ShellCommandUtils; 34 import com.android.bedstead.nene.utils.Versions; 35 36 import java.util.UUID; 37 38 /** 39 * Builder for creating a new Android User. 40 */ 41 public final class UserBuilder { 42 43 private String mName; 44 private @Nullable UserType mType; 45 private @Nullable UserReference mParent; 46 private boolean mForTesting = true; 47 private boolean mEphemeral = false; 48 49 private static final String LOG_TAG = "UserBuilder"; 50 UserBuilder()51 UserBuilder() { 52 } 53 54 /** 55 * Set the user's name. 56 */ 57 @CheckResult name(String name)58 public UserBuilder name(String name) { 59 if (name == null) { 60 throw new NullPointerException(); 61 } 62 mName = name; 63 return this; 64 } 65 66 /** 67 * Set the {@link UserType}. 68 * 69 * <p>Defaults to android.os.usertype.full.SECONDARY 70 */ 71 @CheckResult type(UserType type)72 public UserBuilder type(UserType type) { 73 if (type == null) { 74 // We don't want to allow null to be passed in explicitly as that would cause subtle 75 // bugs when chaining with .supportedType() which can return null 76 throw new NullPointerException("Can not set type to null"); 77 } 78 mType = type; 79 return this; 80 } 81 82 /** 83 * Set the {@link UserType}. 84 * 85 * <p>Defaults to android.os.usertype.full.SECONDARY 86 */ 87 @CheckResult type(String typeName)88 public UserBuilder type(String typeName) { 89 if (typeName == null) { 90 // We don't want to allow null to be passed in explicitly as that would cause subtle 91 // bugs when chaining with .supportedType() which can return null 92 throw new NullPointerException("Can not set type to null"); 93 } 94 return type(TestApis.users().supportedType(typeName)); 95 } 96 97 /** 98 * Set if this user should be marked as for-testing. 99 * 100 * <p>This means it should not contain human user data - and will ensure it does not block 101 * usage of some test functionality 102 * 103 * <p>This defaults to true 104 */ 105 @CheckResult forTesting(boolean forTesting)106 public UserBuilder forTesting(boolean forTesting) { 107 mForTesting = forTesting; 108 return this; 109 } 110 111 /** 112 * Set if this user is ephemeral. 113 * 114 * <p>This defaults to false 115 */ 116 @CheckResult ephemeral(boolean ephemeral)117 public UserBuilder ephemeral(boolean ephemeral) { 118 mEphemeral = ephemeral; 119 return this; 120 } 121 122 /** 123 * Set the parent of the new user. 124 * 125 * <p>This should only be set if the {@link #type(UserType)} is a profile. 126 */ 127 @CheckResult parent(UserReference parent)128 public UserBuilder parent(UserReference parent) { 129 mParent = parent; 130 return this; 131 } 132 133 /** Create the user. */ create()134 public UserReference create() { 135 if (mName == null) { 136 mName = UUID.randomUUID().toString(); 137 } 138 139 ShellCommand.Builder commandBuilder = ShellCommand.builder("pm create-user"); 140 141 if (mType != null) { 142 if (mType.baseType().contains(UserType.BaseType.SYSTEM)) { 143 throw new NeneException( 144 "Can not create additional system users " + this); 145 } 146 147 if (mType.baseType().contains(UserType.BaseType.PROFILE)) { 148 if (mParent == null) { 149 throw new NeneException("When creating a profile, the parent user must be" 150 + " specified"); 151 } 152 153 commandBuilder.addOption("--profileOf", mParent.id()); 154 } else if (mParent != null) { 155 throw new NeneException("A parent should only be specified when create profiles"); 156 } 157 158 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { 159 if (mType.name().equals(MANAGED_PROFILE_TYPE_NAME)) { 160 if (mParent.id() != SYSTEM_USER_ID) { 161 // On R, this error will be thrown when we execute the command 162 throw new NeneException( 163 "Can not create managed profiles of users other than the " 164 + "system user" 165 ); 166 } 167 168 commandBuilder.addOperand("--managed"); 169 } else if (!mType.name().equals(SECONDARY_USER_TYPE_NAME)) { 170 // This shouldn't be reachable as before R we can't fetch a list of user types 171 // so the only supported ones are system/managed profile/secondary 172 throw new NeneException( 173 "Can not create users of type " + mType + " on this device"); 174 } 175 } else { 176 commandBuilder.addOption("--user-type", mType.name()); 177 } 178 } 179 180 if (Versions.meetsMinimumSdkVersionRequirement(Versions.U) && mForTesting) { 181 // Marking all created users as test users means we don't block changing device 182 // management states 183 commandBuilder.addOperand("--for-testing"); 184 } 185 186 if (mEphemeral) { 187 commandBuilder.addOperand("--ephemeral"); 188 } 189 190 commandBuilder.addOperand(mName); 191 192 // Expected success string is e.g. "Success: created user id 14" 193 try { 194 195 Log.d(LOG_TAG, "Creating user with command " + commandBuilder); 196 int userId = 197 commandBuilder.validate(ShellCommandUtils::startsWithSuccess) 198 .executeAndParseOutput( 199 (output) -> Integer.parseInt(output.split("id ")[1].trim())); 200 return TestApis.users().find(userId); 201 } catch (AdbException e) { 202 throw new NeneException("Could not create user " + this, e); 203 } 204 } 205 206 /** 207 * Create the user and start it. 208 * 209 * <p>Equivalent of calling {@link #create()} and then {@link User#start()}. 210 */ createAndStart()211 public UserReference createAndStart() { 212 return create().start(); 213 } 214 215 @Override toString()216 public String toString() { 217 return new StringBuilder("UserBuilder{") 218 .append("name=").append(mName) 219 .append(", type=").append(mType) 220 .append("}") 221 .toString(); 222 } 223 } 224