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 android.car.cts;
18 
19 import static com.google.common.truth.Truth.assertWithMessage;
20 
21 import android.app.UiAutomation;
22 import android.car.Car;
23 import android.car.FuelType;
24 import android.car.PortLocationType;
25 import android.car.test.AbstractExpectableTestCase;
26 import android.car.test.PermissionsCheckerRule;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.ServiceConnection;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.ParcelFileDescriptor;
33 import android.platform.test.flag.junit.CheckFlagsRule;
34 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
35 
36 import androidx.test.platform.app.InstrumentationRegistry;
37 
38 import org.junit.After;
39 import org.junit.Before;
40 import org.junit.Rule;
41 
42 import java.io.ByteArrayOutputStream;
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.util.Arrays;
46 import java.util.List;
47 import java.util.concurrent.Semaphore;
48 import java.util.concurrent.TimeUnit;
49 
50 /**
51  * Base class for tests that don't need to connect to a {@link android.car.Car} object.
52  *
53  * <p>For tests that don't need a {@link android.car.Car} object, use
54  * {@link AbstractCarLessTestCase} instead.
55  */
56 abstract class AbstractCarTestCase extends AbstractExpectableTestCase {
57 
58     private static final String TAG = AbstractCarTestCase.class.getSimpleName();
59 
60     protected static final long DEFAULT_WAIT_TIMEOUT_MS = 1000;
61 
62     // Enums in FuelType
63     protected static final List<Integer> EXPECTED_FUEL_TYPES =
64             Arrays.asList(FuelType.UNKNOWN, FuelType.UNLEADED, FuelType.LEADED, FuelType.DIESEL_1,
65                     FuelType.DIESEL_2, FuelType.BIODIESEL, FuelType.E85, FuelType.LPG, FuelType.CNG,
66                     FuelType.LNG, FuelType.ELECTRIC, FuelType.HYDROGEN, FuelType.OTHER);
67     // Enums in PortLocationType
68     protected static final List<Integer> EXPECTED_PORT_LOCATIONS =
69             Arrays.asList(PortLocationType.UNKNOWN, PortLocationType.FRONT_LEFT,
70                     PortLocationType.FRONT_RIGHT, PortLocationType.REAR_RIGHT,
71                     PortLocationType.REAR_LEFT, PortLocationType.FRONT, PortLocationType.REAR);
72 
73     // CheckFlagsRule rule needs to before PermissionsCheckerRule.
74     @Rule(order = 0)
75     public final CheckFlagsRule checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
76 
77     @Rule(order = 1)
78     public final PermissionsCheckerRule permissionsCheckerRule = new PermissionsCheckerRule();
79 
80     protected final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
81 
82     private final DefaultServiceConnectionListener mConnectionListener =
83             new DefaultServiceConnectionListener();
84 
85     private Car mCar;
86 
assertMainThread()87     protected void assertMainThread() {
88         assertWithMessage("Looper.getMainLooper().isCurrentThread()")
89                 .that(Looper.getMainLooper().isCurrentThread()).isTrue();
90     }
91 
92     @Before
createCar()93     public final void createCar() throws Exception {
94         mCar = Car.createCar(mContext);
95     }
96 
97     @After
disconnectCar()98     public final void disconnectCar() throws Exception {
99         if (mCar != null) {
100             mCar.disconnect();
101         }
102     }
103 
startUser(int userId)104     public void startUser(int userId) throws Exception {
105         executeShellCommand("am start-user %d", userId);
106     }
107 
getCar()108     protected synchronized Car getCar() {
109         return mCar;
110     }
111 
112     protected class DefaultServiceConnectionListener implements ServiceConnection {
113         private final Semaphore mConnectionWait = new Semaphore(0);
114 
waitForConnection(long timeoutMs)115         public void waitForConnection(long timeoutMs) throws InterruptedException {
116             mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
117         }
118 
119         @Override
onServiceDisconnected(ComponentName name)120         public void onServiceDisconnected(ComponentName name) {
121             assertMainThread();
122         }
123 
124         @Override
onServiceConnected(ComponentName name, IBinder service)125         public void onServiceConnected(ComponentName name, IBinder service) {
126             assertMainThread();
127             mConnectionWait.release();
128         }
129     }
130 
executeShellCommand(String commandFormat, Object... args)131     protected static String executeShellCommand(String commandFormat, Object... args)
132             throws IOException {
133         UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
134         return executeShellCommand(uiAutomation, commandFormat, args);
135     }
136 
executeShellCommandWithPermission(String permission, String commandFormat, Object... args)137     protected static String executeShellCommandWithPermission(String permission,
138             String commandFormat, Object... args) throws IOException {
139         UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
140         String result;
141         try {
142             uiAutomation.adoptShellPermissionIdentity(permission);
143             result = executeShellCommand(uiAutomation, commandFormat, args);
144         } finally {
145             uiAutomation.dropShellPermissionIdentity();
146         }
147         return result;
148     }
149 
executeShellCommand(UiAutomation uiAutomation, String commandFormat, Object... args)150     private static String executeShellCommand(UiAutomation uiAutomation, String commandFormat,
151             Object... args) throws IOException {
152         ParcelFileDescriptor stdout = uiAutomation.executeShellCommand(
153                 String.format(commandFormat, args));
154         try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) {
155             ByteArrayOutputStream result = new ByteArrayOutputStream();
156             byte[] buffer = new byte[1024];
157             int length;
158             while ((length = inputStream.read(buffer)) != -1) {
159                 result.write(buffer, 0, length);
160             }
161             return result.toString("UTF-8");
162         }
163     }
164 }
165