1 /* 2 * Copyright (C) 2015 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.car.apitest; 18 19 import static com.android.compatibility.common.util.ShellUtils.runShellCommand; 20 import static com.android.compatibility.common.util.TestUtils.BooleanSupplierWithThrow; 21 22 import static com.google.common.truth.Truth.assertThat; 23 import static com.google.common.truth.Truth.assertWithMessage; 24 25 import static org.junit.Assert.fail; 26 import static org.junit.Assume.assumeFalse; 27 28 import android.annotation.NonNull; 29 import android.app.UiAutomation; 30 import android.car.Car; 31 import android.content.ComponentName; 32 import android.content.Context; 33 import android.content.ServiceConnection; 34 import android.hardware.automotive.vehicle.V2_0.InitialUserInfoRequestType; 35 import android.os.Build; 36 import android.os.IBinder; 37 import android.os.Looper; 38 import android.os.ParcelFileDescriptor; 39 import android.os.PowerManager; 40 import android.os.SystemClock; 41 import android.util.Log; 42 43 import androidx.test.platform.app.InstrumentationRegistry; 44 45 import org.junit.After; 46 import org.junit.Before; 47 import org.junit.Rule; 48 import org.junit.rules.TestName; 49 50 import java.io.BufferedReader; 51 import java.io.FileDescriptor; 52 import java.io.FileInputStream; 53 import java.io.IOException; 54 import java.io.InputStreamReader; 55 import java.util.concurrent.Semaphore; 56 import java.util.concurrent.TimeUnit; 57 58 public abstract class CarApiTestBase { 59 60 private static final String TAG = CarApiTestBase.class.getSimpleName(); 61 62 protected static final long DEFAULT_WAIT_TIMEOUT_MS = 60_000; 63 64 /** 65 * Constant used to wait blindly, when there is no condition that can be checked. 66 */ 67 private static final int SUSPEND_TIMEOUT_MS = 5_000; 68 69 /** 70 * How long to sleep (multiple times) while waiting for a condition. 71 */ 72 private static final int SMALL_NAP_MS = 100; 73 74 protected static final Context sContext = InstrumentationRegistry.getInstrumentation() 75 .getTargetContext(); 76 77 private Car mCar; 78 79 protected final DefaultServiceConnectionListener mConnectionListener = 80 new DefaultServiceConnectionListener(); 81 82 // NOTE: public as required by JUnit; tests should call getTestName() instead 83 @Rule 84 public final TestName mTestName = new TestName(); 85 86 @Before setFixturesAndConnectToCar()87 public final void setFixturesAndConnectToCar() throws Exception { 88 Log.d(TAG, "setFixturesAndConnectToCar() for " + mTestName.getMethodName()); 89 90 mCar = Car.createCar(getContext(), mConnectionListener); 91 mCar.connect(); 92 mConnectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS); 93 } 94 95 @After disconnectCar()96 public final void disconnectCar() throws Exception { 97 if (mCar == null) { 98 Log.wtf(TAG, "no mCar on " + getTestName() + ".tearDown()"); 99 return; 100 } 101 mCar.disconnect(); 102 } 103 getCar()104 protected Car getCar() { 105 return mCar; 106 } 107 getContext()108 protected final Context getContext() { 109 return sContext; 110 } 111 112 @SuppressWarnings("TypeParameterUnusedInFormals") // error prone complains about returning <T> getCarService(@onNull String serviceName)113 protected final <T> T getCarService(@NonNull String serviceName) { 114 assertThat(serviceName).isNotNull(); 115 Object service = mCar.getCarManager(serviceName); 116 assertWithMessage("Could not get service %s", serviceName).that(service).isNotNull(); 117 118 @SuppressWarnings("unchecked") 119 T castService = (T) service; 120 return castService; 121 } 122 assertMainThread()123 protected static void assertMainThread() { 124 assertThat(Looper.getMainLooper().isCurrentThread()).isTrue(); 125 } 126 127 protected static final class DefaultServiceConnectionListener implements ServiceConnection { 128 private final Semaphore mConnectionWait = new Semaphore(0); 129 waitForConnection(long timeoutMs)130 public void waitForConnection(long timeoutMs) throws InterruptedException { 131 mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS); 132 } 133 134 @Override onServiceConnected(ComponentName name, IBinder service)135 public void onServiceConnected(ComponentName name, IBinder service) { 136 assertMainThread(); 137 mConnectionWait.release(); 138 } 139 140 @Override onServiceDisconnected(ComponentName name)141 public void onServiceDisconnected(ComponentName name) { 142 assertMainThread(); 143 } 144 } 145 suspendToRamAndResume(boolean disableBackgroundUsersStart)146 protected static void suspendToRamAndResume(boolean disableBackgroundUsersStart) 147 throws Exception { 148 Log.d(TAG, "Emulate suspend to RAM and resume"); 149 try { 150 if (disableBackgroundUsersStart) { 151 runShellCommand("cmd car_service set-start-bg-users-on-garage-mode false"); 152 } 153 154 PowerManager powerManager = sContext.getSystemService(PowerManager.class); 155 // clear log 156 runShellCommand("logcat -b all -c"); 157 runShellCommand("cmd car_service suspend"); 158 // Check for suspend success 159 waitUntil("screen is still on after suspend", 160 SUSPEND_TIMEOUT_MS, () -> !powerManager.isScreenOn()); 161 162 // Force turn off garage mode 163 runShellCommand("cmd car_service garage-mode off"); 164 runShellCommand("cmd car_service resume"); 165 waitForLogcatMessage("logcat -b events", "car_user_svc_initial_user_info_req_complete: " 166 + InitialUserInfoRequestType.RESUME, 60_000); 167 } catch (Exception e) { 168 runShellCommand("cmd car_service set-start-bg-users-on-garage-mode true"); 169 } 170 } 171 172 /** 173 * Wait for a particular logcat message. 174 * 175 * @param cmd is logcat command with buffer or filters 176 * @param match is the string to be found in the log 177 * @param timeoutMs for which call should wait for the match 178 */ waitForLogcatMessage(String cmd, String match, int timeoutMs)179 protected static void waitForLogcatMessage(String cmd, String match, int timeoutMs) { 180 Log.d(TAG, "waiting for logcat match: " + match); 181 long startTime = SystemClock.elapsedRealtime(); 182 UiAutomation automation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 183 ParcelFileDescriptor output = automation.executeShellCommand(cmd); 184 FileDescriptor fd = output.getFileDescriptor(); 185 FileInputStream fileInputStream = new FileInputStream(fd); 186 try (BufferedReader bufferedReader = new BufferedReader( 187 new InputStreamReader(fileInputStream))) { 188 String line; 189 while ((line = bufferedReader.readLine()) != null) { 190 if (line.contains(match)) { 191 Log.d(TAG, "match found in " 192 + (SystemClock.elapsedRealtime() - startTime) + " ms"); 193 break; 194 } 195 if ((SystemClock.elapsedRealtime() - startTime) > timeoutMs) { 196 fail("match was not found, Timeout: " + timeoutMs + " ms"); 197 } 198 } 199 } catch (IOException e) { 200 fail("match was not found, IO exception: " + e); 201 } 202 } 203 waitUntil(String msg, long timeoutMs, BooleanSupplierWithThrow condition)204 protected static boolean waitUntil(String msg, long timeoutMs, 205 BooleanSupplierWithThrow condition) { 206 long deadline = SystemClock.elapsedRealtime() + timeoutMs; 207 do { 208 try { 209 if (condition.getAsBoolean()) { 210 return true; 211 } 212 } catch (Exception e) { 213 Log.e(TAG, "Exception in waitUntil: " + msg); 214 throw new RuntimeException(e); 215 } 216 SystemClock.sleep(SMALL_NAP_MS); 217 } while (SystemClock.elapsedRealtime() < deadline); 218 219 fail(msg + " after: " + timeoutMs + "ms"); 220 return false; 221 } 222 requireNonUserBuild()223 protected void requireNonUserBuild() { 224 assumeFalse("Requires Shell commands that are not available on user builds", Build.IS_USER); 225 } 226 getTestName()227 protected String getTestName() { 228 return getClass().getSimpleName() + "." + mTestName.getMethodName(); 229 } 230 } 231