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