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