1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 package android.appsecurity.cts;
17 
18 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
19 import com.android.tradefed.build.IBuildInfo;
20 import com.android.tradefed.device.DeviceNotAvailableException;
21 import com.android.tradefed.device.ITestDevice;
22 import com.android.tradefed.testtype.IAbi;
23 import com.android.tradefed.util.AbiUtils;
24 
25 import junit.framework.TestCase;
26 
27 import java.io.File;
28 import java.io.FileNotFoundException;
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 /**
33  * Base class for invoking the install-multiple command via ADB. Subclass this for less typing:
34  *
35  * <code>
36  *     private class InstallMultiple extends BaseInstallMultiple&lt;InstallMultiple&gt; {
37  *         public InstallMultiple() {
38  *             super(getDevice(), null, null);
39  *         }
40  *     }
41  * </code>
42  */
43 public class BaseInstallMultiple<T extends BaseInstallMultiple<?>> {
44     private final ITestDevice mDevice;
45     private final IBuildInfo mBuild;
46     private final IAbi mAbi;
47 
48     private final List<String> mArgs = new ArrayList<>();
49     private final List<File> mApks = new ArrayList<>();
50     private final List<String> mSplits = new ArrayList<>();
51     private boolean mUseNaturalAbi;
52 
BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo, IAbi abi)53     public BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo, IAbi abi) {
54         mDevice = device;
55         mBuild = buildInfo;
56         mAbi = abi;
57         addArg("-g");
58     }
59 
addArg(String arg)60     T addArg(String arg) {
61         mArgs.add(arg);
62         return (T) this;
63     }
64 
addApk(String apk)65     T addApk(String apk) throws FileNotFoundException {
66         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
67         mApks.add(buildHelper.getTestFile(apk));
68         return (T) this;
69     }
70 
removeSplit(String split)71     T removeSplit(String split) {
72         mSplits.add(split);
73         return (T) this;
74     }
75 
inheritFrom(String packageName)76     T inheritFrom(String packageName) {
77         addArg("-r");
78         addArg("-p " + packageName);
79         return (T) this;
80     }
81 
useNaturalAbi()82     T useNaturalAbi() {
83         mUseNaturalAbi = true;
84         return (T) this;
85     }
86 
allowTest()87     T allowTest() {
88         addArg("-t");
89         return (T) this;
90     }
91 
locationAuto()92     T locationAuto() {
93         addArg("--install-location 0");
94         return (T) this;
95     }
96 
locationInternalOnly()97     T locationInternalOnly() {
98         addArg("--install-location 1");
99         return (T) this;
100     }
101 
locationPreferExternal()102     T locationPreferExternal() {
103         addArg("--install-location 2");
104         return (T) this;
105     }
106 
forceUuid(String uuid)107     T forceUuid(String uuid) {
108         addArg("--force-uuid " + uuid);
109         return (T) this;
110     }
111 
forUser(int userId)112     T forUser(int userId) {
113         addArg("--user " + userId);
114         return (T) this;
115     }
116 
run()117     void run() throws DeviceNotAvailableException {
118         run(true, null);
119     }
120 
runExpectingFailure()121     void runExpectingFailure() throws DeviceNotAvailableException {
122         run(false, null);
123     }
124 
runExpectingFailure(String failure)125     void runExpectingFailure(String failure) throws DeviceNotAvailableException {
126         run(false, failure);
127     }
128 
run(boolean expectingSuccess, String failure)129     private void run(boolean expectingSuccess, String failure) throws DeviceNotAvailableException {
130         final ITestDevice device = mDevice;
131 
132         // Create an install session
133         final StringBuilder cmd = new StringBuilder();
134         cmd.append("pm install-create");
135         for (String arg : mArgs) {
136             cmd.append(' ').append(arg);
137         }
138         if (!mUseNaturalAbi && mAbi != null) {
139             cmd.append(' ').append(AbiUtils.createAbiFlag(mAbi.getName()));
140         }
141 
142         String result = device.executeShellCommand(cmd.toString());
143         TestCase.assertTrue(result, result.startsWith("Success"));
144 
145         final int start = result.lastIndexOf("[");
146         final int end = result.lastIndexOf("]");
147         int sessionId = -1;
148         try {
149             if (start != -1 && end != -1 && start < end) {
150                 sessionId = Integer.parseInt(result.substring(start + 1, end));
151             }
152         } catch (NumberFormatException e) {
153         }
154         if (sessionId == -1) {
155             throw new IllegalStateException("Failed to create install session: " + result);
156         }
157 
158         // Push our files into session. Ideally we'd use stdin streaming,
159         // but ddmlib doesn't support it yet.
160         for (int i = 0; i < mApks.size(); i++) {
161             final File apk = mApks.get(i);
162             final String remotePath = "/data/local/tmp/" + i + "_" + apk.getName();
163             if (!device.pushFile(apk, remotePath)) {
164                 throw new IllegalStateException("Failed to push " + apk);
165             }
166 
167             cmd.setLength(0);
168             cmd.append("pm install-write");
169             cmd.append(' ').append(sessionId);
170             cmd.append(' ').append(i + "_" + apk.getName());
171             cmd.append(' ').append(remotePath);
172 
173             result = device.executeShellCommand(cmd.toString());
174             TestCase.assertTrue(result, result.startsWith("Success"));
175         }
176 
177         for (int i = 0; i < mSplits.size(); i++) {
178             final String split = mSplits.get(i);
179 
180             cmd.setLength(0);
181             cmd.append("pm install-remove");
182             cmd.append(' ').append(sessionId);
183             cmd.append(' ').append(split);
184 
185             result = device.executeShellCommand(cmd.toString());
186             TestCase.assertTrue(result, result.startsWith("Success"));
187         }
188 
189         // Everything staged; let's pull trigger
190         cmd.setLength(0);
191         cmd.append("pm install-commit");
192         cmd.append(' ').append(sessionId);
193 
194         result = device.executeShellCommand(cmd.toString()).trim();
195         if (failure == null) {
196             if (expectingSuccess) {
197                 TestCase.assertTrue(result, result.startsWith("Success"));
198             } else {
199                 TestCase.assertFalse(result, result.startsWith("Success"));
200             }
201         } else {
202             TestCase.assertTrue(result, result.contains(failure));
203         }
204     }
205 }
206