1 /* 2 * Copyright (C) 2024 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; 18 19 import static android.car.Car.CAR_SERVICE_BINDER_SERVICE_NAME; 20 import static android.car.feature.Flags.FLAG_DISPLAY_COMPATIBILITY; 21 import static android.car.feature.Flags.FLAG_PERSIST_AP_SETTINGS; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.junit.Assert.assertThrows; 26 import static org.mockito.ArgumentMatchers.any; 27 import static org.mockito.ArgumentMatchers.anyInt; 28 import static org.mockito.Mockito.after; 29 import static org.mockito.Mockito.clearInvocations; 30 import static org.mockito.Mockito.doAnswer; 31 import static org.mockito.Mockito.timeout; 32 import static org.mockito.Mockito.times; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.when; 35 36 import static java.util.concurrent.TimeUnit.MILLISECONDS; 37 38 import android.car.Car.CarBuilder; 39 import android.car.Car.CarBuilder.ServiceManager; 40 import android.car.hardware.property.CarPropertyManager; 41 import android.car.hardware.property.ICarProperty; 42 import android.content.ComponentName; 43 import android.content.Context; 44 import android.content.ServiceConnection; 45 import android.content.pm.ApplicationInfo; 46 import android.content.pm.PackageManager; 47 import android.os.Build; 48 import android.os.Handler; 49 import android.os.HandlerThread; 50 import android.os.IBinder; 51 import android.os.Looper; 52 import android.os.RemoteException; 53 import android.os.SystemClock; 54 import android.platform.test.annotations.EnableFlags; 55 import android.platform.test.flag.junit.SetFlagsRule; 56 import android.platform.test.ravenwood.RavenwoodRule; 57 import android.util.Pair; 58 59 import com.android.car.internal.ICarServiceHelper; 60 import com.android.internal.annotations.GuardedBy; 61 62 import org.junit.After; 63 import org.junit.Before; 64 import org.junit.Rule; 65 import org.junit.Test; 66 import org.junit.runner.RunWith; 67 import org.mockito.Mock; 68 import org.mockito.junit.MockitoJUnitRunner; 69 70 import java.util.ArrayList; 71 import java.util.Collections; 72 import java.util.List; 73 import java.util.concurrent.CountDownLatch; 74 75 /** 76 * Unit test for Car API. 77 */ 78 @RunWith(MockitoJUnitRunner.Silent.class) 79 @EnableFlags({FLAG_PERSIST_AP_SETTINGS, FLAG_DISPLAY_COMPATIBILITY}) 80 public final class CarUnitTest { 81 82 private static final String TAG = CarUnitTest.class.getSimpleName(); 83 private static final String PKG_NAME = "Bond.James.Bond"; 84 private static final int DEFAULT_TIMEOUT_MS = 1000; 85 86 @Rule 87 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 88 @Rule 89 public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().setProvideMainThread(true) 90 .build(); 91 92 @Mock 93 private Context mContext; 94 @Mock 95 private ServiceConnection mServiceConnectionListener; 96 @Mock 97 private ComponentName mCarServiceComponentName; 98 @Mock 99 private PackageManager mPackageManager; 100 @Mock 101 private ServiceManager mServiceManager; 102 @Mock 103 private ICarProperty.Stub mICarProperty; 104 @Mock 105 private ApplicationInfo mApplicationInfo; 106 107 private HandlerThread mEventHandlerThread; 108 private Handler mEventHandler; 109 private Handler mMainHandler; 110 private CarBuilder mCarBuilder; 111 112 private final Object mLock = new Object(); 113 @GuardedBy("mLock") 114 private final List<ServiceConnection> mBindServiceConnections = new ArrayList<>(); 115 @GuardedBy("mLock") 116 private boolean mCarServiceRegistered; 117 118 // It is tricky to mock this. So create placeholder version instead. 119 private final class FakeService extends ICar.Stub { 120 121 @Override setSystemServerConnections(ICarServiceHelper helper, ICarResultReceiver receiver)122 public void setSystemServerConnections(ICarServiceHelper helper, 123 ICarResultReceiver receiver) throws RemoteException { 124 } 125 126 @Override isFeatureEnabled(String featureName)127 public boolean isFeatureEnabled(String featureName) { 128 return false; 129 } 130 131 @Override enableFeature(String featureName)132 public int enableFeature(String featureName) { 133 return Car.FEATURE_REQUEST_SUCCESS; 134 } 135 136 @Override disableFeature(String featureName)137 public int disableFeature(String featureName) { 138 return Car.FEATURE_REQUEST_SUCCESS; 139 } 140 141 @Override getAllEnabledFeatures()142 public List<String> getAllEnabledFeatures() { 143 return Collections.EMPTY_LIST; 144 } 145 146 @Override getAllPendingDisabledFeatures()147 public List<String> getAllPendingDisabledFeatures() { 148 return Collections.EMPTY_LIST; 149 } 150 151 @Override getAllPendingEnabledFeatures()152 public List<String> getAllPendingEnabledFeatures() { 153 return Collections.EMPTY_LIST; 154 } 155 156 @Override getCarManagerClassForFeature(String featureName)157 public String getCarManagerClassForFeature(String featureName) { 158 return null; 159 } 160 161 @Override getCarService(java.lang.String serviceName)162 public IBinder getCarService(java.lang.String serviceName) { 163 if (serviceName.equals(Car.PROPERTY_SERVICE)) { 164 return mICarProperty; 165 } 166 return null; 167 } 168 169 @Override getCarConnectionType()170 public int getCarConnectionType() { 171 return 0; 172 } 173 }; 174 175 private final FakeService mService = new FakeService(); 176 177 private static final class LifecycleListener implements Car.CarServiceLifecycleListener { 178 private final Object mLock = new Object(); 179 @GuardedBy("mLock") 180 private final ArrayList<Pair<Car, Boolean>> mEvents = new ArrayList<>(); 181 182 @Override onLifecycleChanged(Car car, boolean ready)183 public void onLifecycleChanged(Car car, boolean ready) { 184 synchronized (mLock) { 185 assertThat(Looper.getMainLooper()).isEqualTo(Looper.myLooper()); 186 mEvents.add(new Pair<>(car, ready)); 187 mLock.notifyAll(); 188 } 189 } 190 waitForEvent(int count, int timeoutInMs)191 void waitForEvent(int count, int timeoutInMs) throws InterruptedException { 192 synchronized (mLock) { 193 while (mEvents.size() < count) { 194 mLock.wait(timeoutInMs); 195 } 196 } 197 } 198 assertOneListenerCallAndClear(Car expectedCar, boolean ready)199 void assertOneListenerCallAndClear(Car expectedCar, boolean ready) { 200 synchronized (mLock) { 201 assertThat(mEvents).containsExactly(new Pair<>(expectedCar, ready)); 202 mEvents.clear(); 203 } 204 } 205 assertNoEvent()206 void assertNoEvent() { 207 synchronized (mLock) { 208 assertThat(mEvents).isEmpty(); 209 } 210 } 211 } 212 213 private final LifecycleListener mLifecycleListener = new LifecycleListener(); 214 215 @Before setUp()216 public void setUp() { 217 mEventHandlerThread = new HandlerThread("CarTestEvent"); 218 mEventHandlerThread.start(); 219 mEventHandler = new Handler(mEventHandlerThread.getLooper()); 220 mMainHandler = new Handler(Looper.getMainLooper()); 221 // Inject mServiceManager as a dependency for creating Car. 222 mCarBuilder = new CarBuilder().setServiceManager(mServiceManager); 223 224 when(mContext.getPackageName()).thenReturn(PKG_NAME); 225 when(mContext.getPackageManager()).thenReturn(mPackageManager); 226 when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(true); 227 setupFakeServiceManager(); 228 229 // Setup context for CarPropertyManager 230 mApplicationInfo.targetSdkVersion = Build.VERSION_CODES.R; 231 when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); 232 } 233 234 @After tearDown()235 public void tearDown() { 236 mEventHandlerThread.quitSafely(); 237 } 238 setupFakeServiceManager()239 private void setupFakeServiceManager() { 240 when(mContext.bindService(any(), any(), anyInt())).thenAnswer((inv) -> { 241 ServiceConnection serviceConnection = inv.getArgument(1); 242 243 synchronized (mLock) { 244 if (mCarServiceRegistered) { 245 mMainHandler.post(() -> serviceConnection.onServiceConnected( 246 mCarServiceComponentName, mService)); 247 } 248 mBindServiceConnections.add(serviceConnection); 249 } 250 251 return true; 252 }); 253 doAnswer((inv) -> { 254 ServiceConnection serviceConnection = inv.getArgument(0); 255 256 synchronized (mLock) { 257 mBindServiceConnections.remove(serviceConnection); 258 } 259 return null; 260 }).when(mContext).unbindService(any()); 261 262 when(mServiceManager.getService(CAR_SERVICE_BINDER_SERVICE_NAME)) 263 .thenAnswer((inv) -> { 264 synchronized (mLock) { 265 if (mCarServiceRegistered) { 266 return mService; 267 } 268 return null; 269 } 270 }); 271 } 272 setCarServiceRegistered()273 private void setCarServiceRegistered() { 274 synchronized (mLock) { 275 mCarServiceRegistered = true; 276 for (int i = 0; i < mBindServiceConnections.size(); i++) { 277 var serviceConnection = mBindServiceConnections.get(i); 278 mMainHandler.post(() -> serviceConnection.onServiceConnected( 279 mCarServiceComponentName, mService)); 280 } 281 } 282 } 283 setCarServiceDisconnected()284 private void setCarServiceDisconnected() { 285 synchronized (mLock) { 286 mCarServiceRegistered = false; 287 for (int i = 0; i < mBindServiceConnections.size(); i++) { 288 var serviceConnection = mBindServiceConnections.get(i); 289 mMainHandler.post(() -> serviceConnection.onServiceDisconnected( 290 mCarServiceComponentName)); 291 } 292 } 293 } 294 295 @Test testCreateCar_Context_ServiceConnection_Handler()296 public void testCreateCar_Context_ServiceConnection_Handler() { 297 Car car = Car.createCar(mContext, mServiceConnectionListener, mEventHandler); 298 299 assertThat(car).isNotNull(); 300 301 car.connect(); 302 303 assertThat(car.isConnecting()).isTrue(); 304 assertThat(car.isConnected()).isFalse(); 305 306 setCarServiceRegistered(); 307 308 verify(mServiceConnectionListener, timeout(DEFAULT_TIMEOUT_MS)).onServiceConnected( 309 mCarServiceComponentName, mService); 310 assertThat(car.isConnected()).isTrue(); 311 312 car.disconnect(); 313 assertThat(car.isConnected()).isFalse(); 314 } 315 316 @Test testCreateCar_Context_ServiceConnection_DefaultHandler()317 public void testCreateCar_Context_ServiceConnection_DefaultHandler() { 318 Car car = Car.createCar(mContext, mServiceConnectionListener); 319 320 assertThat(car).isNotNull(); 321 322 car.connect(); 323 324 assertThat(car.isConnecting()).isTrue(); 325 assertThat(car.isConnected()).isFalse(); 326 327 setCarServiceRegistered(); 328 329 verify(mServiceConnectionListener, timeout(DEFAULT_TIMEOUT_MS)).onServiceConnected( 330 mCarServiceComponentName, mService); 331 assertThat(car.isConnected()).isTrue(); 332 333 car.disconnect(); 334 assertThat(car.isConnected()).isFalse(); 335 } 336 337 @Test testCreateCar_Context_ServiceConnection_Handler_CarServiceRegistered()338 public void testCreateCar_Context_ServiceConnection_Handler_CarServiceRegistered() { 339 setCarServiceRegistered(); 340 341 Car car = Car.createCar(mContext, mServiceConnectionListener, mEventHandler); 342 343 assertThat(car).isNotNull(); 344 345 car.connect(); 346 347 verify(mServiceConnectionListener, timeout(DEFAULT_TIMEOUT_MS)).onServiceConnected( 348 mCarServiceComponentName, mService); 349 assertThat(car.isConnected()).isTrue(); 350 351 car.disconnect(); 352 assertThat(car.isConnected()).isFalse(); 353 } 354 355 @Test testCreateCar_Context_ServiceConnection_Handler_Disconnect_Reconnect()356 public void testCreateCar_Context_ServiceConnection_Handler_Disconnect_Reconnect() { 357 setCarServiceRegistered(); 358 359 Car car = Car.createCar(mContext, mServiceConnectionListener, mEventHandler); 360 car.connect(); 361 362 verify(mServiceConnectionListener, timeout(DEFAULT_TIMEOUT_MS)).onServiceConnected( 363 mCarServiceComponentName, mService); 364 clearInvocations(mServiceConnectionListener); 365 366 car.disconnect(); 367 car.connect(); 368 369 verify(mServiceConnectionListener, timeout(DEFAULT_TIMEOUT_MS)).onServiceConnected( 370 mCarServiceComponentName, mService); 371 } 372 373 @Test testCreateCar_Context_ServiceConnection_Handler_Disconnect_IgnoreCallback()374 public void testCreateCar_Context_ServiceConnection_Handler_Disconnect_IgnoreCallback() { 375 Car car = Car.createCar(mContext, mServiceConnectionListener, mEventHandler); 376 car.connect(); 377 car.disconnect(); 378 379 setCarServiceRegistered(); 380 381 // Callback must not be invoked while car is disconnected. 382 verify(mServiceConnectionListener, after(DEFAULT_TIMEOUT_MS).never()).onServiceConnected( 383 mCarServiceComponentName, mService); 384 385 car.connect(); 386 387 // Callback should be invoked after connect again. 388 verify(mServiceConnectionListener, timeout(DEFAULT_TIMEOUT_MS)).onServiceConnected( 389 mCarServiceComponentName, mService); 390 } 391 392 @Test testCreateCar_Context_ServiceConnection_Handler_ContextIsNull()393 public void testCreateCar_Context_ServiceConnection_Handler_ContextIsNull() { 394 assertThrows(NullPointerException.class, () -> Car.createCar( 395 /* context= */ null, mServiceConnectionListener, mEventHandler)); 396 } 397 398 @Test testCreateCar_Context_ServiceConnection_Handler_NoAutoFeature()399 public void testCreateCar_Context_ServiceConnection_Handler_NoAutoFeature() { 400 when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)).thenReturn(false); 401 402 Car car = Car.createCar(mContext, mServiceConnectionListener, mEventHandler); 403 404 assertThat(car).isNull(); 405 } 406 407 @Test testCreateCar_Context_CarServiceRegistered()408 public void testCreateCar_Context_CarServiceRegistered() throws Exception { 409 setCarServiceRegistered(); 410 411 Car car = mCarBuilder.createCar(mContext); 412 413 assertThat(car).isNotNull(); 414 assertThat(car.isConnected()).isTrue(); 415 416 // In the legacy implementation, createCar will bind to car service and cause an 417 // onServiceConnected callback to be invoked later. We must make sure this callback is 418 // invoked before disconnect, otherwise, the callback will set isConnected to true again. 419 finishTasksOnMain(); 420 421 car.disconnect(); 422 assertThat(car.isConnected()).isFalse(); 423 } 424 425 @Test testCreateCar_Context_CarServiceRegistered_DisconnectReconnect()426 public void testCreateCar_Context_CarServiceRegistered_DisconnectReconnect() throws Exception { 427 setCarServiceRegistered(); 428 429 Car car = mCarBuilder.createCar(mContext); 430 431 assertThat(car).isNotNull(); 432 assertThat(car.isConnected()).isTrue(); 433 434 // In the legacy implementation, createCar will bind to car service and cause an 435 // onServiceConnected callback to be invoked later. We must make sure this callback is 436 // invoked before disconnect, otherwise, the callback will set isConnected to true again. 437 finishTasksOnMain(); 438 439 car.disconnect(); 440 car.connect(); 441 442 // It takes a while for the callback to set connection state to connected. 443 long currentTimeMs = SystemClock.elapsedRealtime(); 444 long timeout = currentTimeMs + DEFAULT_TIMEOUT_MS; 445 while (!car.isConnected() && SystemClock.elapsedRealtime() < timeout) { 446 Thread.sleep(100); 447 } 448 449 assertThat(car.isConnected()).isTrue(); 450 } 451 452 @Test testCreateCar_Context_CarServiceNeverRegistered_Timeout()453 public void testCreateCar_Context_CarServiceNeverRegistered_Timeout() { 454 // This should timeout. 455 Car car = mCarBuilder.createCar(mContext); 456 457 assertThat(car).isNull(); 458 } 459 460 @Test testCreateCar_Context_CarServiceRegisteredLater_BeforeTimeout()461 public void testCreateCar_Context_CarServiceRegisteredLater_BeforeTimeout() throws Exception { 462 // Car service is registered after 200ms. 463 mEventHandler.postDelayed(() -> setCarServiceRegistered(), 200); 464 465 // This should block until car service is registered. 466 Car car = mCarBuilder.createCar(mContext); 467 468 assertThat(car).isNotNull(); 469 assertThat(car.isConnected()).isTrue(); 470 verify(mContext).bindService(any(), any(), anyInt()); 471 472 // In the legacy implementation, createCar will bind to car service and cause an 473 // onServiceConnected callback to be invoked later. We must make sure this callback is 474 // invoked before disconnect, otherwise, the callback will set isConnected to true again. 475 finishTasksOnMain(); 476 477 car.disconnect(); 478 assertThat(car.isConnected()).isFalse(); 479 } 480 481 @Test testCreateCar_Context_InvokeFromMain()482 public void testCreateCar_Context_InvokeFromMain() throws Exception { 483 // Car service is registered after 200ms. 484 mEventHandler.postDelayed(() -> setCarServiceRegistered(), 200); 485 486 runOnMain(() -> { 487 // This should block until car service is registered. 488 Car car = mCarBuilder.createCar(mContext); 489 490 assertThat(car).isNotNull(); 491 assertThat(car.isConnected()).isTrue(); 492 verify(mContext).bindService(any(), any(), anyInt()); 493 494 car.disconnect(); 495 assertThat(car.isConnected()).isFalse(); 496 }); 497 } 498 499 @Test testCreateCar_Context_WaitForever_Lclistener_CarServiceRegisteredLater()500 public void testCreateCar_Context_WaitForever_Lclistener_CarServiceRegisteredLater() 501 throws Exception { 502 // Car service is registered after 200ms. 503 mEventHandler.postDelayed(() -> setCarServiceRegistered(), 200); 504 505 Car car = mCarBuilder.createCar(mContext, null, 506 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mLifecycleListener); 507 508 assertThat(car).isNotNull(); 509 assertThat(car.isConnected()).isTrue(); 510 verify(mContext).bindService(any(), any(), anyInt()); 511 mLifecycleListener.assertOneListenerCallAndClear(car, true); 512 513 // Just call these to guarantee that nothing crashes with these call. 514 ServiceConnection serviceConnection; 515 synchronized (mLock) { 516 serviceConnection = mBindServiceConnections.get(0); 517 } 518 runOnMain(() -> { 519 serviceConnection.onServiceConnected(new ComponentName("", ""), mService); 520 serviceConnection.onServiceDisconnected(new ComponentName("", "")); 521 }); 522 } 523 524 @Test testCreateCar_Context_WaitForever_Lclistener_ConnectCrashRestart()525 public void testCreateCar_Context_WaitForever_Lclistener_ConnectCrashRestart() 526 throws Exception { 527 // Car service is registered after 100ms. 528 mEventHandler.postDelayed(() -> setCarServiceRegistered(), 100); 529 530 Car car = mCarBuilder.createCar(mContext, null, 531 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mLifecycleListener); 532 533 assertThat(car).isNotNull(); 534 assertThat(car.isConnected()).isTrue(); 535 // The callback will be called from the main thread, so it is not guaranteed to be called 536 // after createCar returns. 537 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 538 mLifecycleListener.assertOneListenerCallAndClear(car, true); 539 540 // Fake crash. 541 mEventHandler.post(() -> setCarServiceDisconnected()); 542 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 543 544 mLifecycleListener.assertOneListenerCallAndClear(car, false); 545 assertThat(car.isConnected()).isFalse(); 546 547 // fake restart 548 mEventHandler.post(() -> setCarServiceRegistered()); 549 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 550 551 mLifecycleListener.assertOneListenerCallAndClear(car, true); 552 assertThat(car.isConnected()).isTrue(); 553 } 554 555 @Test testCreateCar_Context_WaitForever_Lclistener_CarServiceAlreadyRegistered()556 public void testCreateCar_Context_WaitForever_Lclistener_CarServiceAlreadyRegistered() 557 throws Exception { 558 setCarServiceRegistered(); 559 560 runOnMain(() -> { 561 Car car = mCarBuilder.createCar(mContext, null, 562 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mLifecycleListener); 563 564 assertThat(car).isNotNull(); 565 assertThat(car.isConnected()).isTrue(); 566 verify(mContext, times(1)).bindService(any(), any(), anyInt()); 567 568 // mLifecycleListener should have been called as this is main thread. 569 mLifecycleListener.assertOneListenerCallAndClear(car, true); 570 }); 571 } 572 573 @Test testCreateCar_Context_WaitForever_Lclistener_ManagerNotTheSameAfterReconnect()574 public void testCreateCar_Context_WaitForever_Lclistener_ManagerNotTheSameAfterReconnect() 575 throws Exception { 576 setCarServiceRegistered(); 577 578 Car car = mCarBuilder.createCar(mContext, null, 579 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mLifecycleListener); 580 581 CarPropertyManager oldMgr = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE); 582 583 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 584 mLifecycleListener.assertOneListenerCallAndClear(car, true); 585 586 // Simulate car service crash. 587 setCarServiceDisconnected(); 588 589 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 590 mLifecycleListener.assertOneListenerCallAndClear(car, false); 591 592 // Simulate car service restore. 593 setCarServiceRegistered(); 594 595 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 596 mLifecycleListener.assertOneListenerCallAndClear(car, true); 597 CarPropertyManager newMgr = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE); 598 599 assertThat(oldMgr).isNotEqualTo(newMgr); 600 } 601 602 @Test testCreateCar_Context_DoNotWait_CarServiceRegistered()603 public void testCreateCar_Context_DoNotWait_CarServiceRegistered() 604 throws Exception { 605 setCarServiceRegistered(); 606 607 Car car = mCarBuilder.createCar(mContext, null, 608 Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, mLifecycleListener); 609 610 assertThat(car).isNotNull(); 611 assertThat(car.isConnected()).isTrue(); 612 verify(mContext).bindService(any(), any(), anyInt()); 613 614 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 615 mLifecycleListener.assertOneListenerCallAndClear(car, true); 616 } 617 618 @Test testCreateCar_Context_DoNotWait_CarServiceCrash_Restore()619 public void testCreateCar_Context_DoNotWait_CarServiceCrash_Restore() 620 throws Exception { 621 setCarServiceRegistered(); 622 623 Car car = mCarBuilder.createCar(mContext, null, 624 Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, mLifecycleListener); 625 626 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 627 mLifecycleListener.assertOneListenerCallAndClear(car, true); 628 629 // Simulate car service crash. 630 setCarServiceDisconnected(); 631 632 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 633 mLifecycleListener.assertOneListenerCallAndClear(car, false); 634 assertThat(car.isConnected()).isFalse(); 635 636 // Simulate car service restore. 637 setCarServiceRegistered(); 638 639 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 640 mLifecycleListener.assertOneListenerCallAndClear(car, true); 641 assertThat(car.isConnected()).isTrue(); 642 } 643 644 @Test testCreateCar_Context_DoNotWait_InvokeFromMain_CarServiceRegistered()645 public void testCreateCar_Context_DoNotWait_InvokeFromMain_CarServiceRegistered() 646 throws Exception { 647 setCarServiceRegistered(); 648 649 runOnMain(() -> { 650 Car car = mCarBuilder.createCar(mContext, null, 651 Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, mLifecycleListener); 652 653 assertThat(car).isNotNull(); 654 assertThat(car.isConnected()).isTrue(); 655 verify(mContext).bindService(any(), any(), anyInt()); 656 // createCar is called from main handler, so callback must have already been called. 657 mLifecycleListener.assertOneListenerCallAndClear(car, true); 658 }); 659 } 660 661 @Test testCreateCar_Context_DoNotWait_CarServiceRegisteredLater()662 public void testCreateCar_Context_DoNotWait_CarServiceRegisteredLater() 663 throws Exception { 664 Car car = mCarBuilder.createCar(mContext, null, 665 Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, mLifecycleListener); 666 667 assertThat(car).isNotNull(); 668 assertThat(car.isConnected()).isFalse(); 669 verify(mContext).bindService(any(), any(), anyInt()); 670 671 setCarServiceRegistered(); 672 673 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 674 mLifecycleListener.assertOneListenerCallAndClear(car, true); 675 } 676 677 @Test testCreateCar_Context_DoNotWait_CarServiceRegisteredAfterDisconnect()678 public void testCreateCar_Context_DoNotWait_CarServiceRegisteredAfterDisconnect() 679 throws Exception { 680 Car car = mCarBuilder.createCar(mContext, null, 681 Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, mLifecycleListener); 682 683 assertThat(car).isNotNull(); 684 assertThat(car.isConnected()).isFalse(); 685 verify(mContext).bindService(any(), any(), anyInt()); 686 687 car.disconnect(); 688 689 // Car service is registered after disconnect, must not invoke callback. 690 setCarServiceRegistered(); 691 692 Thread.sleep(DEFAULT_TIMEOUT_MS); 693 mLifecycleListener.assertNoEvent(); 694 695 // After connect, the callback must be invoked. 696 car.connect(); 697 698 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 699 mLifecycleListener.assertOneListenerCallAndClear(car, true); 700 } 701 702 @Test testCreateCar_Context_DoNotWait_InvokeFromMain_CarServiceRegisteredLater()703 public void testCreateCar_Context_DoNotWait_InvokeFromMain_CarServiceRegisteredLater() 704 throws Exception { 705 setCarServiceRegistered(); 706 707 runOnMain(() -> { 708 Car car = mCarBuilder.createCar(mContext, null, 709 Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, mLifecycleListener); 710 711 assertThat(car).isNotNull(); 712 assertThat(car.isConnected()).isTrue(); 713 verify(mContext).bindService(any(), any(), anyInt()); 714 // createCar is called from main handler, so callback must have already been called. 715 mLifecycleListener.assertOneListenerCallAndClear(car, true); 716 }); 717 } 718 719 @Test testCreateCar_Context_WithTimeout_InvokeFromMain_CarServiceRegisteredLater()720 public void testCreateCar_Context_WithTimeout_InvokeFromMain_CarServiceRegisteredLater() 721 throws Exception { 722 // Car service is registered after 200ms. 723 mEventHandler.postDelayed(() -> setCarServiceRegistered(), 200); 724 725 runOnMain(() -> { 726 Car car = mCarBuilder.createCar(mContext, null, DEFAULT_TIMEOUT_MS, mLifecycleListener); 727 728 assertThat(car).isNotNull(); 729 assertThat(car.isConnected()).isTrue(); 730 verify(mContext).bindService(any(), any(), anyInt()); 731 // createCar is called from main handler, so callback must have already been called. 732 mLifecycleListener.assertOneListenerCallAndClear(car, true); 733 }); 734 } 735 736 @Test testCreateCar_Context_WithTimeout_CarServiceRegisteredAfterTimeout()737 public void testCreateCar_Context_WithTimeout_CarServiceRegisteredAfterTimeout() 738 throws Exception { 739 // Car service is registered after 200ms. 740 mEventHandler.postDelayed(() -> setCarServiceRegistered(), 200); 741 742 Car car = mCarBuilder.createCar(mContext, null, 50, mLifecycleListener); 743 assertThat(car).isNotNull(); 744 assertThat(car.isConnected()).isFalse(); 745 verify(mContext).bindService(any(), any(), anyInt()); 746 747 // The callback should be invoked after 200ms. 748 mLifecycleListener.waitForEvent(1, DEFAULT_TIMEOUT_MS); 749 mLifecycleListener.assertOneListenerCallAndClear(car, true); 750 assertThat(car.isConnected()).isTrue(); 751 } 752 753 @Test testCreateCar_Context_WaitForever_InvokeFromMain_CarServiceRegisteredLater()754 public void testCreateCar_Context_WaitForever_InvokeFromMain_CarServiceRegisteredLater() 755 throws Exception { 756 // Car service is registered after 200ms. 757 mEventHandler.postDelayed(() -> setCarServiceRegistered(), 200); 758 759 runOnMain(() -> { 760 Car car = mCarBuilder.createCar(mContext, null, 761 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, mLifecycleListener); 762 763 assertThat(car).isNotNull(); 764 assertThat(car.isConnected()).isTrue(); 765 verify(mContext, times(1)).bindService(any(), any(), anyInt()); 766 767 // mLifecycleListener should have been called as this is main thread. 768 mLifecycleListener.assertOneListenerCallAndClear(car, true); 769 }); 770 } 771 runOnMain(Runnable runnable)772 private void runOnMain(Runnable runnable) throws InterruptedException { 773 var cdLatch = new CountDownLatch(1); 774 mMainHandler.post(() -> { 775 runnable.run(); 776 cdLatch.countDown(); 777 }); 778 cdLatch.await(DEFAULT_TIMEOUT_MS, MILLISECONDS); 779 } 780 finishTasksOnMain()781 private void finishTasksOnMain() throws InterruptedException { 782 // Do nothing on main just to make sure main finished handling the callbacks. 783 runOnMain(() -> {}); 784 } 785 } 786