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.ApiCheckerRule; 27 import android.car.test.PermissionsCheckerRule; 28 import android.content.ComponentName; 29 import android.content.Context; 30 import android.content.ServiceConnection; 31 import android.os.IBinder; 32 import android.os.Looper; 33 import android.os.ParcelFileDescriptor; 34 import android.util.Log; 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 // TODO(b/242350638): temporary hack to allow subclasses to disable checks - should be removed 74 // when not needed anymore 75 private final ApiCheckerRule.Builder mApiCheckerRuleBuilder = new ApiCheckerRule.Builder(); 76 77 @Rule 78 public final PermissionsCheckerRule mPermissionsCheckerRule = new PermissionsCheckerRule(); 79 80 @Rule 81 public final ApiCheckerRule mApiCheckerRule; 82 83 protected final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); 84 85 private final DefaultServiceConnectionListener mConnectionListener = 86 new DefaultServiceConnectionListener(); 87 88 private Car mCar; 89 90 // TODO(b/242350638): temporary hack to allow subclasses to disable checks - should be removed 91 // when not needed anymore AbstractCarTestCase()92 protected AbstractCarTestCase() { 93 configApiCheckerRule(mApiCheckerRuleBuilder); 94 mApiCheckerRule = mApiCheckerRuleBuilder.build(); 95 } 96 97 // TODO(b/242350638): temporary hack to allow subclasses to disable checks - should be removed 98 // when not needed anymore configApiCheckerRule(ApiCheckerRule.Builder builder)99 protected void configApiCheckerRule(ApiCheckerRule.Builder builder) { 100 Log.v(TAG, "Good News, Everyone! Class " + getClass() 101 + " doesn't override configApiCheckerRule()"); 102 } 103 assertMainThread()104 protected void assertMainThread() { 105 assertWithMessage("Looper.getMainLooper().isCurrentThread()") 106 .that(Looper.getMainLooper().isCurrentThread()).isTrue(); 107 } 108 109 @Before createCar()110 public final void createCar() throws Exception { 111 mCar = Car.createCar(mContext); 112 } 113 114 @After disconnectCar()115 public final void disconnectCar() throws Exception { 116 if (mCar != null) { 117 mCar.disconnect(); 118 } 119 } 120 startUser(int userId)121 public void startUser(int userId) throws Exception { 122 executeShellCommand("am start-user %d", userId); 123 } 124 getCar()125 protected synchronized Car getCar() { 126 return mCar; 127 } 128 129 protected class DefaultServiceConnectionListener implements ServiceConnection { 130 private final Semaphore mConnectionWait = new Semaphore(0); 131 waitForConnection(long timeoutMs)132 public void waitForConnection(long timeoutMs) throws InterruptedException { 133 mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS); 134 } 135 136 @Override onServiceDisconnected(ComponentName name)137 public void onServiceDisconnected(ComponentName name) { 138 assertMainThread(); 139 } 140 141 @Override onServiceConnected(ComponentName name, IBinder service)142 public void onServiceConnected(ComponentName name, IBinder service) { 143 assertMainThread(); 144 mConnectionWait.release(); 145 } 146 } 147 executeShellCommand(String commandFormat, Object... args)148 protected static String executeShellCommand(String commandFormat, Object... args) 149 throws IOException { 150 UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 151 return executeShellCommand(uiAutomation, commandFormat, args); 152 } 153 executeShellCommandWithPermission(String permission, String commandFormat, Object... args)154 protected static String executeShellCommandWithPermission(String permission, 155 String commandFormat, Object... args) throws IOException { 156 UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); 157 String result; 158 try { 159 uiAutomation.adoptShellPermissionIdentity(permission); 160 result = executeShellCommand(uiAutomation, commandFormat, args); 161 } finally { 162 uiAutomation.dropShellPermissionIdentity(); 163 } 164 return result; 165 } 166 executeShellCommand(UiAutomation uiAutomation, String commandFormat, Object... args)167 private static String executeShellCommand(UiAutomation uiAutomation, String commandFormat, 168 Object... args) throws IOException { 169 ParcelFileDescriptor stdout = uiAutomation.executeShellCommand( 170 String.format(commandFormat, args)); 171 try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) { 172 ByteArrayOutputStream result = new ByteArrayOutputStream(); 173 byte[] buffer = new byte[1024]; 174 int length; 175 while ((length = inputStream.read(buffer)) != -1) { 176 result.write(buffer, 0, length); 177 } 178 return result.toString("UTF-8"); 179 } 180 } 181 } 182