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.functional.externalstoragetests;
18 
19 import android.app.Instrumentation;
20 import android.app.UiAutomation;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.content.pm.PackageManager.NameNotFoundException;
26 import android.os.ParcelFileDescriptor;
27 import android.os.StatFs;
28 import android.os.SystemClock;
29 import android.provider.Settings;
30 import android.support.test.uiautomator.By;
31 import android.support.test.uiautomator.UiDevice;
32 import android.support.test.uiautomator.UiObject2;
33 import android.support.test.uiautomator.Until;
34 import android.util.Log;
35 
36 import android.platform.test.helpers.PlayStoreHelperImpl;
37 
38 import junit.framework.Assert;
39 
40 import java.io.BufferedReader;
41 import java.io.File;
42 import java.io.FileInputStream;
43 import java.io.IOException;
44 import java.io.InputStreamReader;
45 import java.util.HashMap;
46 import java.util.Hashtable;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.regex.Matcher;
50 import java.util.regex.Pattern;
51 
52 public class ExternalStorageHelper {
53     public static final String TEST_TAG = "StorageFunctionalTest";
54     public final String SETTINGS_PKG = "com.android.settings";
55     public final String PLAYSTORE_PKG = "com.android.vending";
56     public final String DOCUMENTS_PKG = "com.android.documentsui";
57     public static final Map<String, String> APPLIST = new HashMap<String, String>();
58     static {
59         APPLIST.put("w35location1", "com.test.w35location1");
60         APPLIST.put("w35location2", "com.test.w35location2");
61         APPLIST.put("w35location3", "com.test.w35location3");
62     }
63     public final int TIMEOUT = 2000;
64     public static ExternalStorageHelper mInstance = null;
65     public UiDevice mDevice;
66     public Context mContext;
67     public static UiAutomation mUiAutomation;
68     public static Instrumentation mInstrumentation;
69     public static Hashtable<String, List<String>> mPermissionGroupInfo = null;
70 
ExternalStorageHelper(UiDevice device, Context context, UiAutomation uiAutomation, Instrumentation instrumentation)71     public ExternalStorageHelper(UiDevice device, Context context, UiAutomation uiAutomation,
72             Instrumentation instrumentation) {
73         mDevice = device;
74         mContext = context;
75         mUiAutomation = uiAutomation;
76         mInstrumentation = instrumentation;
77     }
78 
getInstance(UiDevice device, Context context, UiAutomation uiAutomation, Instrumentation instrumentation)79     public static ExternalStorageHelper getInstance(UiDevice device, Context context,
80             UiAutomation uiAutomation, Instrumentation instrumentation) {
81         if (mInstance == null) {
82             mInstance = new ExternalStorageHelper(device, context, uiAutomation, instrumentation);
83         }
84         return mInstance;
85     }
86 
87     /**
88      * Opens SD card setup notification from homescreen
89      */
openSdCardSetUpNotification()90     public UiObject2 openSdCardSetUpNotification() throws InterruptedException {
91         boolean success = mDevice.openNotification();
92         Thread.sleep(TIMEOUT);
93         UiObject2 sdCardDetected = mDevice
94                 .wait(Until.findObject(By.textContains("SD card detected")), TIMEOUT);
95         Assert.assertNotNull(sdCardDetected);
96         return sdCardDetected;
97     }
98 
99     /**
100      * Open Storage settings, then SD Card
101      */
openStorageSettings()102     public void openStorageSettings() throws InterruptedException {
103         Intent intent = new Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS);
104         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
105         mContext.startActivity(intent);
106         Thread.sleep(TIMEOUT * 2);
107     }
108 
109     /**
110      * Open Storage settings, then SD Card
111      */
openSDCard()112     public void openSDCard() throws InterruptedException {
113         openStorageSettings();
114         mDevice.wait(Until.findObject(By.textContains("SD card")), TIMEOUT)
115                 .clickAndWait(Until.newWindow(), TIMEOUT);
116     }
117 
executeShellCommand(String command)118     public String executeShellCommand(String command) {
119         ParcelFileDescriptor pfd = mUiAutomation.executeShellCommand(command);
120         try (BufferedReader reader = new BufferedReader(
121                 new InputStreamReader(new FileInputStream(pfd.getFileDescriptor())))) {
122             String str = reader.readLine();
123             Log.d(TEST_TAG, String.format("Executing command: %s", command));
124             return str;
125         } catch (IOException e) {
126             Log.e(TEST_TAG, e.getMessage());
127         }
128 
129         return null;
130     }
131 
132     /**
133      * Create # of files in a given dir
134      */
createFiles(int numberOfFiles, String dir)135     public void createFiles(int numberOfFiles, String dir) {
136         for (int i = 0; i < numberOfFiles; ++i) {
137             if (!new File(String.format("%s/Test_%d", dir, i)).exists()) {
138                 fillInStorage(dir, String.format("Test_%d", i), 1);
139             }
140         }
141     }
142 
fillInStorage(String location, String filename, int sizeInKb)143     public void fillInStorage(String location, String filename, int sizeInKb) {
144         executeShellCommand(String.format("dd if=/dev/zero of=%s/%s bs=1024 count=%d",
145                 location, filename, sizeInKb));
146     }
147 
getFreeSpaceSize(File path)148     public int getFreeSpaceSize(File path) {
149         StatFs stat = new StatFs(path.getPath());
150         long blockSize = stat.getBlockSize();
151         long availableBlocks = stat.getAvailableBlocks();
152         return (int) ((availableBlocks * blockSize) / (1024 * 1024));
153     }
154 
hasAdoptable()155     public boolean hasAdoptable() {
156         return Boolean.parseBoolean(executeShellCommand("sm has-adoptable").trim());
157     }
158 
getAdoptionDisk()159     public String getAdoptionDisk() throws InterruptedException {
160         int counter = 10;
161         String disks = null;
162         while (((disks == null || disks.length() == 0)) && counter > 0) {
163             disks = executeShellCommand("sm list-disks adoptable");
164             Thread.sleep(TIMEOUT);
165             --counter;
166         }
167         if (counter == 0) {
168             throw new AssertionError("Devices must have adoptable media inserted");
169         }
170         return disks.split("\n")[0].trim();
171     }
172 
hasPublicVolume()173     public Boolean hasPublicVolume() {
174         return (null != executeShellCommand("sm list-volumes public"));
175     }
176 
getAdoptionVolumeId(String volType)177     public String getAdoptionVolumeId(String volType) throws InterruptedException {
178         return getAdoptionVolumeInfo(volType).volId;
179     }
180 
getAdoptionVolumeUuid(String volType)181     public String getAdoptionVolumeUuid(String volType) throws InterruptedException {
182         return getAdoptionVolumeInfo(volType).uuid;
183     }
184 
getAdoptionVolumeInfo(String volType)185     public LocalVolumeInfo getAdoptionVolumeInfo(String volType) throws InterruptedException {
186         String[] lines = null;
187         int attempt = 0;
188         while (attempt++ < 5) {
189             if (null != (lines = executeShellCommand("sm list-volumes " + volType).split("\n"))) {
190                 for (String line : lines) {
191                     final LocalVolumeInfo info = new LocalVolumeInfo(line.trim());
192                     if (info.volId.startsWith(volType) && "mounted".equals(info.state)) {
193                         return info;
194                     }
195                 }
196                 Thread.sleep(TIMEOUT);
197             }
198         }
199         return null;
200     }
201 
partitionDisk(String type)202     public void partitionDisk(String type) throws InterruptedException {
203         if (type.equals("private")) {
204             executeShellCommand(String.format("sm partition %s %s", getAdoptionDisk(), type));
205             Thread.sleep(2 * TIMEOUT);
206         } else if (!hasPublicVolume() && type.equals("public")) {
207             executeShellCommand("sm forget all");
208             executeShellCommand(String.format("sm partition %s %s", getAdoptionDisk(), type));
209             Thread.sleep(2 * TIMEOUT);
210             setupAsPortableUiFlow();
211         }
212     }
213 
setupAsPortableUiFlow()214     public void setupAsPortableUiFlow() throws InterruptedException {
215         openSdCardSetUpNotification();
216         Thread.sleep(TIMEOUT);
217         Pattern pattern = Pattern.compile("Set up", Pattern.CASE_INSENSITIVE);
218         UiObject2 adoptFlowUi = mDevice.wait(Until.findObject(By.desc(pattern)), TIMEOUT);
219         adoptFlowUi.clickAndWait(Until.newWindow(), TIMEOUT);
220         adoptFlowUi = mDevice.wait(Until.findObject(
221                 By.res(SETTINGS_PKG, "storage_wizard_init_external_title")),
222                 TIMEOUT);
223         adoptFlowUi.click();
224         pattern = Pattern.compile("Next", Pattern.CASE_INSENSITIVE);
225         adoptFlowUi = mDevice.wait(Until.findObject(By.text(pattern)),
226                 TIMEOUT);
227         adoptFlowUi.clickAndWait(Until.newWindow(), TIMEOUT);
228         pattern = Pattern.compile("Done", Pattern.CASE_INSENSITIVE);
229         mDevice.wait(Until.findObject(By.text(pattern)), TIMEOUT).clickAndWait(
230                 Until.newWindow(), TIMEOUT);
231         hasPublicVolume();
232 
233     }
234 
installFromPlayStore(String appName)235     public void installFromPlayStore(String appName) {
236         PlayStoreHelperImpl mHelper = new PlayStoreHelperImpl(mInstrumentation);
237         mHelper.open();
238         mHelper.doSearch(appName);
239         mHelper.selectFirstResult();
240         mDevice.wait(Until.findObject(By.res(PLAYSTORE_PKG, "buy_button").text("INSTALL")),
241                 TIMEOUT).clickAndWait(Until.newWindow(), 2 * TIMEOUT);
242         SystemClock.sleep(2 * TIMEOUT);
243         mDevice.wait(Until.findObject(By.res(PLAYSTORE_PKG, "launch_button").text("OPEN")),
244                 5 * TIMEOUT);
245     }
246 
getPackageInfo(String packageName)247     public PackageInfo getPackageInfo(String packageName) throws NameNotFoundException {
248         return mContext.getPackageManager().getPackageInfo(packageName, 0);
249     }
250 
doesPackageExist(String packageName)251     public Boolean doesPackageExist(String packageName) throws NameNotFoundException {
252         try {
253             mContext.getPackageManager().getPackageInfo(packageName, 0);
254         } catch (NameNotFoundException nex) {
255             throw nex;
256         }
257 
258         return Boolean.TRUE;
259     }
260 
getInstalledLocation(String packageName)261     public String getInstalledLocation(String packageName) throws NameNotFoundException {
262         Assert.assertTrue(String.format("%s doesn't exist!", packageName),
263                 doesPackageExist(packageName));
264         return getPackageInfo(packageName).applicationInfo.dataDir;
265     }
266 
settingsUiCleanUp()267     public void settingsUiCleanUp() {
268         executeShellCommand("pm clear " + SETTINGS_PKG);
269         executeShellCommand("pm clear " + DOCUMENTS_PKG);
270     }
271 
272     private static class LocalVolumeInfo {
273         public String volId;
274         public String state;
275         public String uuid;
276 
LocalVolumeInfo(String line)277         public LocalVolumeInfo(String line) {
278             final String[] split = line.split(" ");
279             volId = split[0];
280             state = split[1];
281             uuid = split[2];
282         }
283     }
284 
getPackageManager()285     public PackageManager getPackageManager() {
286         return mContext.getPackageManager();
287     }
288 }
289