1/*
2 * Copyright (C) 2010 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
17package android.content.pm;
18
19import com.android.ddmlib.AndroidDebugBridge;
20import com.android.ddmlib.IDevice;
21import com.android.ddmlib.IShellOutputReceiver;
22import com.android.ddmlib.Log;
23import com.android.ddmlib.MultiLineReceiver;
24import com.android.ddmlib.SyncService;
25import com.android.ddmlib.SyncService.ISyncProgressMonitor;
26import com.android.ddmlib.SyncService.SyncResult;
27import com.android.ddmlib.testrunner.ITestRunListener;
28import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
29import com.android.ddmlib.testrunner.TestIdentifier;
30import com.android.hosttest.DeviceTestCase;
31import com.android.hosttest.DeviceTestSuite;
32
33import java.io.BufferedReader;
34import java.io.File;
35import java.io.InputStreamReader;
36import java.io.IOException;
37import java.io.StringReader;
38import java.lang.Runtime;
39import java.lang.Process;
40import java.util.Hashtable;
41import java.util.Map;
42import java.util.Map.Entry;
43import java.util.regex.Matcher;
44import java.util.regex.Pattern;
45
46import junit.framework.Assert;
47import com.android.hosttest.DeviceTestCase;
48
49/**
50 * Set of tests that verify host side install cases
51 */
52public class PackageManagerHostTestUtils extends Assert {
53
54    private static final String LOG_TAG = "PackageManagerHostTests";
55    private IDevice mDevice = null;
56
57    // TODO: get this value from Android Environment instead of hardcoding
58    private static final String APP_PRIVATE_PATH = "/data/app-private/";
59    private static final String DEVICE_APP_PATH = "/data/app/";
60    private static final String SDCARD_APP_PATH = "/mnt/secure/asec/";
61
62    private static final int MAX_WAIT_FOR_DEVICE_TIME = 120 * 1000;
63    private static final int WAIT_FOR_DEVICE_POLL_TIME = 10 * 1000;
64    private static final int MAX_WAIT_FOR_APP_LAUNCH_TIME = 60 * 1000;
65    private static final int WAIT_FOR_APP_LAUNCH_POLL_TIME = 5 * 1000;
66
67    // Install preference on the device-side
68    public static enum InstallLocPreference {
69        AUTO,
70        INTERNAL,
71        EXTERNAL
72    }
73
74    // Actual install location
75    public static enum InstallLocation {
76        DEVICE,
77        SDCARD
78    }
79
80    /**
81     * Constructor takes the device to use
82     * @param the device to use when performing operations
83     */
84    public PackageManagerHostTestUtils(IDevice device)
85    {
86          mDevice = device;
87    }
88
89    /**
90     * Disable default constructor
91     */
92    private PackageManagerHostTestUtils() {}
93
94    /**
95     * Returns the path on the device of forward-locked apps.
96     *
97     * @return path of forward-locked apps on the device
98     */
99    public static String getAppPrivatePath() {
100        return APP_PRIVATE_PATH;
101    }
102
103    /**
104     * Returns the path on the device of normal apps.
105     *
106     * @return path of forward-locked apps on the device
107     */
108    public static String getDeviceAppPath() {
109        return DEVICE_APP_PATH;
110    }
111
112    /**
113     * Returns the path of apps installed on the SD card.
114     *
115     * @return path of forward-locked apps on the device
116     */
117    public static String getSDCardAppPath() {
118        return SDCARD_APP_PATH;
119    }
120
121    /**
122     * Helper method to run tests and return the listener that collected the results.
123     *
124     * For the optional params, pass null to use the default values.
125
126     * @param pkgName Android application package for tests
127     * @param className (optional) The class containing the method to test
128     * @param methodName (optional) The method in the class of which to test
129     * @param runnerName (optional) The name of the TestRunner of the test on the device to be run
130     * @param params (optional) Any additional parameters to pass into the Test Runner
131     * @return the {@link CollectingTestRunListener}
132     */
133    private CollectingTestRunListener doRunTests(String pkgName, String className, String
134            methodName, String runnerName, Map<String, String> params) throws IOException {
135
136        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName, runnerName,
137                mDevice);
138
139        if (className != null && methodName != null) {
140            testRunner.setMethodName(className, methodName);
141        }
142
143        // Add in any additional args to pass into the test
144        if (params != null) {
145            for (Entry<String, String> argPair : params.entrySet()) {
146                testRunner.addInstrumentationArg(argPair.getKey(), argPair.getValue());
147            }
148        }
149
150        CollectingTestRunListener listener = new CollectingTestRunListener();
151        testRunner.run(listener);
152        return listener;
153    }
154
155    /**
156     * Runs the specified packages tests, and returns whether all tests passed or not.
157     *
158     * @param pkgName Android application package for tests
159     * @param className The class containing the method to test
160     * @param methodName The method in the class of which to test
161     * @param runnerName The name of the TestRunner of the test on the device to be run
162     * @param params Any additional parameters to pass into the Test Runner
163     * @return true if test passed, false otherwise.
164     */
165    public boolean runDeviceTestsDidAllTestsPass(String pkgName, String className,
166            String methodName, String runnerName, Map<String, String> params) throws IOException {
167        CollectingTestRunListener listener = doRunTests(pkgName, className, methodName,
168                runnerName, params);
169        return listener.didAllTestsPass();
170    }
171
172    /**
173     * Runs the specified packages tests, and returns whether all tests passed or not.
174     *
175     * @param pkgName Android application package for tests
176     * @return true if every test passed, false otherwise.
177     */
178    public boolean runDeviceTestsDidAllTestsPass(String pkgName) throws IOException {
179        CollectingTestRunListener listener = doRunTests(pkgName, null, null, null, null);
180        return listener.didAllTestsPass();
181    }
182
183    /**
184     * Helper method to push a file to device
185     * @param apkAppPrivatePath
186     * @throws IOException
187     */
188    public void pushFile(final String localFilePath, final String destFilePath)
189            throws IOException {
190        SyncResult result = mDevice.getSyncService().pushFile(
191                localFilePath, destFilePath, new NullSyncProgressMonitor());
192        assertEquals(SyncService.RESULT_OK, result.getCode());
193    }
194
195    /**
196     * Helper method to install a file
197     * @param localFilePath the absolute file system path to file on local host to install
198     * @param reinstall set to <code>true</code> if re-install of app should be performed
199     * @throws IOException
200     */
201    public void installFile(final String localFilePath, final boolean replace) throws IOException {
202        String result = mDevice.installPackage(localFilePath, replace);
203        assertEquals(null, result);
204    }
205
206    /**
207     * Helper method to install a file that should not be install-able
208     * @param localFilePath the absolute file system path to file on local host to install
209     * @param reinstall set to <code>true</code> if re-install of app should be performed
210     * @return the string output of the failed install attempt
211     * @throws IOException
212     */
213    public String installFileFail(final String localFilePath, final boolean replace)
214            throws IOException {
215        String result = mDevice.installPackage(localFilePath, replace);
216        assertNotNull(result);
217        return result;
218    }
219
220    /**
221     * Helper method to install a file to device as forward locked
222     * @param localFilePath the absolute file system path to file on local host to install
223     * @param reinstall set to <code>true</code> if re-install of app should be performed
224     * @throws IOException
225     */
226    public String installFileForwardLocked(final String localFilePath, final boolean replace)
227            throws IOException {
228        String remoteFilePath = mDevice.syncPackageToDevice(localFilePath);
229        InstallReceiver receiver = new InstallReceiver();
230        String cmd = String.format(replace ? "pm install -r -l \"%1$s\"" :
231                "pm install -l \"%1$s\"", remoteFilePath);
232        mDevice.executeShellCommand(cmd, receiver);
233        mDevice.removeRemotePackage(remoteFilePath);
234        return receiver.getErrorMessage();
235    }
236
237    /**
238     * Helper method to determine if file on device exists.
239     *
240     * @param destPath the absolute path of file on device to check
241     * @return <code>true</code> if file exists, <code>false</code> otherwise.
242     * @throws IOException if adb shell command failed
243     */
244    public boolean doesRemoteFileExist(String destPath) throws IOException {
245        String lsGrep = executeShellCommand(String.format("ls %s", destPath));
246        return !lsGrep.contains("No such file or directory");
247    }
248
249    /**
250     * Helper method to determine if file exists on the device containing a given string.
251     *
252     * @param destPath the absolute path of the file
253     * @return <code>true</code> if file exists containing given string,
254     *         <code>false</code> otherwise.
255     * @throws IOException if adb shell command failed
256     */
257    public boolean doesRemoteFileExistContainingString(String destPath, String searchString)
258            throws IOException {
259        String lsResult = executeShellCommand(String.format("ls %s", destPath));
260        return lsResult.contains(searchString);
261    }
262
263    /**
264     * Helper method to determine if package on device exists.
265     *
266     * @param packageName the Android manifest package to check.
267     * @return <code>true</code> if package exists, <code>false</code> otherwise
268     * @throws IOException if adb shell command failed
269     */
270    public boolean doesPackageExist(String packageName) throws IOException {
271        String pkgGrep = executeShellCommand(String.format("pm path %s", packageName));
272        return pkgGrep.contains("package:");
273    }
274
275    /**
276     * Determines if app was installed on device.
277     *
278     * @param packageName package name to check for
279     * @return <code>true</code> if file exists, <code>false</code> otherwise.
280     * @throws IOException if adb shell command failed
281     */
282    public boolean doesAppExistOnDevice(String packageName) throws IOException {
283        return doesRemoteFileExistContainingString(DEVICE_APP_PATH, packageName);
284    }
285
286    /**
287     * Determines if app was installed on SD card.
288     *
289     * @param packageName package name to check for
290     * @return <code>true</code> if file exists, <code>false</code> otherwise.
291     * @throws IOException if adb shell command failed
292     */
293    public boolean doesAppExistOnSDCard(String packageName) throws IOException {
294        return doesRemoteFileExistContainingString(SDCARD_APP_PATH, packageName);
295    }
296
297    /**
298     * Helper method to determine if app was installed on SD card.
299     *
300     * @param packageName package name to check for
301     * @return <code>true</code> if file exists, <code>false</code> otherwise.
302     * @throws IOException if adb shell command failed
303     */
304    public boolean doesAppExistAsForwardLocked(String packageName) throws IOException {
305        return doesRemoteFileExistContainingString(APP_PRIVATE_PATH, packageName);
306    }
307
308    /**
309     * Waits for device's package manager to respond.
310     *
311     * @throws InterruptedException
312     * @throws IOException
313     */
314    public void waitForPackageManager() throws InterruptedException, IOException {
315        Log.i(LOG_TAG, "waiting for device");
316        int currentWaitTime = 0;
317        // poll the package manager until it returns something for android
318        while (!doesPackageExist("android")) {
319            Thread.sleep(WAIT_FOR_DEVICE_POLL_TIME);
320            currentWaitTime += WAIT_FOR_DEVICE_POLL_TIME;
321            if (currentWaitTime > MAX_WAIT_FOR_DEVICE_TIME) {
322                Log.e(LOG_TAG, "time out waiting for device");
323                throw new InterruptedException();
324            }
325        }
326    }
327
328    /**
329     * Helper to determine if the device is currently online and visible via ADB.
330     *
331     * @return true iff the device is currently available to ADB and online, false otherwise.
332     */
333    private boolean deviceIsOnline() {
334        AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
335        IDevice[] devices = bridge.getDevices();
336
337        for (IDevice device : devices) {
338            // only online if the device appears in the devices list, and its state is online
339            if ((mDevice != null) &&
340                    mDevice.getSerialNumber().equals(device.getSerialNumber()) &&
341                    device.isOnline()) {
342                return true;
343            }
344        }
345        return false;
346    }
347
348    /**
349     * Waits for device to be online (visible to ADB) before returning, or times out if we've
350     * waited too long. Note that this only means the device is visible via ADB, not that
351     * PackageManager is fully up and running yet.
352     *
353     * @throws InterruptedException
354     * @throws IOException
355     */
356    public void waitForDeviceToComeOnline() throws InterruptedException, IOException {
357        Log.i(LOG_TAG, "waiting for device to be online");
358        int currentWaitTime = 0;
359
360        // poll ADB until we see the device is online
361        while (!deviceIsOnline()) {
362            Thread.sleep(WAIT_FOR_DEVICE_POLL_TIME);
363            currentWaitTime += WAIT_FOR_DEVICE_POLL_TIME;
364            if (currentWaitTime > MAX_WAIT_FOR_DEVICE_TIME) {
365                Log.e(LOG_TAG, "time out waiting for device");
366                throw new InterruptedException();
367            }
368        }
369        // Note: if we try to access the device too quickly after it is "officially" online,
370        // there are sometimes strange issues where it's actually not quite ready yet,
371        // so we pause for a bit once more before actually returning.
372        Thread.sleep(WAIT_FOR_DEVICE_POLL_TIME);
373    }
374
375    /**
376     * Queries package manager and waits until a package is launched (or times out)
377     *
378     * @param packageName The name of the package to wait to load
379     * @throws InterruptedException
380     * @throws IOException
381     */
382    public void waitForApp(String packageName) throws InterruptedException, IOException {
383        Log.i(LOG_TAG, "waiting for app to launch");
384        int currentWaitTime = 0;
385        // poll the package manager until it returns something for the package we're looking for
386        while (!doesPackageExist(packageName)) {
387            Thread.sleep(WAIT_FOR_APP_LAUNCH_POLL_TIME);
388            currentWaitTime += WAIT_FOR_APP_LAUNCH_POLL_TIME;
389            if (currentWaitTime > MAX_WAIT_FOR_APP_LAUNCH_TIME) {
390                Log.e(LOG_TAG, "time out waiting for app to launch: " + packageName);
391                throw new InterruptedException();
392            }
393        }
394    }
395
396    /**
397     * Helper method which executes a adb shell command and returns output as a {@link String}
398     * @return the output of the command
399     * @throws IOException
400     */
401    public String executeShellCommand(String command) throws IOException {
402        Log.i(LOG_TAG, String.format("adb shell %s", command));
403        CollectingOutputReceiver receiver = new CollectingOutputReceiver();
404        mDevice.executeShellCommand(command, receiver);
405        String output = receiver.getOutput();
406        Log.i(LOG_TAG, String.format("Result: %s", output));
407        return output;
408    }
409
410    /**
411     * Helper method ensures we are in root mode on the host side. It returns only after
412     * PackageManager is actually up and running.
413     * @throws IOException
414     */
415    public void runAdbRoot() throws IOException, InterruptedException {
416        Log.i(LOG_TAG, "adb root");
417        Runtime runtime = Runtime.getRuntime();
418        Process process = runtime.exec("adb root"); // adb should be in the path
419        BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
420
421        String nextLine = null;
422        while (null != (nextLine = output.readLine())) {
423            Log.i(LOG_TAG, nextLine);
424        }
425        process.waitFor();
426        waitForDeviceToComeOnline();
427        waitForPackageManager(); // now wait for package manager to actually load
428    }
429
430    /**
431     * Helper method which reboots the device and returns once the device is online again
432     * and package manager is up and running (note this function is synchronous to callers).
433     * @throws IOException
434     * @throws InterruptedException
435     */
436    public void rebootDevice() throws IOException, InterruptedException {
437        String command = "reboot"; // no need for -s since mDevice is already tied to a device
438        Log.i(LOG_TAG, command);
439        CollectingOutputReceiver receiver = new CollectingOutputReceiver();
440        mDevice.executeShellCommand(command, receiver);
441        String output = receiver.getOutput();
442        Log.i(LOG_TAG, String.format("Result: %s", output));
443        waitForDeviceToComeOnline(); // wait for device to come online
444        runAdbRoot();
445    }
446
447    /**
448     * A {@link IShellOutputReceiver} which collects the whole shell output into one {@link String}
449     */
450    private class CollectingOutputReceiver extends MultiLineReceiver {
451
452        private StringBuffer mOutputBuffer = new StringBuffer();
453
454        public String getOutput() {
455            return mOutputBuffer.toString();
456        }
457
458        @Override
459        public void processNewLines(String[] lines) {
460            for (String line: lines) {
461                mOutputBuffer.append(line);
462                mOutputBuffer.append("\n");
463            }
464        }
465
466        public boolean isCancelled() {
467            return false;
468        }
469    }
470
471    private class NullSyncProgressMonitor implements ISyncProgressMonitor {
472        public void advance(int work) {
473            // ignore
474        }
475
476        public boolean isCanceled() {
477            // ignore
478            return false;
479        }
480
481        public void start(int totalWork) {
482            // ignore
483
484        }
485
486        public void startSubTask(String name) {
487            // ignore
488        }
489
490        public void stop() {
491            // ignore
492        }
493    }
494
495    // For collecting results from running device tests
496    public static class CollectingTestRunListener implements ITestRunListener {
497
498        private boolean mAllTestsPassed = true;
499        private String mTestRunErrorMessage = null;
500
501        public void testEnded(TestIdentifier test) {
502            // ignore
503        }
504
505        public void testFailed(TestFailure status, TestIdentifier test,
506                String trace) {
507            Log.w(LOG_TAG, String.format("%s#%s failed: %s", test.getClassName(),
508                    test.getTestName(), trace));
509            mAllTestsPassed = false;
510        }
511
512        public void testRunEnded(long elapsedTime) {
513            // ignore
514        }
515
516        public void testRunFailed(String errorMessage) {
517            Log.w(LOG_TAG, String.format("test run failed: %s", errorMessage));
518            mAllTestsPassed = false;
519            mTestRunErrorMessage = errorMessage;
520        }
521
522        public void testRunStarted(int testCount) {
523            // ignore
524        }
525
526        public void testRunStopped(long elapsedTime) {
527            // ignore
528        }
529
530        public void testStarted(TestIdentifier test) {
531            // ignore
532        }
533
534        boolean didAllTestsPass() {
535            return mAllTestsPassed;
536        }
537
538        /**
539         * Get the test run failure error message.
540         * @return the test run failure error message or <code>null</code> if test run completed.
541         */
542        String getTestRunErrorMessage() {
543            return mTestRunErrorMessage;
544        }
545    }
546
547    /**
548     * Output receiver for "pm install package.apk" command line.
549     *
550     */
551    private static final class InstallReceiver extends MultiLineReceiver {
552
553        private static final String SUCCESS_OUTPUT = "Success"; //$NON-NLS-1$
554        private static final Pattern FAILURE_PATTERN = Pattern.compile("Failure\\s+\\[(.*)\\]"); //$NON-NLS-1$
555
556        private String mErrorMessage = null;
557
558        public InstallReceiver() {
559        }
560
561        @Override
562        public void processNewLines(String[] lines) {
563            for (String line : lines) {
564                if (line.length() > 0) {
565                    if (line.startsWith(SUCCESS_OUTPUT)) {
566                        mErrorMessage = null;
567                    } else {
568                        Matcher m = FAILURE_PATTERN.matcher(line);
569                        if (m.matches()) {
570                            mErrorMessage = m.group(1);
571                        }
572                    }
573                }
574            }
575        }
576
577        public boolean isCancelled() {
578            return false;
579        }
580
581        public String getErrorMessage() {
582            return mErrorMessage;
583        }
584    }
585
586    /**
587     * Helper method for installing an app to wherever is specified in its manifest, and
588     * then verifying the app was installed onto SD Card.
589     *
590     * @param the path of the apk to install
591     * @param the name of the package
592     * @param true</code> if the app should be overwritten, <code>false</code> otherwise
593     * @throws IOException if adb shell command failed
594     * @throws InterruptedException if the thread was interrupted
595     * <p/>
596     * Assumes adb is running as root in device under test.
597     */
598    public void installAppAndVerifyExistsOnSDCard(String apkPath, String pkgName, boolean overwrite)
599            throws IOException, InterruptedException {
600        // Start with a clean slate if we're not overwriting
601        if (!overwrite) {
602            // cleanup test app just in case it already exists
603            mDevice.uninstallPackage(pkgName);
604            // grep for package to make sure its not installed
605            assertFalse(doesPackageExist(pkgName));
606        }
607
608        installFile(apkPath, overwrite);
609        assertTrue(doesAppExistOnSDCard(pkgName));
610        assertFalse(doesAppExistOnDevice(pkgName));
611        waitForPackageManager();
612
613        // grep for package to make sure it is installed
614        assertTrue(doesPackageExist(pkgName));
615    }
616
617    /**
618     * Helper method for installing an app to wherever is specified in its manifest, and
619     * then verifying the app was installed onto device.
620     *
621     * @param the path of the apk to install
622     * @param the name of the package
623     * @param true</code> if the app should be overwritten, <code>false</code> otherwise
624     * @throws IOException if adb shell command failed
625     * @throws InterruptedException if the thread was interrupted
626     * <p/>
627     * Assumes adb is running as root in device under test.
628     */
629    public void installAppAndVerifyExistsOnDevice(String apkPath, String pkgName, boolean overwrite)
630            throws IOException, InterruptedException {
631        // Start with a clean slate if we're not overwriting
632        if (!overwrite) {
633            // cleanup test app just in case it already exists
634            mDevice.uninstallPackage(pkgName);
635            // grep for package to make sure its not installed
636            assertFalse(doesPackageExist(pkgName));
637        }
638
639        installFile(apkPath, overwrite);
640        assertFalse(doesAppExistOnSDCard(pkgName));
641        assertTrue(doesAppExistOnDevice(pkgName));
642        waitForPackageManager();
643
644        // grep for package to make sure it is installed
645        assertTrue(doesPackageExist(pkgName));
646    }
647
648    /**
649     * Helper method for installing an app as forward-locked, and
650     * then verifying the app was installed in the proper forward-locked location.
651     *
652     * @param the path of the apk to install
653     * @param the name of the package
654     * @param true</code> if the app should be overwritten, <code>false</code> otherwise
655     * @throws IOException if adb shell command failed
656     * @throws InterruptedException if the thread was interrupted
657     * <p/>
658     * Assumes adb is running as root in device under test.
659     */
660    public void installFwdLockedAppAndVerifyExists(String apkPath,
661            String pkgName, boolean overwrite) throws IOException, InterruptedException {
662        // Start with a clean slate if we're not overwriting
663        if (!overwrite) {
664            // cleanup test app just in case it already exists
665            mDevice.uninstallPackage(pkgName);
666            // grep for package to make sure its not installed
667            assertFalse(doesPackageExist(pkgName));
668        }
669
670        String result = installFileForwardLocked(apkPath, overwrite);
671        assertEquals(null, result);
672        assertTrue(doesAppExistAsForwardLocked(pkgName));
673        assertFalse(doesAppExistOnSDCard(pkgName));
674        waitForPackageManager();
675
676        // grep for package to make sure it is installed
677        assertTrue(doesPackageExist(pkgName));
678    }
679
680    /**
681     * Helper method for uninstalling an app.
682     *
683     * @param pkgName package name to uninstall
684     * @throws IOException if adb shell command failed
685     * @throws InterruptedException if the thread was interrupted
686     * <p/>
687     * Assumes adb is running as root in device under test.
688     */
689    public void uninstallApp(String pkgName) throws IOException, InterruptedException {
690        mDevice.uninstallPackage(pkgName);
691        // make sure its not installed anymore
692        assertFalse(doesPackageExist(pkgName));
693    }
694
695    /**
696     * Helper method for clearing any installed non-system apps.
697     * Useful ensuring no non-system apps are installed, and for cleaning up stale files that
698     * may be lingering on the system for whatever reason.
699     *
700     * @throws IOException if adb shell command failed
701     * <p/>
702     * Assumes adb is running as root in device under test.
703     */
704    public void wipeNonSystemApps() throws IOException {
705      String allInstalledPackages = executeShellCommand("pm list packages -f");
706      BufferedReader outputReader = new BufferedReader(new StringReader(allInstalledPackages));
707
708      // First use Package Manager to uninstall all non-system apps
709      String currentLine = null;
710      while ((currentLine = outputReader.readLine()) != null) {
711          // Skip over any system apps...
712          if (currentLine.contains("/system/")) {
713              continue;
714          }
715          String packageName = currentLine.substring(currentLine.indexOf('=') + 1);
716          mDevice.uninstallPackage(packageName);
717      }
718      // Make sure there are no stale app files under these directories
719      executeShellCommand(String.format("rm %s*", SDCARD_APP_PATH, "*"));
720      executeShellCommand(String.format("rm %s*", DEVICE_APP_PATH, "*"));
721      executeShellCommand(String.format("rm %s*", APP_PRIVATE_PATH, "*"));
722    }
723
724    /**
725     * Sets the device's install location preference.
726     *
727     * <p/>
728     * Assumes adb is running as root in device under test.
729     */
730    public void setDevicePreferredInstallLocation(InstallLocPreference pref) throws IOException {
731        String command = "pm setInstallLocation %d";
732        int locValue = 0;
733        switch (pref) {
734            case INTERNAL:
735                locValue = 1;
736                break;
737            case EXTERNAL:
738                locValue = 2;
739                break;
740            default: // AUTO
741                locValue = 0;
742                break;
743        }
744        executeShellCommand(String.format(command, locValue));
745    }
746
747    /**
748     * Gets the device's install location preference.
749     *
750     * <p/>
751     * Assumes adb is running as root in device under test.
752     */
753    public InstallLocPreference getDevicePreferredInstallLocation() throws IOException {
754        String result = executeShellCommand("pm getInstallLocation");
755        if (result.indexOf('0') != -1) {
756            return InstallLocPreference.AUTO;
757        }
758        else if (result.indexOf('1') != -1) {
759            return InstallLocPreference.INTERNAL;
760        }
761        else {
762            return InstallLocPreference.EXTERNAL;
763        }
764    }
765}
766