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