• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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