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