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 com.android.cts.appcloning; 18 19 20 import static org.junit.Assert.assertNotNull; 21 22 import com.android.modules.utils.build.testing.DeviceSdkLevel; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.device.NativeDevice; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 28 import com.android.tradefed.util.CommandResult; 29 import com.android.tradefed.util.CommandStatus; 30 import com.android.tradefed.util.RunUtil; 31 32 import com.google.common.collect.Iterables; 33 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.List; 37 import java.util.function.BooleanSupplier; 38 39 40 abstract class BaseHostTestCase extends BaseHostJUnit4Test { 41 private int mCurrentUserId = NativeDevice.INVALID_USER_ID; 42 private static final String ERROR_MESSAGE_TAG = "[ERROR]"; 43 protected static ITestDevice sDevice = null; 44 private static final String MEDIA_PROVIDER_MODULE_NAME = "android.providers.media.module"; 45 setDevice(ITestDevice device)46 protected static void setDevice(ITestDevice device) { 47 sDevice = device; 48 } 49 executeShellCommand(String cmd, Object... args)50 protected String executeShellCommand(String cmd, Object... args) throws Exception { 51 return sDevice.executeShellCommand(String.format(cmd, args)); 52 } 53 executeShellV2Command(String cmd, Object... args)54 protected CommandResult executeShellV2Command(String cmd, Object... args) throws Exception { 55 return sDevice.executeShellV2Command(String.format(cmd, args)); 56 } 57 isPackageInstalled(String packageName, String userId)58 protected boolean isPackageInstalled(String packageName, String userId) throws Exception { 59 return sDevice.isPackageInstalled(packageName, userId); 60 } 61 62 // TODO (b/174775905) remove after exposing the check from ITestDevice. isHeadlessSystemUserMode()63 protected static boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException { 64 String result = sDevice 65 .executeShellCommand("getprop ro.fw.mu.headless_system_user").trim(); 66 return "true".equalsIgnoreCase(result); 67 } 68 supportsMultipleUsers()69 protected static boolean supportsMultipleUsers() throws DeviceNotAvailableException { 70 return sDevice.getMaxNumberOfUsersSupported() > 1; 71 } 72 isAtLeastS()73 protected static boolean isAtLeastS() throws DeviceNotAvailableException { 74 DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(sDevice); 75 return deviceSdkLevel.isDeviceAtLeastS(); 76 } 77 isAtLeastT()78 protected boolean isAtLeastT() throws DeviceNotAvailableException { 79 DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(sDevice); 80 return deviceSdkLevel.isDeviceAtLeastT(); 81 } 82 isAtLeastU(ITestDevice device)83 protected static boolean isAtLeastU(ITestDevice device) throws DeviceNotAvailableException { 84 DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(device); 85 return deviceSdkLevel.isDeviceAtLeastU(); 86 } 87 isAtLeastV(ITestDevice device)88 protected static boolean isAtLeastV(ITestDevice device) throws DeviceNotAvailableException { 89 DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(device); 90 return deviceSdkLevel.isDeviceAtLeastV(); 91 } 92 throwExceptionIfTimeout(long start, long timeoutMillis, Throwable e)93 protected static void throwExceptionIfTimeout(long start, long timeoutMillis, Throwable e) { 94 if (System.currentTimeMillis() - start < timeoutMillis) { 95 RunUtil.getDefault().sleep(100); 96 } else { 97 throw new RuntimeException(e); 98 } 99 } 100 eventually(ThrowingRunnable r, long timeoutMillis)101 protected static void eventually(ThrowingRunnable r, long timeoutMillis) { 102 long start = System.currentTimeMillis(); 103 104 while (true) { 105 try { 106 r.run(); 107 return; 108 } catch (Throwable e) { 109 throwExceptionIfTimeout(start, timeoutMillis, e); 110 } 111 } 112 } 113 eventually(ThrowingBooleanSupplier booleanSupplier, long timeoutMillis, String failureMessage)114 protected static void eventually(ThrowingBooleanSupplier booleanSupplier, 115 long timeoutMillis, String failureMessage) { 116 long start = System.currentTimeMillis(); 117 118 while (true) { 119 try { 120 if (booleanSupplier.getAsBoolean()) { 121 return; 122 } 123 124 throw new RuntimeException(failureMessage); 125 } catch (Throwable e) { 126 throwExceptionIfTimeout(start, timeoutMillis, e); 127 } 128 } 129 } 130 getCurrentUserId()131 protected int getCurrentUserId() throws Exception { 132 setCurrentUserId(); 133 134 return mCurrentUserId; 135 } 136 isSuccessful(CommandResult result)137 protected static boolean isSuccessful(CommandResult result) { 138 if (!CommandStatus.SUCCESS.equals(result.getStatus())) { 139 return false; 140 } 141 String stdout = result.getStdout(); 142 if (stdout.contains(ERROR_MESSAGE_TAG)) { 143 return false; 144 } 145 String stderr = result.getStderr(); 146 return (stderr == null || stderr.trim().isEmpty()); 147 } 148 setCurrentUserId()149 private void setCurrentUserId() throws Exception { 150 if (mCurrentUserId != NativeDevice.INVALID_USER_ID) return; 151 152 mCurrentUserId = sDevice.getCurrentUser(); 153 CLog.i("Current user: %d"); 154 } 155 156 protected interface ThrowingRunnable { 157 /** 158 * Similar to {@link Runnable#run} but has {@code throws Exception}. 159 */ run()160 void run() throws Exception; 161 } 162 163 protected interface ThrowingBooleanSupplier { 164 /** 165 * Similar to {@link BooleanSupplier#getAsBoolean} but has {@code throws Exception}. 166 */ getAsBoolean()167 boolean getAsBoolean() throws Exception; 168 } 169 getPublicVolumeExcluding(String excludingVolume)170 protected static String getPublicVolumeExcluding(String excludingVolume) throws Exception { 171 List<String> volList = splitMultiLineOutput(sDevice.executeShellCommand("sm list-volumes")); 172 173 // list volumes will result in something like 174 // private mounted null 175 // public:7,281 mounted 3080-17E8 176 // emulated;0 mounted null 177 // and we are interested in 3080-17E8 178 for (String volume: volList) { 179 if (volume.contains("public") 180 && (excludingVolume == null || !volume.contains(excludingVolume))) { 181 //public:7,281 mounted 3080-17E8 182 String[] splits = volume.split(" "); 183 //Return the last snippet, that is 3080-17E8 184 return splits[splits.length - 1]; 185 } 186 } 187 return null; 188 } 189 partitionDisks()190 protected static boolean partitionDisks() { 191 try { 192 List<String> diskNames = splitMultiLineOutput(sDevice 193 .executeShellCommand("sm list-disks")); 194 if (!diskNames.isEmpty() && !Iterables.getLast(diskNames).isEmpty()) { 195 sDevice.executeShellCommand("sm partition " 196 + Iterables.getLast(diskNames) + " public"); 197 return true; 198 } 199 } catch (Exception ignored) { 200 //ignored 201 } 202 return false; 203 } 204 getMediaProviderProcess(String userId)205 protected String getMediaProviderProcess(String userId) throws Exception { 206 String processUserPrefix = "u" + userId; 207 List<String> mediaProcessList = splitMultiLineOutput(sDevice 208 .executeShellCommand("ps -A -o user,name,pid | grep -i " 209 + MEDIA_PROVIDER_MODULE_NAME)); 210 // Media Process list will be something like: 211 // u0_a246 com.google.android.providers.media.module 21403 212 // u10_a246 com.google.android.providers.media.module 25576 213 for (String mediaProcess : mediaProcessList) { 214 if (mediaProcess.startsWith(processUserPrefix)) { 215 //u0_a246 com.google.android.providers.media.module 21403 216 String[] splits = mediaProcess.split(" "); 217 //Return the last snippet, that is 21403 218 return splits[splits.length - 1]; 219 } 220 } 221 return null; 222 } 223 isUserVolumeMounted(String userId)224 protected boolean isUserVolumeMounted(String userId) throws Exception { 225 String mountStatus = sDevice 226 .executeShellCommand("dumpsys mount | grep mountUserId=" + userId); 227 // Mount Status will be something like: 228 // type=EMULATED diskId=null partGuid= mountFlags=PRIMARY|VISIBLE_FOR_WRITE mountUserId=11 229 // state=MOUNTED 230 assertNotNull("No Volumes Found for user " + userId, mountStatus); 231 return mountStatus.contains("state=MOUNTED"); 232 } 233 splitMultiLineOutput(String input)234 private static List<String> splitMultiLineOutput(String input) throws Exception { 235 if (input == null) { 236 return new ArrayList<>(); 237 } 238 return Arrays.asList(input.split("\\r?\\n")); 239 } 240 } 241