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