1 /* 2 * Copyright (C) 2022 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 android.os.storage.cts; 18 19 import android.os.ParcelFileDescriptor; 20 import android.util.Log; 21 22 import androidx.test.platform.app.InstrumentationRegistry; 23 24 import com.google.common.collect.Iterables; 25 26 import java.io.BufferedReader; 27 import java.io.FileInputStream; 28 import java.io.InputStream; 29 import java.io.InputStreamReader; 30 import java.nio.charset.StandardCharsets; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.concurrent.TimeoutException; 34 import java.util.function.Supplier; 35 36 final class StorageManagerHelper { 37 38 /** 39 * Creates a virtual disk that simulates SDCard on a device. It is 40 * mounted as a public visible disk. 41 * @return the volume name of the disk just created 42 * @throws Exception, if the volume could not be created 43 */ createSDCardVirtualDisk()44 static String createSDCardVirtualDisk() throws Exception { 45 return createDiskAndGetVolumeName(true); 46 } 47 /** 48 * Creates a virtual disk that simulates USB on a device. It is 49 * mounted as a public invisible disk. 50 * @return the volume name of the disk just created 51 * @throws Exception, if the volume could not be created 52 */ createUSBVirtualDisk()53 static String createUSBVirtualDisk() throws Exception { 54 return createDiskAndGetVolumeName(false); 55 } 56 57 /** 58 * Removes the simulated disk 59 */ removeVirtualDisk()60 static void removeVirtualDisk() throws Exception { 61 executeShellCommand("sm set-virtual-disk false"); 62 //sleep to make sure that it is unmounted 63 Thread.sleep(5000); 64 } 65 66 /** 67 * Create a public volume for testing and only return the one newly created as the volumeName. 68 */ createDiskAndGetVolumeName(boolean visible)69 public static String createDiskAndGetVolumeName(boolean visible) throws Exception { 70 //remove any existing volume that was mounted before 71 removeVirtualDisk(); 72 String existingPublicVolume = getPublicVolumeExcluding(null); 73 executeShellCommand("sm set-force-adoptable " + (visible ? "on" : "off")); 74 executeShellCommand("sm set-virtual-disk true"); 75 Thread.sleep(10000); 76 pollForCondition(StorageManagerHelper::partitionDisks, 77 "Could not create public volume in time"); 78 return getPublicVolumeExcluding(existingPublicVolume); 79 } 80 partitionDisks()81 private static boolean partitionDisks() { 82 try { 83 List<String> diskNames = executeShellCommand("sm list-disks"); 84 if (!diskNames.isEmpty()) { 85 executeShellCommand("sm partition " + Iterables.getLast(diskNames) + " public"); 86 return true; 87 } 88 } catch (Exception ignored) { 89 //ignored 90 } 91 return false; 92 } 93 pollForCondition(Supplier<Boolean> condition, String errorMessage)94 private static void pollForCondition(Supplier<Boolean> condition, String errorMessage) 95 throws Exception { 96 Thread.sleep(2000); 97 for (int i = 0; i < 20; i++) { 98 if (condition.get()) { 99 return; 100 } 101 Thread.sleep(100); 102 } 103 throw new TimeoutException(errorMessage); 104 } 105 getPublicVolumeExcluding(String excludingVolume)106 private static String getPublicVolumeExcluding(String excludingVolume) throws Exception { 107 108 List<String> volumes = executeShellCommand("sm list-volumes"); 109 // list volumes will result in something like 110 // private mounted null 111 // public:7,281 mounted 3080-17E8 112 // emulated;0 mounted null 113 // and we are interested in 3080-17E8 114 for (String volume: volumes) { 115 if (volume.contains("public") 116 && (excludingVolume == null || !volume.contains(excludingVolume))) { 117 //public:7,281 mounted 3080-17E8 118 String[] splits = volume.split(" "); 119 //Return the last snippet, that is 3080-17E8 120 return splits[splits.length - 1]; 121 } 122 } 123 return null; 124 } 125 executeShellCommand(String command)126 private static List<String> executeShellCommand(String command) throws Exception { 127 final ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation() 128 .getUiAutomation().executeShellCommand(command); 129 BufferedReader br = null; 130 try (InputStream in = new FileInputStream(pfd.getFileDescriptor())) { 131 br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); 132 String str = null; 133 List<String> output = new ArrayList<>(); 134 while ((str = br.readLine()) != null) { 135 output.add(str); 136 } 137 return output; 138 } finally { 139 if (br != null) { 140 closeQuietly(br); 141 } 142 closeQuietly(pfd); 143 } 144 } 145 closeQuietly(AutoCloseable closeable)146 private static void closeQuietly(AutoCloseable closeable) { 147 if (closeable != null) { 148 149 try { 150 closeable.close(); 151 } catch (RuntimeException rethrown) { 152 throw rethrown; 153 } catch (Exception ignored) { 154 Log.w("StorageManagerHelper", ignored.getMessage()); 155 } 156 } 157 } 158 } 159