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 final TestApis mTestApis; 42 private String mName; 43 private @Nullable UserType mType; 44 private @Nullable UserReference mParent; 45 UserBuilder(TestApis testApis)46 UserBuilder(TestApis testApis) { 47 mTestApis = testApis; 48 } 49 50 /** 51 * Set the user's name. 52 */ 53 @CheckResult name(String name)54 public UserBuilder name(String name) { 55 if (name == null) { 56 throw new NullPointerException(); 57 } 58 mName = name; 59 return this; 60 } 61 62 /** 63 * Set the {@link UserType}. 64 * 65 * <p>Defaults to android.os.usertype.full.SECONDARY 66 */ 67 @CheckResult type(UserType type)68 public UserBuilder type(UserType type) { 69 if (type == null) { 70 // We don't want to allow null to be passed in explicitly as that would cause subtle 71 // bugs when chaining with .supportedType() which can return null 72 throw new NullPointerException("Can not set type to null"); 73 } 74 mType = type; 75 return this; 76 } 77 78 /** 79 * Set the parent of the new user. 80 * 81 * <p>This should only be set if the {@link #type(UserType)} is a profile. 82 */ 83 @CheckResult parent(UserReference parent)84 public UserBuilder parent(UserReference parent) { 85 mParent = parent; 86 return this; 87 } 88 89 /** Create the user. */ create()90 public UserReference create() { 91 if (mName == null) { 92 mName = UUID.randomUUID().toString(); 93 } 94 95 ShellCommand.Builder commandBuilder = ShellCommand.builder("pm create-user"); 96 97 if (mType != null) { 98 if (mType.baseType().contains(UserType.BaseType.SYSTEM)) { 99 throw new NeneException( 100 "Can not create additional system users " + this); 101 } 102 103 if (mType.baseType().contains(UserType.BaseType.PROFILE)) { 104 if (mParent == null) { 105 throw new NeneException("When creating a profile, the parent user must be" 106 + " specified"); 107 } 108 109 commandBuilder.addOption("--profileOf", mParent.id()); 110 } else if (mParent != null) { 111 throw new NeneException("A parent should only be specified when create profiles"); 112 } 113 114 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { 115 if (mType.name().equals(MANAGED_PROFILE_TYPE_NAME)) { 116 if (mParent.id() != SYSTEM_USER_ID) { 117 // On R, this error will be thrown when we execute the command 118 throw new NeneException( 119 "Can not create managed profiles of users other than the " 120 + "system user" 121 ); 122 } 123 124 commandBuilder.addOperand("--managed"); 125 } else if (!mType.name().equals(SECONDARY_USER_TYPE_NAME)) { 126 // This shouldn't be reachable as before R we can't fetch a list of user types 127 // so the only supported ones are system/managed profile/secondary 128 throw new NeneException( 129 "Can not create users of type " + mType + " on this device"); 130 } 131 } else { 132 commandBuilder.addOption("--user-type", mType.name()); 133 } 134 } 135 136 commandBuilder.addOperand(mName); 137 138 // Expected success string is e.g. "Success: created user id 14" 139 try { 140 int userId = 141 commandBuilder.validate(ShellCommandUtils::startsWithSuccess) 142 .executeAndParseOutput( 143 (output) -> Integer.parseInt(output.split("id ")[1].trim())); 144 return new UnresolvedUser(mTestApis, userId); 145 } catch (AdbException e) { 146 throw new NeneException("Could not create user " + this, e); 147 } 148 } 149 150 /** 151 * Create the user and start it. 152 * 153 * <p>Equivalent of calling {@link #create()} and then {@link User#start()}. 154 */ createAndStart()155 public UserReference createAndStart() { 156 return create().start(); 157 } 158 159 @Override toString()160 public String toString() { 161 return new StringBuilder("UserBuilder{") 162 .append("name=").append(mName) 163 .append(", type=").append(mType) 164 .append("}") 165 .toString(); 166 } 167 } 168