Car.java revision 6b3544cf13ee99d6704216c96d40f89aff9bdc2b
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 17package android.car; 18 19import android.annotation.IntDef; 20import android.annotation.Nullable; 21import android.annotation.SystemApi; 22import android.car.annotation.FutureFeature; 23import android.car.content.pm.CarPackageManager; 24import android.car.hardware.CarDiagnosticManager; 25import android.car.hardware.CarSensorManager; 26import android.car.hardware.CarVendorExtensionManager; 27import android.car.hardware.cabin.CarCabinManager; 28import android.car.hardware.camera.CarCameraManager; 29import android.car.hardware.hvac.CarHvacManager; 30import android.car.hardware.radio.CarRadioManager; 31import android.car.media.CarAudioManager; 32import android.car.navigation.CarNavigationStatusManager; 33import android.car.test.CarTestManagerBinderWrapper; 34import android.car.vms.VmsSubscriberManager; 35import android.content.ComponentName; 36import android.content.Context; 37import android.content.Intent; 38import android.content.ServiceConnection; 39import android.content.pm.PackageManager; 40import android.os.Handler; 41import android.os.IBinder; 42import android.os.Looper; 43import android.os.RemoteException; 44import android.os.UserHandle; 45import android.util.Log; 46 47import com.android.car.internal.FeatureConfiguration; 48import com.android.car.internal.FeatureUtil; 49import com.android.internal.annotations.GuardedBy; 50 51import java.lang.annotation.Retention; 52import java.lang.annotation.RetentionPolicy; 53import java.util.HashMap; 54 55/** 56 * Top level car API for embedded Android Auto deployments. 57 * This API works only for devices with {@link PackageManager#FEATURE_AUTOMOTIVE} 58 * Calling this API on a device with no such feature will lead to an exception. 59 */ 60public final class Car { 61 62 /** 63 * Represent the version of Car API. 64 */ 65 public static final int VERSION = 1; 66 67 /** Service name for {@link CarSensorManager}, to be used in {@link #getCarManager(String)}. */ 68 public static final String SENSOR_SERVICE = "sensor"; 69 70 /** Service name for {@link CarInfoManager}, to be used in {@link #getCarManager(String)}. */ 71 public static final String INFO_SERVICE = "info"; 72 73 /** Service name for {@link CarAppFocusManager}. */ 74 public static final String APP_FOCUS_SERVICE = "app_focus"; 75 76 /** Service name for {@link CarPackageManager} */ 77 public static final String PACKAGE_SERVICE = "package"; 78 79 /** Service name for {@link CarAudioManager} */ 80 public static final String AUDIO_SERVICE = "audio"; 81 /** 82 * Service name for {@link CarNavigationStatusManager} 83 * @hide 84 */ 85 public static final String CAR_NAVIGATION_SERVICE = "car_navigation_service"; 86 87 /** 88 * @hide 89 */ 90 @SystemApi 91 public static final String CABIN_SERVICE = "cabin"; 92 93 /** 94 * @hide 95 */ 96 @SystemApi 97 public static final String DIAGNOSTIC_SERVICE = "diagnostic"; 98 99 /** 100 * @hide 101 */ 102 @SystemApi 103 public static final String CAMERA_SERVICE = "camera"; 104 105 /** 106 * @hide 107 */ 108 @SystemApi 109 public static final String RADIO_SERVICE = "radio"; 110 111 /** 112 * @hide 113 */ 114 @SystemApi 115 public static final String HVAC_SERVICE = "hvac"; 116 117 /** 118 * @hide 119 */ 120 @SystemApi 121 public static final String PROJECTION_SERVICE = "projection"; 122 123 /** 124 * @hide 125 */ 126 @SystemApi 127 public static final String VENDOR_EXTENSION_SERVICE = "vendor_extension"; 128 129 /** 130 * @FutureFeature Cannot drop due to usage in non-flag protected place. 131 * @hide 132 */ 133 @SystemApi 134 public static final String VMS_SUBSCRIBER_SERVICE = "vehicle_map_subscriber_service"; 135 136 /** 137 * Service for testing. This is system app only feature. 138 * Service name for {@link CarTestManager}, to be used in {@link #getCarManager(String)}. 139 * @hide 140 */ 141 @SystemApi 142 public static final String TEST_SERVICE = "car-service-test"; 143 144 /** Permission necessary to access car's mileage information. */ 145 public static final String PERMISSION_MILEAGE = "android.car.permission.CAR_MILEAGE"; 146 147 /** Permission necessary to access car's fuel level. */ 148 public static final String PERMISSION_FUEL = "android.car.permission.CAR_FUEL"; 149 150 /** Permission necessary to access car's speed. */ 151 public static final String PERMISSION_SPEED = "android.car.permission.CAR_SPEED"; 152 153 /** 154 * Permission necessary to change car audio volume through {@link CarAudioManager}. 155 */ 156 public static final String PERMISSION_CAR_CONTROL_AUDIO_VOLUME = 157 "android.car.permission.CAR_CONTROL_AUDIO_VOLUME"; 158 159 /** 160 * Permission necessary to change car audio settings through {@link CarAudioManager}. 161 * @hide 162 */ 163 public static final String PERMISSION_CAR_CONTROL_AUDIO_SETTINGS = 164 "android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"; 165 166 /** 167 * Permission necessary to use {@link CarNavigationStatusManager}. 168 * @hide 169 */ 170 public static final String PERMISSION_CAR_NAVIGATION_MANAGER = 171 "android.car.permission.CAR_NAVIGATION_MANAGER"; 172 173 /** 174 * Permission necessary to access car specific communication channel. 175 * @hide 176 */ 177 @SystemApi 178 public static final String PERMISSION_VENDOR_EXTENSION = 179 "android.car.permission.CAR_VENDOR_EXTENSION"; 180 181 /** 182 * @hide 183 */ 184 @SystemApi 185 public static final String PERMISSION_CONTROL_APP_BLOCKING = 186 "android.car.permission.CONTROL_APP_BLOCKING"; 187 188 /** 189 * Permission necessary to access Car Cabin APIs. 190 * @hide 191 */ 192 @SystemApi 193 public static final String PERMISSION_CAR_CABIN = "android.car.permission.CAR_CABIN"; 194 195 /** 196 * Permission necessary to access Car Camera APIs. 197 * @hide 198 */ 199 @SystemApi 200 public static final String PERMISSION_CAR_CAMERA = "android.car.permission.CAR_CAMERA"; 201 202 /** 203 * Permission necessary to access Car HVAC APIs. 204 * @hide 205 */ 206 @SystemApi 207 public static final String PERMISSION_CAR_HVAC = "android.car.permission.CAR_HVAC"; 208 209 /** 210 * Permission necessary to access Car RADIO system APIs. 211 * @hide 212 */ 213 @SystemApi 214 public static final String PERMISSION_CAR_RADIO = "android.car.permission.CAR_RADIO"; 215 216 217 /** 218 * Permission necessary to access Car PROJECTION system APIs. 219 * @hide 220 */ 221 @SystemApi 222 public static final String PERMISSION_CAR_PROJECTION = "android.car.permission.CAR_PROJECTION"; 223 224 /** 225 * Permission necessary to mock vehicle hal for testing. 226 * @hide 227 * @deprecated mocking vehicle HAL in car service is no longer supported. 228 */ 229 @SystemApi 230 public static final String PERMISSION_MOCK_VEHICLE_HAL = 231 "android.car.permission.CAR_MOCK_VEHICLE_HAL"; 232 233 /** 234 * Permission necessary to access CarTestService. 235 * @hide 236 */ 237 @SystemApi 238 public static final String PERMISSION_CAR_TEST_SERVICE = 239 "android.car.permission.CAR_TEST_SERVICE"; 240 241 /** 242 * Permissions necessary to access VMS publisher APIs. 243 * 244 * @hide 245 */ 246 @FutureFeature 247 @SystemApi 248 public static final String PERMISSION_VMS_PUBLISHER = "android.car.permission.VMS_PUBLISHER"; 249 250 /** 251 * Permissions necessary to access VMS subscriber APIs. 252 * 253 * @hide 254 */ 255 @FutureFeature 256 @SystemApi 257 public static final String PERMISSION_VMS_SUBSCRIBER = "android.car.permission.VMS_SUBSCRIBER"; 258 259 /** Type of car connection: platform runs directly in car. */ 260 public static final int CONNECTION_TYPE_EMBEDDED = 5; 261 262 263 /** @hide */ 264 @IntDef({CONNECTION_TYPE_EMBEDDED}) 265 @Retention(RetentionPolicy.SOURCE) 266 public @interface ConnectionType {} 267 268 /** 269 * CarXyzService throws IllegalStateException with this message is re-thrown as 270 * {@link CarNotConnectedException}. 271 * 272 * @hide 273 */ 274 public static final String CAR_NOT_CONNECTED_EXCEPTION_MSG = "CarNotConnected"; 275 276 /** @hide */ 277 public static final String CAR_SERVICE_INTERFACE_NAME = "android.car.ICar"; 278 279 private static final String CAR_SERVICE_PACKAGE = "com.android.car"; 280 281 private static final String CAR_SERVICE_CLASS = "com.android.car.CarService"; 282 283 private static final long CAR_SERVICE_BIND_RETRY_INTERVAL_MS = 500; 284 private static final long CAR_SERVICE_BIND_MAX_RETRY = 20; 285 286 private final Context mContext; 287 @GuardedBy("this") 288 private ICar mService; 289 private final boolean mOwnsService; 290 private static final int STATE_DISCONNECTED = 0; 291 private static final int STATE_CONNECTING = 1; 292 private static final int STATE_CONNECTED = 2; 293 @GuardedBy("this") 294 private int mConnectionState; 295 @GuardedBy("this") 296 private int mConnectionRetryCount; 297 298 private final Runnable mConnectionRetryRunnable = new Runnable() { 299 @Override 300 public void run() { 301 startCarService(); 302 } 303 }; 304 305 private final Runnable mConnectionRetryFailedRunnable = new Runnable() { 306 @Override 307 public void run() { 308 mServiceConnectionListener.onServiceDisconnected(new ComponentName(CAR_SERVICE_PACKAGE, 309 CAR_SERVICE_CLASS)); 310 } 311 }; 312 313 private final ServiceConnection mServiceConnectionListener = 314 new ServiceConnection () { 315 public void onServiceConnected(ComponentName name, IBinder service) { 316 synchronized (Car.this) { 317 mService = ICar.Stub.asInterface(service); 318 mConnectionState = STATE_CONNECTED; 319 } 320 mServiceConnectionListenerClient.onServiceConnected(name, service); 321 } 322 323 public void onServiceDisconnected(ComponentName name) { 324 synchronized (Car.this) { 325 mService = null; 326 if (mConnectionState == STATE_DISCONNECTED) { 327 return; 328 } 329 mConnectionState = STATE_DISCONNECTED; 330 } 331 // unbind explicitly here. 332 disconnect(); 333 mServiceConnectionListenerClient.onServiceDisconnected(name); 334 } 335 }; 336 337 private final ServiceConnection mServiceConnectionListenerClient; 338 private final Object mCarManagerLock = new Object(); 339 @GuardedBy("mCarManagerLock") 340 private final HashMap<String, CarManagerBase> mServiceMap = new HashMap<>(); 341 342 /** Handler for generic event dispatching. */ 343 private final Handler mEventHandler; 344 345 private final Handler mMainThreadEventHandler; 346 347 /** 348 * A factory method that creates Car instance for all Car API access. 349 * @param context 350 * @param serviceConnectionListener listener for monitoring service connection. 351 * @param handler the handler on which the callback should execute, or null to execute on the 352 * service's main thread. Note: the service connection listener will be always on the main 353 * thread regardless of the handler given. 354 * @return Car instance if system is in car environment and returns {@code null} otherwise. 355 */ 356 public static Car createCar(Context context, ServiceConnection serviceConnectionListener, 357 @Nullable Handler handler) { 358 if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { 359 Log.e(CarLibLog.TAG_CAR, "FEATURE_AUTOMOTIVE not declared while android.car is used"); 360 return null; 361 } 362 try { 363 return new Car(context, serviceConnectionListener, handler); 364 } catch (IllegalArgumentException e) { 365 // Expected when car service loader is not available. 366 } 367 return null; 368 } 369 370 /** 371 * A factory method that creates Car instance for all Car API access using main thread {@code 372 * Looper}. 373 * 374 * @see #createCar(Context, ServiceConnection, Handler) 375 */ 376 public static Car createCar(Context context, ServiceConnection serviceConnectionListener) { 377 return createCar(context, serviceConnectionListener, null); 378 } 379 380 private Car(Context context, ServiceConnection serviceConnectionListener, 381 @Nullable Handler handler) { 382 mContext = context; 383 mEventHandler = determineEventHandler(handler); 384 mMainThreadEventHandler = determineMainThreadEventHandler(mEventHandler); 385 386 mService = null; 387 mOwnsService = true; 388 mServiceConnectionListenerClient = serviceConnectionListener; 389 } 390 391 392 /** 393 * Car constructor when ICar binder is already available. 394 * @hide 395 */ 396 public Car(Context context, ICar service, @Nullable Handler handler) { 397 mContext = context; 398 mEventHandler = determineEventHandler(handler); 399 mMainThreadEventHandler = determineMainThreadEventHandler(mEventHandler); 400 401 mService = service; 402 mOwnsService = false; 403 mConnectionState = STATE_CONNECTED; 404 mServiceConnectionListenerClient = null; 405 } 406 407 private static Handler determineMainThreadEventHandler(Handler eventHandler) { 408 Looper mainLooper = Looper.getMainLooper(); 409 return (eventHandler.getLooper() == mainLooper) ? eventHandler : new Handler(mainLooper); 410 } 411 412 private static Handler determineEventHandler(@Nullable Handler handler) { 413 if (handler == null) { 414 Looper looper = Looper.getMainLooper(); 415 handler = new Handler(looper); 416 } 417 return handler; 418 } 419 420 /** 421 * Connect to car service. This can be called while it is disconnected. 422 * @throws IllegalStateException If connection is still on-going from previous 423 * connect call or it is already connected 424 */ 425 public void connect() throws IllegalStateException { 426 synchronized (this) { 427 if (mConnectionState != STATE_DISCONNECTED) { 428 throw new IllegalStateException("already connected or connecting"); 429 } 430 mConnectionState = STATE_CONNECTING; 431 startCarService(); 432 } 433 } 434 435 /** 436 * Disconnect from car service. This can be called while disconnected. Once disconnect is 437 * called, all Car*Managers from this instance becomes invalid, and 438 * {@link Car#getCarManager(String)} will return different instance if it is connected again. 439 */ 440 public void disconnect() { 441 synchronized (this) { 442 if (mConnectionState == STATE_DISCONNECTED) { 443 return; 444 } 445 mEventHandler.removeCallbacks(mConnectionRetryRunnable); 446 mMainThreadEventHandler.removeCallbacks(mConnectionRetryFailedRunnable); 447 mConnectionRetryCount = 0; 448 tearDownCarManagers(); 449 mService = null; 450 mConnectionState = STATE_DISCONNECTED; 451 452 if (mOwnsService) { 453 mContext.unbindService(mServiceConnectionListener); 454 } 455 } 456 } 457 458 /** 459 * Tells if it is connected to the service or not. This will return false if it is still 460 * connecting. 461 * @return 462 */ 463 public boolean isConnected() { 464 synchronized (this) { 465 return mService != null; 466 } 467 } 468 469 /** 470 * Tells if this instance is already connecting to car service or not. 471 * @return 472 */ 473 public boolean isConnecting() { 474 synchronized (this) { 475 return mConnectionState == STATE_CONNECTING; 476 } 477 } 478 479 /** 480 * Get car specific service as in {@link Context#getSystemService(String)}. Returned 481 * {@link Object} should be type-casted to the desired service. 482 * For example, to get sensor service, 483 * SensorManagerService sensorManagerService = car.getCarManager(Car.SENSOR_SERVICE); 484 * @param serviceName Name of service that should be created like {@link #SENSOR_SERVICE}. 485 * @return Matching service manager or null if there is no such service. 486 * @throws CarNotConnectedException if the connection to the car service has been lost. 487 */ 488 public Object getCarManager(String serviceName) throws CarNotConnectedException { 489 CarManagerBase manager; 490 ICar service = getICarOrThrow(); 491 synchronized (mCarManagerLock) { 492 manager = mServiceMap.get(serviceName); 493 if (manager == null) { 494 try { 495 IBinder binder = service.getCarService(serviceName); 496 if (binder == null) { 497 Log.w(CarLibLog.TAG_CAR, "getCarManager could not get binder for service:" + 498 serviceName); 499 return null; 500 } 501 manager = createCarManager(serviceName, binder); 502 if (manager == null) { 503 Log.w(CarLibLog.TAG_CAR, 504 "getCarManager could not create manager for service:" + 505 serviceName); 506 return null; 507 } 508 mServiceMap.put(serviceName, manager); 509 } catch (RemoteException e) { 510 handleRemoteException(e); 511 } 512 } 513 } 514 return manager; 515 } 516 517 /** 518 * Return the type of currently connected car. 519 * @return 520 */ 521 @ConnectionType 522 public int getCarConnectionType() { 523 return CONNECTION_TYPE_EMBEDDED; 524 } 525 526 /** 527 * IllegalStateException from XyzCarService with special message is re-thrown as a different 528 * exception. If the IllegalStateException is not understood then this message will throw the 529 * original exception. 530 * 531 * @param e exception from XyzCarService. 532 * @throws CarNotConnectedException if the connection to the car service has been lost. 533 * @hide 534 */ 535 public static void checkCarNotConnectedExceptionFromCarService( 536 IllegalStateException e) throws CarNotConnectedException, IllegalStateException { 537 String message = e.getMessage(); 538 if (CAR_NOT_CONNECTED_EXCEPTION_MSG.equals(message)) { 539 throw new CarNotConnectedException(); 540 } else { 541 throw e; 542 } 543 } 544 545 /** @hide */ 546 public static void hideCarNotConnectedExceptionFromCarService( 547 IllegalStateException e) throws IllegalStateException { 548 String message = e.getMessage(); 549 if (CAR_NOT_CONNECTED_EXCEPTION_MSG.equals(message)) { 550 return; //ignore 551 } else { 552 throw e; 553 } 554 } 555 556 private CarManagerBase createCarManager(String serviceName, IBinder binder) 557 throws CarNotConnectedException { 558 CarManagerBase manager = null; 559 switch (serviceName) { 560 case AUDIO_SERVICE: 561 manager = new CarAudioManager(binder, mContext, mEventHandler); 562 break; 563 case SENSOR_SERVICE: 564 manager = new CarSensorManager(binder, mContext, mEventHandler); 565 break; 566 case INFO_SERVICE: 567 manager = new CarInfoManager(binder); 568 break; 569 case APP_FOCUS_SERVICE: 570 manager = new CarAppFocusManager(binder, mEventHandler); 571 break; 572 case PACKAGE_SERVICE: 573 manager = new CarPackageManager(binder, mContext); 574 break; 575 case CAR_NAVIGATION_SERVICE: 576 manager = new CarNavigationStatusManager(binder); 577 break; 578 case CABIN_SERVICE: 579 manager = new CarCabinManager(binder, mContext, mEventHandler); 580 break; 581 case DIAGNOSTIC_SERVICE: 582 //TODO(egranata): only enable this if FeatureConfiguration is turned on 583 manager = new CarDiagnosticManager(binder, mContext, mEventHandler); 584 break; 585 case CAMERA_SERVICE: 586 manager = new CarCameraManager(binder, mContext); 587 break; 588 case HVAC_SERVICE: 589 manager = new CarHvacManager(binder, mContext, mEventHandler); 590 break; 591 case PROJECTION_SERVICE: 592 manager = new CarProjectionManager(binder, mEventHandler); 593 break; 594 case RADIO_SERVICE: 595 manager = new CarRadioManager(binder, mEventHandler); 596 break; 597 case VENDOR_EXTENSION_SERVICE: 598 manager = new CarVendorExtensionManager(binder, mEventHandler); 599 break; 600 case TEST_SERVICE: 601 /* CarTestManager exist in static library. So instead of constructing it here, 602 * only pass binder wrapper so that CarTestManager can be constructed outside. */ 603 manager = new CarTestManagerBinderWrapper(binder); 604 break; 605 case VMS_SUBSCRIBER_SERVICE: 606 if (FeatureConfiguration.ENABLE_VEHICLE_MAP_SERVICE) { 607 manager = new VmsSubscriberManager(binder, mEventHandler); 608 } 609 break; 610 } 611 return manager; 612 } 613 614 private void startCarService() { 615 Intent intent = new Intent(); 616 intent.setPackage(CAR_SERVICE_PACKAGE); 617 intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME); 618 boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener, 619 Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF); 620 if (!bound) { 621 mConnectionRetryCount++; 622 if (mConnectionRetryCount > CAR_SERVICE_BIND_MAX_RETRY) { 623 Log.w(CarLibLog.TAG_CAR, "cannot bind to car service after max retry"); 624 mMainThreadEventHandler.post(mConnectionRetryFailedRunnable); 625 } else { 626 mEventHandler.postDelayed(mConnectionRetryRunnable, 627 CAR_SERVICE_BIND_RETRY_INTERVAL_MS); 628 } 629 } else { 630 mConnectionRetryCount = 0; 631 } 632 } 633 634 private synchronized ICar getICarOrThrow() throws IllegalStateException { 635 if (mService == null) { 636 throw new IllegalStateException("not connected"); 637 } 638 return mService; 639 } 640 641 private void handleRemoteException(RemoteException e) { 642 Log.w(CarLibLog.TAG_CAR, "RemoteException", e); 643 disconnect(); 644 } 645 646 private void tearDownCarManagers() { 647 synchronized (mCarManagerLock) { 648 for (CarManagerBase manager: mServiceMap.values()) { 649 manager.onCarDisconnected(); 650 } 651 mServiceMap.clear(); 652 } 653 } 654} 655