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