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