blob: 8527189c73d21bffc18b10437ea6f78bc41b3c54 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.car.cts;
import static com.google.common.truth.Truth.assertThat;
import android.car.Car;
import android.content.ComponentName;
import android.content.Context;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.platform.test.annotations.AppModeFull;
import android.test.suitebuilder.annotation.SmallTest;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.RequiredFeatureRule;
import org.junit.After;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(AndroidJUnit4.class)
@AppModeFull(reason = "Test relies on other server to connect to.")
public class CarTest {
@ClassRule
public static final RequiredFeatureRule sRequiredFeatureRule = new RequiredFeatureRule(
PackageManager.FEATURE_AUTOMOTIVE);
private static final long DEFAULT_WAIT_TIMEOUT_MS = 2000;
private Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
private Car mCar;
private DefaultServiceConnectionListener mServiceConnectionListener;
@After
public void tearDown() {
if (mCar != null && mCar.isConnected()) {
mCar.disconnect();
}
}
@Test
public void testConnection() throws Exception {
mServiceConnectionListener = new DefaultServiceConnectionListener();
mCar = Car.createCar(mContext, mServiceConnectionListener);
assertThat(mCar.isConnected()).isFalse();
assertThat(mCar.isConnecting()).isFalse();
mCar.connect();
mServiceConnectionListener.waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
assertThat(mServiceConnectionListener.isConnected()).isTrue();
assertThat(mCar.getCarConnectionType()).isEqualTo(Car.CONNECTION_TYPE_EMBEDDED);
mCar.disconnect();
assertThat(mCar.isConnected()).isFalse();
assertThat(mCar.isConnecting()).isFalse();
}
@Test
public void testBlockingCreateCar() throws Exception {
mCar = Car.createCar(mContext);
assertConnectedCar(mCar);
}
@Test
public void testConnectionType() throws Exception {
createCarAndRunOnReady((car) -> assertThat(car.getCarConnectionType()).isEqualTo(
Car.CONNECTION_TYPE_EMBEDDED));
}
@Test
public void testIsFeatureEnabled() throws Exception {
createCarAndRunOnReady(
(car) -> assertThat(car.isFeatureEnabled(Car.AUDIO_SERVICE)).isTrue());
}
@Test
public void testCreateCarWaitForever() throws Exception {
CarServiceLifecycleListenerImpl listenerImpl = new CarServiceLifecycleListenerImpl(null);
mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
listenerImpl);
assertConnectedCar(mCar);
listenerImpl.waitForReady(DEFAULT_WAIT_TIMEOUT_MS);
}
@Test
public void testCreateCarNoWait() throws Exception {
CarServiceLifecycleListenerImpl listenerImpl = new CarServiceLifecycleListenerImpl(null);
// car service should be already running, so for normal apps, this should always return
// immediately
mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
listenerImpl);
assertConnectedCar(mCar);
listenerImpl.waitForReady(DEFAULT_WAIT_TIMEOUT_MS);
}
private static void assertConnectedCar(Car car) {
assertThat(car).isNotNull();
assertThat(car.isConnected()).isTrue();
}
private interface ReadyListener {
void onReady(Car car);
}
private void createCarAndRunOnReady(ReadyListener readyListener) throws Exception {
CarServiceLifecycleListenerImpl listenerImpl = new CarServiceLifecycleListenerImpl(
readyListener);
mCar = Car.createCar(mContext, /* handler= */ null, DEFAULT_WAIT_TIMEOUT_MS,
listenerImpl);
assertConnectedCar(mCar);
listenerImpl.waitForReady(DEFAULT_WAIT_TIMEOUT_MS);
}
private static final class CarServiceLifecycleListenerImpl
implements Car.CarServiceLifecycleListener {
private final ReadyListener mReadyListener;
private final CountDownLatch mWaitLatch = new CountDownLatch(1);
private CarServiceLifecycleListenerImpl(@Nullable ReadyListener readyListener) {
mReadyListener = readyListener;
}
private void waitForReady(long waitTimeMs) throws Exception {
assertThat(mWaitLatch.await(waitTimeMs, TimeUnit.MILLISECONDS)).isTrue();
}
@Override
public void onLifecycleChanged(@NonNull Car car, boolean ready) {
assertConnectedCar(car);
assertThat(ready).isTrue();
if (mReadyListener != null) {
mReadyListener.onReady(car);
}
mWaitLatch.countDown();
}
}
protected class DefaultServiceConnectionListener implements ServiceConnection {
private final Semaphore mConnectionWait = new Semaphore(0);
private boolean mIsconnected = false;
public synchronized boolean isConnected() {
return mIsconnected;
}
public void waitForConnection(long timeoutMs) throws InterruptedException {
mConnectionWait.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
}
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (this) {
mIsconnected = false;
}
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (this) {
mIsconnected = true;
}
mConnectionWait.release();
}
}
}