• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     static class DeviceFile {
49         public final File localFile;
50         public final File remoteFile;
51         public final boolean addToInstallSession;
52 
DeviceFile(File localFile, File remoteFile, boolean addToInstallSession)53         private DeviceFile(File localFile, File remoteFile, boolean addToInstallSession) {
54             this.localFile = localFile;
55             this.remoteFile = remoteFile;
56             this.addToInstallSession = addToInstallSession;
57         }
58 
addToSession(File file)59         static DeviceFile addToSession(File file) {
60             return new DeviceFile(file, file, true);
61         }
62 
renameAndAddToSession(File localFile, File remoteFile)63         static DeviceFile renameAndAddToSession(File localFile, File remoteFile) {
64             return new DeviceFile(localFile, remoteFile, true);
65         }
66 
pushOnly(File file)67         static DeviceFile pushOnly(File file) {
68             return new DeviceFile(file, file, false);
69         }
70 
renameAndPushOnly(File localFile, File remoteFile)71         static DeviceFile renameAndPushOnly(File localFile, File remoteFile) {
72             return new DeviceFile(localFile, remoteFile, false);
73         }
74     }
75 
76     private final List<String> mArgs = new ArrayList<>();
77     private final List<DeviceFile> mFilesToAdd = new ArrayList<>();
78     private final List<String> mSplitsToRemove = new ArrayList<>();
79     private boolean mUseNaturalAbi = false;
80     private boolean mUseIncremental = false;
81 
BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo, IAbi abi)82     public BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo, IAbi abi) {
83         this(device, buildInfo, abi, true);
84     }
85 
BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo, IAbi abi, boolean grantPermissions)86     public BaseInstallMultiple(ITestDevice device, IBuildInfo buildInfo, IAbi abi,
87             boolean grantPermissions) {
88         mDevice = device;
89         mBuild = buildInfo;
90         mAbi = abi;
91         if (grantPermissions) {
92             addArg("-g");
93         }
94         // Allow the install of test apps
95         addArg("-t");
96     }
97 
addArg(String arg)98     T addArg(String arg) {
99         mArgs.add(arg);
100         return (T) this;
101     }
102 
addFile(String file)103     T addFile(String file) throws FileNotFoundException {
104         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
105         mFilesToAdd.add(DeviceFile.addToSession(buildHelper.getTestFile(file, mAbi)));
106         return (T) this;
107     }
108 
renameAndAddFile(String localFile, String remoteFile)109     T renameAndAddFile(String localFile, String remoteFile) throws FileNotFoundException {
110         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
111         mFilesToAdd.add(DeviceFile.renameAndAddToSession(buildHelper.getTestFile(localFile, mAbi),
112                 buildHelper.getTestFile(remoteFile, mAbi)));
113         return (T) this;
114     }
115 
pushFile(String file)116     T pushFile(String file) throws FileNotFoundException {
117         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
118         mFilesToAdd.add(DeviceFile.pushOnly(buildHelper.getTestFile(file, mAbi)));
119         return (T) this;
120     }
121 
renameAndPushFile(String localFile, String remoteFile)122     T renameAndPushFile(String localFile, String remoteFile) throws FileNotFoundException {
123         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
124         mFilesToAdd.add(DeviceFile.renameAndPushOnly(buildHelper.getTestFile(localFile, mAbi),
125                 buildHelper.getTestFile(remoteFile, mAbi)));
126         return (T) this;
127     }
128 
removeSplit(String split)129     T removeSplit(String split) {
130         mSplitsToRemove.add(split);
131         return (T) this;
132     }
133 
inheritFrom(String packageName)134     T inheritFrom(String packageName) {
135         addArg("-r");
136         addArg("-p " + packageName);
137         return (T) this;
138     }
139 
useNaturalAbi()140     T useNaturalAbi() {
141         mUseNaturalAbi = true;
142         return (T) this;
143     }
144 
useIncremental()145     T useIncremental() {
146         mUseIncremental = true;
147         return (T) this;
148     }
149 
allowTest()150     T allowTest() {
151         addArg("-t");
152         return (T) this;
153     }
154 
locationAuto()155     T locationAuto() {
156         addArg("--install-location 0");
157         return (T) this;
158     }
159 
locationInternalOnly()160     T locationInternalOnly() {
161         addArg("--install-location 1");
162         return (T) this;
163     }
164 
locationPreferExternal()165     T locationPreferExternal() {
166         addArg("--install-location 2");
167         return (T) this;
168     }
169 
forceUuid(String uuid)170     T forceUuid(String uuid) {
171         addArg("--force-uuid " + uuid);
172         return (T) this;
173     }
174 
forUser(int userId)175     T forUser(int userId) {
176         addArg("--user " + userId);
177         return (T) this;
178     }
179 
restrictPermissions()180     T restrictPermissions() {
181         addArg("--restrict-permissions");
182         return (T) this;
183     }
184 
deriveRemoteName(String originalName, int index)185     protected String deriveRemoteName(String originalName, int index) {
186         return index + "_" + originalName;
187     }
188 
run()189     void run() throws DeviceNotAvailableException {
190         run(true, null);
191     }
192 
run(boolean expectingSuccess)193     void run(boolean expectingSuccess) throws DeviceNotAvailableException {
194         run(expectingSuccess, null);
195     }
196 
runExpectingFailure()197     void runExpectingFailure() throws DeviceNotAvailableException {
198         run(false, null);
199     }
200 
runExpectingFailure(String failure)201     void runExpectingFailure(String failure) throws DeviceNotAvailableException {
202         run(false, failure);
203     }
204 
run(boolean expectingSuccess, String failure)205     private void run(boolean expectingSuccess, String failure) throws DeviceNotAvailableException {
206         if (mUseIncremental) {
207             runIncremental(expectingSuccess, failure);
208         } else {
209             runNonIncremental(expectingSuccess, failure);
210         }
211         cleanupDeviceFiles();
212     }
213 
runNonIncremental(boolean expectingSuccess, String failure)214     private void runNonIncremental(boolean expectingSuccess, String failure)
215             throws DeviceNotAvailableException {
216         final ITestDevice device = mDevice;
217 
218         // Create an install session
219         final StringBuilder cmd = new StringBuilder();
220         cmd.append("pm install-create");
221         for (String arg : mArgs) {
222             cmd.append(' ').append(arg);
223         }
224         if (!mUseNaturalAbi && mAbi != null) {
225             cmd.append(' ').append(AbiUtils.createAbiFlag(mAbi.getName()));
226         }
227 
228         String result = device.executeShellCommand(cmd.toString());
229         TestCase.assertTrue(result, result.startsWith("Success"));
230 
231         final int start = result.lastIndexOf("[");
232         final int end = result.lastIndexOf("]");
233         int sessionId = -1;
234         try {
235             if (start != -1 && end != -1 && start < end) {
236                 sessionId = Integer.parseInt(result.substring(start + 1, end));
237             }
238         } catch (NumberFormatException e) {
239         }
240         if (sessionId == -1) {
241             throw new IllegalStateException("Failed to create install session: " + result);
242         }
243 
244         // Push our files into session. Ideally we'd use stdin streaming,
245         // but ddmlib doesn't support it yet.
246         for (int i = 0; i < mFilesToAdd.size(); i++) {
247             final File localFile = mFilesToAdd.get(i).localFile;
248             final File remoteFile = mFilesToAdd.get(i).remoteFile;
249             final String remoteName = deriveRemoteName(remoteFile.getName(), i);
250             final String remotePath = "/data/local/tmp/" + remoteName;
251             if (!device.pushFile(localFile, remotePath)) {
252                 throw new IllegalStateException("Failed to push " + localFile);
253             }
254 
255             if (!mFilesToAdd.get(i).addToInstallSession) {
256                 continue;
257             }
258 
259             cmd.setLength(0);
260             cmd.append("pm install-write");
261             cmd.append(' ').append(sessionId);
262             cmd.append(' ').append(remoteName);
263             cmd.append(' ').append(remotePath);
264 
265             result = device.executeShellCommand(cmd.toString());
266             TestCase.assertTrue(result, result.startsWith("Success"));
267         }
268 
269         for (int i = 0; i < mSplitsToRemove.size(); i++) {
270             final String split = mSplitsToRemove.get(i);
271 
272             cmd.setLength(0);
273             cmd.append("pm install-remove");
274             cmd.append(' ').append(sessionId);
275             cmd.append(' ').append(split);
276 
277             result = device.executeShellCommand(cmd.toString());
278             TestCase.assertTrue(result, result.startsWith("Success"));
279         }
280 
281         // Everything staged; let's pull trigger
282         cmd.setLength(0);
283         cmd.append("pm install-commit");
284         cmd.append(' ').append(sessionId);
285 
286         result = device.executeShellCommand(cmd.toString()).trim();
287         if (failure == null) {
288             if (expectingSuccess) {
289                 TestCase.assertTrue(result, result.startsWith("Success"));
290             } else {
291                 TestCase.assertFalse(result, result.startsWith("Success"));
292             }
293         } else {
294             TestCase.assertTrue(result, result.contains(failure));
295         }
296     }
297 
runIncremental(boolean expectingSuccess, String failure)298     private void runIncremental(boolean expectingSuccess, String failure) throws DeviceNotAvailableException {
299         final ITestDevice device = mDevice;
300 
301         if (!mSplitsToRemove.isEmpty()) {
302             throw new IllegalStateException("Incremental sessions can't remove splits");
303         }
304 
305         // Create an install session
306         final StringBuilder cmd = new StringBuilder();
307         cmd.append("pm install-incremental");
308         for (String arg : mArgs) {
309             cmd.append(' ').append(arg);
310         }
311         if (!mUseNaturalAbi && mAbi != null) {
312             cmd.append(' ').append(AbiUtils.createAbiFlag(mAbi.getName()));
313         }
314 
315         // Push our files into session. Ideally we'd use stdin streaming,
316         // but ddmlib doesn't support it yet.
317         for (int i = 0; i < mFilesToAdd.size(); i++) {
318             final File localFile = mFilesToAdd.get(i).localFile;
319             final File remoteFile = mFilesToAdd.get(i).remoteFile;
320             final String remoteName = deriveRemoteName(remoteFile.getName(), i);
321             final String remotePath = "/data/local/tmp/" + remoteName;
322             if (!device.pushFile(localFile, remotePath)) {
323                 throw new IllegalStateException("Failed to push " + localFile);
324             }
325 
326             if (!mFilesToAdd.get(i).addToInstallSession) {
327                 continue;
328             }
329 
330             cmd.append(' ').append(remotePath);
331         }
332 
333         // Everything staged; let's pull trigger
334         String result = device.executeShellCommand(cmd.toString()).trim();
335         if (failure == null) {
336             if (expectingSuccess) {
337                 TestCase.assertTrue(result, result.startsWith("Success"));
338             } else {
339                 TestCase.assertFalse(result, result.startsWith("Success"));
340             }
341         } else {
342             TestCase.assertTrue(result, result.contains(failure));
343         }
344     }
345 
cleanupDeviceFiles()346     private void cleanupDeviceFiles() throws DeviceNotAvailableException {
347         final ITestDevice device = mDevice;
348         for (int i = 0; i < mFilesToAdd.size(); i++) {
349             final File remoteFile = mFilesToAdd.get(i).remoteFile;
350             final String remoteName = deriveRemoteName(remoteFile.getName(), i);
351             final String remotePath = "/data/local/tmp/" + remoteName;
352             device.deleteFile(remotePath);
353         }
354     }
355 }
356