1 /*
2  * Copyright (C) 2016 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.aetest.tradefed.targetprep;
18 
19 import static com.android.tradefed.util.BuildTestsZipUtils.getApkFile;
20 
21 import com.android.tradefed.build.IBuildInfo;
22 import com.android.tradefed.config.Option;
23 import com.android.tradefed.config.OptionClass;
24 import com.android.tradefed.device.DeviceNotAvailableException;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.log.LogUtil.CLog;
27 import com.android.tradefed.targetprep.AltDirBehavior;
28 import com.android.tradefed.targetprep.BaseTargetPreparer;
29 import com.android.tradefed.targetprep.BuildError;
30 import com.android.tradefed.targetprep.ITargetCleaner;
31 import com.android.tradefed.targetprep.ITargetPreparer;
32 import com.android.tradefed.targetprep.TargetSetupError;
33 import com.android.tradefed.targetprep.TestAppInstallSetup;
34 
35 import java.io.File;
36 import java.io.IOException;
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 /**
41  * A {@link ITargetPreparer} that creates a managed profile in the testing device.
42  *
43  * <p>This was forked off Android Enterprise team's target preparer, where its method of resolving
44  * the test APK was substituted with the platform's implementation. It is also modified to use the
45  * standard TradeFed way of disabling a preparer. The two classes are otherwise the same.
46  */
47 @OptionClass(alias = "ae-test-create-managed-profile")
48 public class AeTestManagedProfileCreator extends BaseTargetPreparer implements ITargetCleaner {
49     public static final String MANAGED_PROFILE_USER_ID = "managed_profile_user_id";
50 
51     private final TestAppInstallSetup mInstallPreparer = new TestAppInstallSetup();
52 
53     @Option(
54             name = "profile-owner-component",
55             description = "Profile owner component to set; optional")
56     private String mProfileOwnerComponent = null;
57 
58     @Option(name = "profile-owner-apk", description = "Profile owner apk path; optional")
59     private String mProfileOwnerApk = null;
60 
61     @Option(
62             name = "alt-dir",
63             description =
64                     "Alternate directory to look for the profile owner apk if the apk is not in "
65                             + "the tests zip file. For each alternate dir, will look in //, "
66                             + "//data/app, //DATA/app, //DATA/app/apk_name/ and "
67                             + "//DATA/priv-app/apk_name/. Can be repeated. Look for apks in "
68                             + "last alt-dir first.")
69     private List<File> mAltDirs = new ArrayList<>();
70 
71     @Option(
72             name = "alt-dir-behavior",
73             description =
74                     "The order of alternate directory to be used "
75                             + "when searching for apks to install")
76     private AltDirBehavior mAltDirBehavior = AltDirBehavior.FALLBACK;
77 
78     @Option(
79             name = "test-app-file-name",
80             description =
81                     "the name of an apk file to be installed in work profile. Can be repeated. Items "
82                             + "that are directories will have any APKs contained therein, "
83                             + "including subdirectories, grouped by package name and installed.")
84     private List<File> mTestFiles = new ArrayList<>();
85 
86     @Option(
87             name = "test-app-alt-dir",
88             description =
89                     "Alternate directory to look for the apk if the apk is not in the tests "
90                             + "zip file. For each alternate dir, will look in //, //data/app, "
91                             + "//DATA/app, //DATA/app/apk_name/ and //DATA/priv-app/apk_name/. "
92                             + "Can be repeated. Look for apks in last alt-dir first.")
93     private List<File> mAltTestDirs = new ArrayList<>();
94 
95     /** UserID for user in managed profile. */
96     private int mManagedProfileUserId;
97 
98     /** {@inheritDoc} */
99     @Override
setUp(ITestDevice device, IBuildInfo buildInfo)100     public void setUp(ITestDevice device, IBuildInfo buildInfo)
101             throws TargetSetupError, DeviceNotAvailableException, BuildError {
102         String pmCommand =
103                 "pm create-user --profileOf 0 --managed "
104                         + "TestProfile_"
105                         + System.currentTimeMillis();
106         String pmCommandOutput = device.executeShellCommand(pmCommand);
107 
108         // Extract the id of the new user.
109         String[] pmCmdTokens = pmCommandOutput.split("\\s+");
110         if (!pmCmdTokens[0].contains("Success:")) {
111             throw new TargetSetupError("Managed profile creation failed.");
112         }
113         mManagedProfileUserId = Integer.parseInt(pmCmdTokens[pmCmdTokens.length - 1]);
114 
115         // Start managed profile user.
116         device.startUser(mManagedProfileUserId);
117 
118         CLog.i(String.format("New user created: %d", mManagedProfileUserId));
119         buildInfo.addBuildAttribute(MANAGED_PROFILE_USER_ID, String.valueOf(mManagedProfileUserId));
120 
121         // Install required test APKs in the managed profile
122         installTestApks(device, buildInfo);
123 
124         if (mProfileOwnerComponent != null && mProfileOwnerApk != null) {
125             device.installPackageForUser(
126                     getApk(device, buildInfo, mProfileOwnerApk), true, mManagedProfileUserId);
127             String command =
128                     String.format(
129                             "dpm set-profile-owner --user %d %s",
130                             mManagedProfileUserId, mProfileOwnerComponent);
131             String commandOutput = device.executeShellCommand(command);
132             String[] cmdTokens = commandOutput.split("\\s+");
133             if (!cmdTokens[0].contains("Success:")) {
134                 throw new RuntimeException(
135                         String.format(
136                                 "Fail to setup %s as profile owner.", mProfileOwnerComponent));
137             }
138 
139             CLog.i(
140                     String.format(
141                             "%s was set as profile owner of user %d",
142                             mProfileOwnerComponent, mManagedProfileUserId));
143         }
144         /*
145         Commenting this out, as the reboot somehow evaporates the permissions granted to
146         managed profile apps during installation. Creation of apps is not being impacted by this.
147         */
148         // Reboot device to create the apps in managed profile.
149         // device.reboot();
150         // device.waitForDeviceAvailable();
151     }
152 
153     /** {@inheritDoc} */
154     @Override
tearDown(ITestDevice testDevice, IBuildInfo buildInfo, Throwable throwable)155     public void tearDown(ITestDevice testDevice, IBuildInfo buildInfo, Throwable throwable)
156             throws DeviceNotAvailableException {
157         mInstallPreparer.tearDown(testDevice, buildInfo, throwable);
158         testDevice.removeUser(mManagedProfileUserId);
159     }
160 
installTestApks(ITestDevice device, IBuildInfo buildInfo)161     private void installTestApks(ITestDevice device, IBuildInfo buildInfo)
162             throws DeviceNotAvailableException, TargetSetupError, BuildError {
163         if (mTestFiles.isEmpty()) {
164             return;
165         }
166         CLog.i(
167                 String.format(
168                         "Installing the following test APKs in user %d: \n%s",
169                         mManagedProfileUserId, mTestFiles));
170         mInstallPreparer.setUserId(mManagedProfileUserId);
171         mInstallPreparer.setShouldGrantPermission(true);
172         for (File file : mTestFiles) {
173             mInstallPreparer.addTestFile(file);
174         }
175         for (File dir : mAltTestDirs) {
176             mInstallPreparer.setAltDir(dir);
177         }
178         mInstallPreparer.setUp(device, buildInfo);
179     }
180 
181     /**
182      * Get the {@link File} object that refers to the APK to install.
183      *
184      * <p>This is a replacement of the method with a similar signature in the original class's super
185      * class, so that the above code can align as closely to the original class as possible.
186      */
getApk(ITestDevice device, IBuildInfo buildInfo, String fileName)187     private File getApk(ITestDevice device, IBuildInfo buildInfo, String fileName)
188             throws TargetSetupError {
189         try {
190             File apkFile =
191                     getApkFile(
192                             buildInfo,
193                             mProfileOwnerApk,
194                             mAltDirs,
195                             mAltDirBehavior,
196                             false /* use resource as fallback */,
197                             null /* device signing key */);
198             if (apkFile == null) {
199                 throw new TargetSetupError(
200                         String.format("Test app %s was not found.", mProfileOwnerApk),
201                         device.getDeviceDescriptor());
202             }
203             return apkFile;
204         } catch (IOException e) {
205             throw new TargetSetupError(
206                     String.format(
207                             "failed to resolve apk path for apk %s in build %s",
208                             mProfileOwnerApk, buildInfo.toString()),
209                     e,
210                     device.getDeviceDescriptor());
211         }
212     }
213 }
214