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