1 /* 2 * Copyright (C) 2021 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 com.android.car.evs; 18 19 import static android.car.evs.CarEvsManager.ERROR_NONE; 20 import static android.car.evs.CarEvsManager.ERROR_UNAVAILABLE; 21 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED; 22 23 import static com.android.car.CarLog.TAG_EVS; 24 import static com.android.car.evs.StateMachine.REQUEST_PRIORITY_NORMAL; 25 import static com.android.car.evs.StateMachine.REQUEST_PRIORITY_HIGH; 26 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEBUGGING_CODE; 27 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 28 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.car.Car; 32 import android.car.VehiclePropertyIds; 33 import android.car.builtin.content.pm.PackageManagerHelper; 34 import android.car.builtin.os.BuildHelper; 35 import android.car.builtin.util.Slogf; 36 import android.car.evs.CarEvsBufferDescriptor; 37 import android.car.evs.CarEvsManager; 38 import android.car.evs.CarEvsManager.CarEvsError; 39 import android.car.evs.CarEvsManager.CarEvsServiceState; 40 import android.car.evs.CarEvsManager.CarEvsServiceType; 41 import android.car.evs.CarEvsStatus; 42 import android.car.evs.ICarEvsStatusListener; 43 import android.car.evs.ICarEvsStreamCallback; 44 import android.car.hardware.CarPropertyValue; 45 import android.car.hardware.property.CarPropertyEvent; 46 import android.car.hardware.property.ICarPropertyEventListener; 47 import android.car.user.CarUserManager.UserLifecycleListener; 48 import android.car.user.UserLifecycleEventFilter; 49 import android.content.ComponentName; 50 import android.content.Context; 51 import android.content.pm.PackageManager.NameNotFoundException; 52 import android.hardware.automotive.vehicle.VehicleGear; 53 import android.hardware.display.DisplayManager; 54 import android.hardware.display.DisplayManager.DisplayListener; 55 import android.os.Binder; 56 import android.os.Handler; 57 import android.os.IBinder; 58 import android.os.Looper; 59 import android.os.RemoteCallbackList; 60 import android.os.RemoteException; 61 import android.os.ServiceManager; 62 import android.os.SystemClock; 63 import android.os.UserHandle; 64 import android.util.ArrayMap; 65 import android.util.ArraySet; 66 import android.util.Log; 67 import android.util.SparseArray; 68 import android.util.proto.ProtoOutputStream; 69 import android.view.Display; 70 71 import com.android.car.CarLocalServices; 72 import com.android.car.CarPropertyService; 73 import com.android.car.CarServiceBase; 74 import com.android.car.CarServiceUtils; 75 import com.android.car.R; 76 import com.android.car.hal.EvsHalService; 77 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 78 import com.android.car.internal.evs.CarEvsUtils; 79 import com.android.car.internal.util.IndentingPrintWriter; 80 import com.android.car.user.CarUserService; 81 import com.android.internal.annotations.GuardedBy; 82 import com.android.internal.annotations.VisibleForTesting; 83 84 import java.lang.ref.WeakReference; 85 import java.util.List; 86 import java.util.Objects; 87 import java.util.Set; 88 89 /** 90 * A service that listens to the Extended View System across a HAL boundary and exposes the data to 91 * system clients in Android via {@link android.car.evs.CarEvsManager}. 92 * 93 * Because of Fast Message Queue usages, android.hardware.automotive.evs@1.1 interfaces does not 94 * support Java backend and, therefore, actual API calls are done in native methods. 95 * 96 * 97 * CarEvsService consists of four states: 98 * 99 * UNAVAILABLE: CarEvsService is not connected to the Extended View System service. In this 100 * state, any service request will be declined. 101 * 102 * INACTIVE: CarEvsService has a valid, live connection the Extended View System service and 103 * ready for any service requests. 104 * 105 * REQUESTED: CarEvsService received a service requeste from a privileged client and requested 106 * the System UI to launch the camera viewing activity. 107 * 108 * ACTIVE: CarEvsService is actively streaming a video to the client. 109 * 110 * See CarEvsService.StateMachine class for more details. 111 */ 112 public final class CarEvsService extends android.car.evs.ICarEvsService.Stub 113 implements CarServiceBase { 114 115 private static final boolean DBG = Slogf.isLoggable(TAG_EVS, Log.DEBUG); 116 private static final String EVS_INTERFACE_NAME = 117 "android.hardware.automotive.evs.IEvsEnumerator"; 118 private static final String EVS_DEFAULT_INSTANCE_NAME = "default"; 119 120 121 static final class EvsHalEvent { 122 private long mTimestamp; 123 private int mServiceType; 124 private boolean mOn; 125 EvsHalEvent(long timestamp, @CarEvsServiceType int type, boolean on)126 public EvsHalEvent(long timestamp, @CarEvsServiceType int type, boolean on) { 127 mTimestamp = timestamp; 128 mServiceType = type; 129 mOn = on; 130 } 131 getTimestamp()132 public long getTimestamp() { 133 return mTimestamp; 134 } 135 getServiceType()136 public @CarEvsServiceType int getServiceType() { 137 return mServiceType; 138 } 139 isRequestingToStartActivity()140 public boolean isRequestingToStartActivity() { 141 return mOn; 142 } 143 144 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) toString()145 public String toString() { 146 return "ServiceType=" + CarEvsUtils.convertToString(mServiceType) + 147 ", mOn=" + mOn + ", Timestamp=" + mTimestamp; 148 } 149 } 150 151 private final Context mContext; 152 private final Context mBuiltinContext; 153 private final EvsHalService mEvsHalService; 154 private final CarPropertyService mPropertyService; 155 private final DisplayManager mDisplayManager; // To monitor the default display's state 156 private final Object mLock = new Object(); 157 private final ArraySet<IBinder> mSessionTokens = new ArraySet<>(); 158 private final boolean mIsEvsAvailable; 159 160 // This handler is to monitor the client sends a video stream request within a given time 161 // after a state transition to the REQUESTED state. 162 private final Handler mHandler = new Handler(Looper.getMainLooper()); 163 164 private static final class StatusListenerList 165 extends RemoteCallbackList<ICarEvsStatusListener> { 166 private final WeakReference<CarEvsService> mService; 167 StatusListenerList(CarEvsService evsService)168 StatusListenerList(CarEvsService evsService) { 169 mService = new WeakReference<>(evsService); 170 } 171 172 /** Handle callback death */ 173 @Override onCallbackDied(ICarEvsStatusListener listener)174 public void onCallbackDied(ICarEvsStatusListener listener) { 175 Slogf.w(TAG_EVS, "StatusListener has died: " + listener.asBinder()); 176 177 CarEvsService svc = mService.get(); 178 if (svc != null) { 179 svc.handleClientDisconnected(listener); 180 } 181 } 182 } 183 184 private final StatusListenerList mStatusListeners = new StatusListenerList(this); 185 186 /** 187 * {@link CarPropertyEvent} listener registered with {@link CarPropertyService} to listen to 188 * {@link VehicleProperty.GEAR_SELECTION} change notifications. 189 */ 190 private final ICarPropertyEventListener mGearSelectionPropertyListener = 191 new ICarPropertyEventListener.Stub() { 192 @Override 193 public void onEvent(List<CarPropertyEvent> events) throws RemoteException { 194 if (events.isEmpty()) { 195 return; 196 } 197 198 // Handle only the latest event 199 Slogf.i(TAG_EVS, "Handling GearSelection event"); 200 handlePropertyEvent(events.get(events.size() - 1)); 201 } 202 }; 203 204 private final DisplayListener mDisplayListener = new DisplayListener() { 205 @Override 206 public void onDisplayAdded(int displayId) { 207 // Nothing to do 208 } 209 210 @Override 211 public void onDisplayRemoved(int displayId) { 212 // Nothing to do 213 } 214 215 @Override 216 public void onDisplayChanged(int displayId) { 217 if (displayId != Display.DEFAULT_DISPLAY) { 218 // We are interested only in the default display. 219 return; 220 } 221 222 Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); 223 if (mCurrentDisplayState == display.getState()) { 224 // We already handled this display state change. 225 if (DBG) { 226 Slogf.d(TAG_EVS, "We already handled a reported display status, %d", 227 display.getState()); 228 } 229 return; 230 } 231 232 // TODO(b/292155786): Current implementation is optimized for the device with a 233 // single display and therefore we may need to consider the 234 // source of the display event and start/stop activities 235 // accordingly. 236 switch (display.getState()) { 237 case Display.STATE_ON: 238 // Requests each StateMachine to launch a registered activity if it's 239 // necessary. 240 for (int i = 0; i < mServiceInstances.size(); i++) { 241 if (mServiceInstances.valueAt(i) 242 .requestStartActivityIfNecessary() == ERROR_NONE) { 243 continue; 244 } 245 Slogf.e(TAG_EVS, "Failed to start %s's activity.", 246 CarEvsUtils.convertToString(mServiceInstances.keyAt(i))); 247 } 248 break; 249 250 case Display.STATE_OFF: 251 // Each StateMachine stores a valid session token that was used for 252 // recognizing a streaming callback from a launched activity. 253 // CarEvsService will request each StateMachine to stop those callbacks 254 // and let other callbacks continue running. Activities not launched by 255 // CarEvsService must handle display's state changes properly by 256 // themselves. 257 for (int i = 0; i < mServiceInstances.size(); i++) { 258 mServiceInstances.valueAt(i) 259 .requestStopActivity(REQUEST_PRIORITY_HIGH); 260 } 261 break; 262 263 default: 264 // Nothing to do for all other state changes 265 break; 266 } 267 268 mCurrentDisplayState = display.getState(); 269 } 270 }; 271 272 private final UserLifecycleListener mUserLifecycleListener; 273 274 // Service instances per each type. 275 private final SparseArray<StateMachine> mServiceInstances; 276 277 // Associates callback objects with their service types. 278 private final ArrayMap<IBinder, ArraySet<Integer>> mCallbackToServiceType = 279 new ArrayMap<>(); 280 281 // The latest display state we have processed. 282 private int mCurrentDisplayState = Display.STATE_OFF; 283 284 // This boolean flag is true if CarEvsService uses GEAR_SELECTION VHAL property instead of 285 // EVS_SERVICE_REQUEST. 286 private boolean mUseGearSelection = true; 287 288 // The last event EvsHalService reported. This will be set to null when a related service 289 // request is handled. 290 // 291 // To properly handle a HAL event that occurred before CarEvsService is ready, we initialize 292 // mLastEvsHalEvent with a zero timestamp here. 293 @GuardedBy("mLock") 294 private EvsHalEvent mLastEvsHalEvent = new EvsHalEvent(/* timestamp= */ 0, 295 CarEvsManager.SERVICE_TYPE_REARVIEW, /* on= */ false); 296 297 private CarUserService mCarUserService; 298 299 /** Creates an Extended View System service instance given a {@link Context}. */ CarEvsService(Context context, Context builtinContext, EvsHalService halService, CarPropertyService propertyService)300 public CarEvsService(Context context, Context builtinContext, EvsHalService halService, 301 CarPropertyService propertyService) { 302 this(context, builtinContext, halService, propertyService, /* checkDependencies= */ true); 303 } 304 305 @VisibleForTesting CarEvsService(Context context, Context builtinContext, EvsHalService halService, CarPropertyService propertyService, boolean checkDependencies)306 CarEvsService(Context context, Context builtinContext, EvsHalService halService, 307 CarPropertyService propertyService, boolean checkDependencies) { 308 mContext = context; 309 mBuiltinContext = builtinContext; 310 311 // CarEvsService should become ineffective if the EVS service is not available. We confirm 312 // this by checking whether IEvsEnumerator/default instance is declared in VINTF. This check 313 // could be skipped only for testing purposes. 314 String instanceName = EVS_INTERFACE_NAME + "/" + EVS_DEFAULT_INSTANCE_NAME; 315 mIsEvsAvailable = !checkDependencies || ServiceManager.isDeclared(instanceName); 316 if (!mIsEvsAvailable) { 317 Slogf.e(TAG_EVS, "%s does not exist. CarEvsService won't be available.", instanceName); 318 319 // Set all final variables ineffective. 320 mPropertyService = null; 321 mEvsHalService = null; 322 mServiceInstances = new SparseArray<>(); 323 mDisplayManager = null; 324 mUserLifecycleListener = null; 325 326 return; 327 } 328 329 mPropertyService = propertyService; 330 mEvsHalService = halService; 331 332 // Reads the service configuration and initializes service instances. 333 String[] rawConfigurationStrings = mContext.getResources() 334 .getStringArray(R.array.config_carEvsService); 335 if (rawConfigurationStrings != null && rawConfigurationStrings.length > 0) { 336 mServiceInstances = new SparseArray<>(rawConfigurationStrings.length); 337 for (String rawString : rawConfigurationStrings) { 338 CarEvsServiceUtils.Parameters params = CarEvsServiceUtils.parse(rawString); 339 340 StateMachine s = new StateMachine(context, builtinContext, this, 341 params.getActivityComponentName(), params.getType(), params.getCameraId()); 342 mServiceInstances.put(params.getType(), s); 343 } 344 345 if (mServiceInstances.size() < 1) { 346 Slogf.e(TAG_EVS, "No valid configuration has been found. " + 347 "CarEvsService won't be available."); 348 mDisplayManager = null; 349 mUserLifecycleListener = null; 350 return; 351 } 352 } else { 353 mServiceInstances = new SparseArray<>(/* capacity= */ 1); 354 Slogf.i(TAG_EVS, "CarEvsService will be initialized only for the rearview service " + 355 "because no service configuration was available via " + 356 "config_carEvsService."); 357 358 String activityName = mContext.getResources() 359 .getString(R.string.config_evsCameraActivity); 360 ComponentName activityComponentName; 361 if (!activityName.isEmpty()) { 362 activityComponentName = ComponentName.unflattenFromString(activityName); 363 } else { 364 activityComponentName = null; 365 } 366 if (DBG) Slogf.d(TAG_EVS, "evsCameraActivity=" + activityName); 367 368 String cameraId = context.getString(R.string.config_evsRearviewCameraId); 369 StateMachine s = new StateMachine(context, builtinContext, this, activityComponentName, 370 CarEvsManager.SERVICE_TYPE_REARVIEW, cameraId); 371 mServiceInstances.put(CarEvsManager.SERVICE_TYPE_REARVIEW, s); 372 } 373 374 mDisplayManager = context.getSystemService(DisplayManager.class); 375 mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); 376 377 mUserLifecycleListener = event -> { 378 synchronized (mLock) { 379 if (!needToStartActivityLocked()) { 380 // No action required. 381 return; 382 } 383 384 StateMachine instance = mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW); 385 if (instance == null) { 386 // No action required if SERVICE_TYPE_REARVIEW is not activated. 387 return; 388 } 389 390 // Ensure a registered activity at the top of the back stack. 391 instance.bringActivityToForeground(); 392 } 393 }; 394 } 395 396 @VisibleForTesting 397 final class EvsTriggerListener implements EvsHalService.EvsHalEventListener { 398 399 /** Implements EvsHalService.EvsHalEventListener to monitor VHAL properties. */ 400 @Override onEvent(@arEvsServiceType int type, boolean on)401 public void onEvent(@CarEvsServiceType int type, boolean on) { 402 if (DBG) { 403 Slogf.d(TAG_EVS, 404 "Received an event from EVS HAL: type = " + type + ", on = " + on); 405 } 406 407 StateMachine instance = mServiceInstances.get(type); 408 if (instance == null) { 409 Slogf.w(TAG_EVS, "CarEvsService is not configured for %s", type); 410 return; 411 } 412 413 // Stores the last event. 414 synchronized (mLock) { 415 mLastEvsHalEvent = new EvsHalEvent(SystemClock.elapsedRealtimeNanos(), type, on); 416 } 417 418 if (on) { 419 // Request a camera activity. 420 if (instance.requestStartActivity(REQUEST_PRIORITY_HIGH) != ERROR_NONE) { 421 Slogf.e(TAG_EVS, "Fail to request a registered activity."); 422 } 423 } else { 424 // Stop a video stream and close an activity. 425 if (instance.requestStopActivity(REQUEST_PRIORITY_HIGH) != ERROR_NONE) { 426 Slogf.e(TAG_EVS, "Fail to stop a registered activity."); 427 } 428 } 429 } 430 } 431 432 @VisibleForTesting 433 final EvsTriggerListener mEvsTriggerListener = new EvsTriggerListener(); 434 435 @Override init()436 public void init() { 437 if (DBG) { 438 Slogf.d(TAG_EVS, "Initializing the service"); 439 } 440 441 if (!mIsEvsAvailable) { 442 Slogf.e(TAG_EVS, "CarEvsService cannot be initialized due to missing dependencies."); 443 return; 444 } 445 446 for (int i = mServiceInstances.size() - 1; i >= 0; i--) { 447 StateMachine instance = mServiceInstances.valueAt(i); 448 if (instance.init()) { 449 continue; 450 } 451 452 Slogf.e(TAG_EVS, "Failed to initialize a service handle for %s.", 453 mServiceInstances.keyAt(i)); 454 mServiceInstances.removeAt(i); 455 } 456 457 if (mEvsHalService.isEvsServiceRequestSupported()) { 458 try { 459 mEvsHalService.setListener(mEvsTriggerListener); 460 if (DBG) { 461 Slogf.d(TAG_EVS, "CarEvsService listens to EVS_SERVICE_REQUEST property."); 462 } 463 mUseGearSelection = false; 464 } catch (IllegalStateException e) { 465 Slogf.w(TAG_EVS, "Failed to set a EvsHalService listener. Try to use " 466 + "GEAR_SELECTION."); 467 } 468 } 469 470 if (mUseGearSelection) { 471 if (mPropertyService == null || mPropertyService.getPropertySafe( 472 VehiclePropertyIds.GEAR_SELECTION, /*areaId=*/ 0) == null) { 473 Slogf.w(TAG_EVS, 474 "GEAR_SELECTION property is also not available. " + 475 "CarEvsService may not respond to the system events."); 476 mUseGearSelection = false; 477 } else { 478 if (DBG) { 479 Slogf.d(TAG_EVS, "CarEvsService listens to GEAR_SELECTION property."); 480 } 481 482 if (!mPropertyService.registerListenerSafe( 483 VehiclePropertyIds.GEAR_SELECTION, /*updateRateHz=*/0, 484 mGearSelectionPropertyListener)) { 485 Slogf.w(TAG_EVS, "Failed to register a listener for GEAR_SELECTION property."); 486 mUseGearSelection = false; 487 } 488 } 489 } 490 491 StateMachine instance = mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW); 492 if (instance == null) { 493 Slogf.w(TAG_EVS, "The service is not initialized for the rearview service."); 494 return; 495 } 496 497 instance.connectToHalServiceIfNecessary(); 498 499 mCarUserService = CarLocalServices.getService(CarUserService.class); 500 if (mCarUserService != null) { 501 UserLifecycleEventFilter userEventFilter = new UserLifecycleEventFilter.Builder() 502 .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED).build(); 503 mCarUserService.addUserLifecycleListener(userEventFilter, mUserLifecycleListener); 504 } 505 } 506 507 @Override release()508 public void release() { 509 if (DBG) { 510 Slogf.d(TAG_EVS, "Finalizing the service"); 511 } 512 513 mDisplayManager.unregisterDisplayListener(mDisplayListener); 514 if (mCarUserService != null) { 515 mCarUserService.removeUserLifecycleListener(mUserLifecycleListener); 516 } 517 518 if (mUseGearSelection && mPropertyService != null) { 519 if (DBG) { 520 Slogf.d(TAG_EVS, "Unregister a property listener in release()"); 521 } 522 mPropertyService.unregisterListenerSafe(VehiclePropertyIds.GEAR_SELECTION, 523 mGearSelectionPropertyListener); 524 } 525 526 for (int i = 0; i < mServiceInstances.size(); i++) { 527 StateMachine instance = mServiceInstances.valueAt(i); 528 instance.release(); 529 } 530 531 mStatusListeners.kill(); 532 } 533 534 @Override 535 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)536 public void dump(IndentingPrintWriter writer) { 537 writer.println("*CarEvsService*"); 538 539 writer.increaseIndent(); 540 for (int i = 0; i < mServiceInstances.size(); i++) { 541 mServiceInstances.valueAt(i).dump(writer); 542 } 543 writer.decreaseIndent(); 544 writer.printf("\n"); 545 546 synchronized (mLock) { 547 writer.printf("%d service listeners subscribed.\n", 548 mStatusListeners.getRegisteredCallbackCount()); 549 writer.printf("Last HAL event: %s\n", mLastEvsHalEvent); 550 } 551 } 552 553 @Override 554 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dumpProto(ProtoOutputStream proto)555 public void dumpProto(ProtoOutputStream proto) {} 556 557 /** 558 * Registers a {@link ICarEvsStatusListener} to listen requests to control the camera 559 * previewing activity. 560 * 561 * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to 562 * access. 563 * 564 * @param listener {@link ICarEvsStatusListener} listener to register. 565 */ 566 @Override registerStatusListener(@onNull ICarEvsStatusListener listener)567 public void registerStatusListener(@NonNull ICarEvsStatusListener listener) { 568 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS); 569 Objects.requireNonNull(listener); 570 571 if (DBG) { 572 Slogf.d(TAG_EVS, "Registering a new service listener"); 573 } 574 mStatusListeners.register(listener); 575 } 576 577 /** 578 * Unregister the given {@link ICarEvsStatusListener} listener from receiving events. 579 * 580 * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to 581 * access. 582 * 583 * @param listener {@link ICarEvsStatusListener} listener to unregister. 584 */ 585 @Override unregisterStatusListener(@onNull ICarEvsStatusListener listener)586 public void unregisterStatusListener(@NonNull ICarEvsStatusListener listener) { 587 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS); 588 Objects.requireNonNull(listener); 589 590 mStatusListeners.unregister(listener); 591 } 592 593 /** 594 * Requests the system to start an activity to show the preview from a given EVS service type. 595 * 596 * <p>Requires {@link android.car.Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY} permissions to 597 * access. 598 * 599 * @param type {@link android.car.evs.CarEvsManager#CarEvsServiceType} 600 * @return {@link android.car.evs.CarEvsManager#CarEvsError} 601 */ 602 @Override startActivity(int type)603 public @CarEvsError int startActivity(int type) { 604 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY); 605 606 if (type == CarEvsManager.SERVICE_TYPE_SURROUNDVIEW) { 607 // TODO(b/179029031): Removes below when Surround View service is integrated. 608 Slogf.e(TAG_EVS, "Surround view is not supported yet."); 609 return ERROR_UNAVAILABLE; 610 } 611 612 StateMachine instance = mServiceInstances.get(type); 613 if (instance == null) { 614 return ERROR_UNAVAILABLE; 615 } 616 617 return instance.requestStartActivity(REQUEST_PRIORITY_NORMAL); 618 } 619 620 /** 621 * Requests to stop a current previewing activity launched via {@link #startActivity}. 622 * 623 * <p>Requires {@link android.car.Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY} permissions to 624 * access. 625 */ 626 @Override stopActivity()627 public void stopActivity() { 628 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY); 629 630 StateMachine instance = mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW); 631 if (instance == null) { 632 return; 633 } 634 635 instance.requestStopActivity(REQUEST_PRIORITY_NORMAL); 636 } 637 638 /** 639 * Starts a video stream. 640 * 641 * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access. 642 * 643 * @param type {@link android.car.evs.CarEvsManager#CarEvsServiceType} 644 * @param token IBinder object as a session token. If this is not null, CarEvsService handles a 645 * coming client as a privileged client. 646 * @param callback {@link ICarEvsStreamCallback} listener to register. 647 * @return {@link android.car.evs.CarEvsManager.CarEvsError} 648 */ 649 @Override startVideoStream(@arEvsServiceType int type, @Nullable IBinder token, @NonNull ICarEvsStreamCallback callback)650 public @CarEvsError int startVideoStream(@CarEvsServiceType int type, @Nullable IBinder token, 651 @NonNull ICarEvsStreamCallback callback) { 652 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA); 653 Objects.requireNonNull(callback); 654 655 StateMachine instance = mServiceInstances.get(type); 656 if (instance == null) { 657 Slogf.e(TAG_EVS, "CarEvsService is not configured for a service type %d.", type); 658 return ERROR_UNAVAILABLE; 659 } 660 661 // Single client can subscribe to multiple services. 662 // ArrayMap<IBinder, ArraySet<Integer>> 663 // Remembers which service a given callback is subscribing to. 664 ArraySet<Integer> types = mCallbackToServiceType.get(callback.asBinder()); 665 if (types == null) { 666 mCallbackToServiceType.put(callback.asBinder(), 667 new ArraySet<>(Set.of(type))); 668 } else { 669 types.add(type); 670 } 671 672 return instance.requestStartVideoStream(callback, token); 673 } 674 675 /** 676 * Requests to stop a video stream from the current client. 677 * 678 * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access. 679 * 680 * @param callback {@link ICarEvsStreamCallback} listener to unregister. 681 */ 682 @Override stopVideoStream(@onNull ICarEvsStreamCallback callback)683 public void stopVideoStream(@NonNull ICarEvsStreamCallback callback) { 684 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA); 685 Objects.requireNonNull(callback); 686 687 ArraySet<Integer> types = mCallbackToServiceType.get(callback.asBinder()); 688 if (types == null || types.isEmpty()) { 689 Slogf.i(TAG_EVS, "Ignores a request to stop a video stream for unknown callback %s.", 690 callback); 691 return; 692 } 693 694 for (int i = 0; i < types.size(); i++) { 695 int type = types.valueAt(i); 696 StateMachine instance = mServiceInstances.get(type); 697 if (instance == null) { 698 Slogf.w(TAG_EVS, "CarEvsService is not configured for a service type %d.", type); 699 continue; 700 } 701 702 instance.requestStopVideoStream(callback); 703 } 704 mCallbackToServiceType.remove(callback.asBinder()); 705 } 706 707 /** 708 * Requests to stop a video stream from a given service type. 709 * 710 * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access. 711 * 712 * @param type {@link CarEvsServiceType} to stop listening. 713 * @param callback {@link ICarEvsStreamCallback} listener to unregister. 714 */ 715 @Override stopVideoStreamFrom(@arEvsServiceType int type, @NonNull ICarEvsStreamCallback callback)716 public void stopVideoStreamFrom(@CarEvsServiceType int type, 717 @NonNull ICarEvsStreamCallback callback) { 718 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA); 719 Objects.requireNonNull(callback); 720 721 ArraySet<Integer> types = mCallbackToServiceType.get(callback.asBinder()); 722 int idx = types.indexOf(type); 723 if (idx < 0) { 724 Slogf.i(TAG_EVS, "Ignores a request to stop a video stream that is not active."); 725 return; 726 } 727 728 StateMachine instance = mServiceInstances.get(type); 729 if (instance == null) { 730 Slogf.w(TAG_EVS, "CarEvsService is not configured for a service type %d.", type); 731 return; 732 } 733 734 instance.requestStopVideoStream(callback); 735 types.remove(type); 736 if (types.isEmpty()) { 737 // No more active stream for this callback object. 738 mCallbackToServiceType.remove(callback.asBinder()); 739 } 740 } 741 742 /** 743 * Returns an used buffer to EVS service. 744 * 745 * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access. 746 * 747 * @param buffer A consumed CarEvsBufferDescriptor object. This would not be used and returned 748 * to the native EVS service. 749 * @throws IllegalArgumentException if a passed buffer has an unregistered identifier. 750 */ 751 @Override returnFrameBuffer(@onNull CarEvsBufferDescriptor buffer)752 public void returnFrameBuffer(@NonNull CarEvsBufferDescriptor buffer) { 753 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA); 754 Objects.requireNonNull(buffer); 755 756 // 8 MSB tells the service type of this buffer. 757 mServiceInstances.get(buffer.getType()).doneWithFrame(buffer.getId()); 758 } 759 760 /** 761 * Returns a current status of CarEvsService's REARVIEW service type. 762 * 763 * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to 764 * access. 765 * 766 * @return {@link android.car.evs.CarEvsStatus} 767 */ 768 @Override 769 @Nullable getCurrentStatus(@arEvsServiceType int type)770 public CarEvsStatus getCurrentStatus(@CarEvsServiceType int type) { 771 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS); 772 773 // This public API only returns current status of SERVICE_TYPE_REARVIEW. To get other 774 // services' status, please register a status listener via 775 // CarEvsService.registerStatusListener() API. 776 StateMachine instance = mServiceInstances.get(type); 777 if (instance == null) { 778 return null; 779 } 780 781 return instance.getCurrentStatus(); 782 } 783 784 /** 785 * Returns a session token to be used to request the services. 786 * 787 * <p>Requires {@link android.car.Car.PERMISSION_CONTROL_CAR_EVS_ACTIVITY} permission to access. 788 * 789 * @return IBinder object as a session token. 790 * @throws IllegalStateException if we fail to find System UI package. 791 */ 792 @Override generateSessionToken()793 public IBinder generateSessionToken() { 794 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_EVS_ACTIVITY); 795 796 // TODO(b/191940626): With the unlimited multi-client supports, a validity of a session 797 // token does not make any different in handling streaming clients. This 798 // needs to be modified with a logic to manage the maximum number of 799 // streaming clients per service. 800 String systemUiPackageName = PackageManagerHelper.getSystemUiPackageName(mContext); 801 IBinder token = new Binder(); 802 try { 803 int systemUiUid = PackageManagerHelper.getPackageUidAsUser(mContext.getPackageManager(), 804 systemUiPackageName, UserHandle.SYSTEM.getIdentifier()); 805 int callerUid = Binder.getCallingUid(); 806 if (systemUiUid == callerUid) { 807 mSessionTokens.add(token); 808 } else { 809 throw new SecurityException("SystemUI only can generate SessionToken"); 810 } 811 } catch (NameNotFoundException e) { 812 throw new IllegalStateException(systemUiPackageName + " package not found", e); 813 } finally { 814 return token; 815 } 816 } 817 818 /** 819 * Returns whether or not a given service type is supported. 820 * 821 * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to 822 * access. 823 */ 824 @Override isSupported(@arEvsServiceType int type)825 public boolean isSupported(@CarEvsServiceType int type) { 826 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS); 827 828 StateMachine instance = mServiceInstances.get(type); 829 if (instance == null) { 830 return false; 831 } 832 833 return instance.isConnected(); 834 } 835 836 /** 837 * Sets a camera device for the rearview. 838 * 839 * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access. 840 * 841 * @param id A string identifier of a target camera device. 842 * @return This method return a false if this runs in a release build; otherwise, this returns 843 * true. 844 */ setRearviewCameraIdFromCommand(@onNull String id)845 public boolean setRearviewCameraIdFromCommand(@NonNull String id) { 846 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA); 847 Objects.requireNonNull(id); 848 849 if (!mIsEvsAvailable) { 850 Slogf.e(TAG_EVS, "CarEvsService is not available."); 851 return false; 852 } 853 854 if (!BuildHelper.isDebuggableBuild()) { 855 // This method is not allowed in the release build. 856 Slogf.e(TAG_EVS, "It is not allowed to change a camera assigned to the rearview " + 857 "in the release build."); 858 return false; 859 } 860 861 mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW).setCameraId(id); 862 return true; 863 } 864 865 /** 866 * Sets a camera device for a given service type. 867 * 868 * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access. 869 * 870 * @param type A service type to assign a camera associated with a given string identifier. 871 * Please use '*' part of CarEvsManager.SERVICE_TYPE_* constants. 872 * @param id A string identifier of a target camera device. 873 * @return This method return true if it successfully programs a camera id for a given service 874 * type. Otherwise, this will return false. 875 */ setCameraIdFromCommand(@onNull String type, @NonNull String id)876 public boolean setCameraIdFromCommand(@NonNull String type, @NonNull String id) { 877 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA); 878 879 if (!BuildHelper.isDebuggableBuild()) { 880 // This method is not allowed in the release build. 881 Slogf.e(TAG_EVS, "It is not allowed to change a camera id assigned to the service " + 882 "in the release build."); 883 return false; 884 } 885 886 @CarEvsServiceType int serviceType = CarEvsUtils.convertToServiceType(type); 887 StateMachine instance = mServiceInstances.get(serviceType); 888 if (instance == null) { 889 Slogf.e(TAG_EVS, "Ignores a request to set a camera %s for unavailable service %s.", 890 id, type); 891 return false; 892 } 893 894 instance.setCameraId(id); 895 return true; 896 } 897 898 /** 899 * Gets an identifier of a current camera device for the rearview. 900 * 901 * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to 902 * access. 903 * 904 * @return A string identifier of current rearview camera device. 905 */ 906 @Nullable getRearviewCameraIdFromCommand()907 public String getRearviewCameraIdFromCommand() { 908 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS); 909 910 StateMachine instance = mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW); 911 if (instance == null) { 912 Slogf.e(TAG_EVS, "Ignores a request to get a camera id for unavailable " + 913 "REARVIEW service."); 914 return null; 915 } 916 917 return instance.getCameraId(); 918 } 919 920 /** 921 * Gets a String identifier of a camera assigned to a given service type. 922 * 923 * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to 924 * access. 925 * 926 * @param type A service type to get a camera identifier. Please use "*" part of 927 * CarEvsManager.SERVICE_TYPE_* constants. 928 * @return A string identifier of a camera assigned to a given service type. 929 */ 930 @Nullable getCameraIdFromCommand(@onNull String type)931 public String getCameraIdFromCommand(@NonNull String type) { 932 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS); 933 @CarEvsServiceType int serviceType = CarEvsUtils.convertToServiceType(type); 934 StateMachine instance = mServiceInstances.get(serviceType); 935 if (instance == null) { 936 Slogf.e(TAG_EVS, "Ignores a request to get a camera id for unavailable service %s.", 937 type); 938 return null; 939 } 940 941 return instance.getCameraId(); 942 } 943 944 /** 945 * Enables a given service type with a specified camera device. 946 * 947 * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access. 948 * 949 * @param typeString A service type to get a camera identifier. Please use "*" part of 950 * CarEvsManager.SERVICE_TYPE_* constants. 951 * @param cameraId A string identifier of a target camera device. A camera associated with this 952 * id must not be assigned to any service type. 953 * @return false if a requested service type is already enabled or a specific camera id is 954 * already assigned to other service types. 955 * true otherwise. 956 */ enableServiceTypeFromCommand(@onNull String typeString, @NonNull String cameraId)957 public boolean enableServiceTypeFromCommand(@NonNull String typeString, 958 @NonNull String cameraId) { 959 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA); 960 961 if (!mIsEvsAvailable) { 962 Slogf.e(TAG_EVS, "Failed to enable %s service due to missing dependencies.", 963 typeString); 964 return false; 965 } 966 967 @CarEvsServiceType int serviceType = CarEvsUtils.convertToServiceType(typeString); 968 for (int i = 0; i < mServiceInstances.size(); i++) { 969 int type = mServiceInstances.keyAt(i); 970 StateMachine instance = mServiceInstances.valueAt(i); 971 972 if (type == serviceType || cameraId.equals(instance.getCameraId())) { 973 Slogf.e(TAG_EVS, "A requested service type is already provided by " + 974 " or a given camera id is used by %s.", instance); 975 return false; 976 } 977 } 978 979 StateMachine s = new StateMachine(mContext, mBuiltinContext, this, null, 980 serviceType, cameraId); 981 if (!s.init()) { 982 Slogf.e(TAG_EVS, "Failed to initialize a requested service type."); 983 return false; 984 } 985 s.connectToHalServiceIfNecessary(); 986 mServiceInstances.put(serviceType, s); 987 return true; 988 } 989 990 /** 991 * Checks whether or not a given service type is enabled. 992 * 993 * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to 994 * access. 995 * 996 * @param type A service type to get a camera identifier. Please use "*" part of 997 * CarEvsManager.SERVICE_TYPE_* constants. 998 * @return true if a given service type is available. 999 * false otherwise. 1000 */ isServiceTypeEnabledFromCommand(@onNull String type)1001 public boolean isServiceTypeEnabledFromCommand(@NonNull String type) { 1002 CarServiceUtils.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS); 1003 @CarEvsServiceType int serviceType = CarEvsUtils.convertToServiceType(type); 1004 return mServiceInstances.get(serviceType) != null; 1005 } 1006 1007 /** Checks whether or not a given token is valid. */ isSessionToken(IBinder token)1008 boolean isSessionToken(IBinder token) { 1009 return mSessionTokens.contains(token); 1010 } 1011 1012 /** Invalidate a given token. */ invalidateSessionToken(IBinder token)1013 void invalidateSessionToken(IBinder token) { 1014 mSessionTokens.remove(token); 1015 } 1016 1017 /** Package-private version of generateSessionToken() method. */ 1018 @NonNull generateSessionTokenInternal()1019 IBinder generateSessionTokenInternal() { 1020 IBinder token = new Binder(); 1021 mSessionTokens.add(token); 1022 return token; 1023 } 1024 1025 /** 1026 * Manually sets a stream callback. 1027 */ 1028 @VisibleForTesting addStreamCallback(@arEvsServiceType int type, @Nullable ICarEvsStreamCallback callback)1029 void addStreamCallback(@CarEvsServiceType int type, @Nullable ICarEvsStreamCallback callback) { 1030 addStreamCallback(type, callback, /* token= */ null); 1031 } 1032 1033 /** 1034 * Manually sets a stream callback with a token. 1035 */ 1036 @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE) 1037 @VisibleForTesting addStreamCallback(@arEvsServiceType int type, @Nullable ICarEvsStreamCallback callback, @Nullable IBinder token)1038 void addStreamCallback(@CarEvsServiceType int type, @Nullable ICarEvsStreamCallback callback, 1039 @Nullable IBinder token) { 1040 StateMachine instance = mServiceInstances.get(type); 1041 if (instance == null || callback == null) { 1042 return; 1043 } 1044 1045 instance.addStreamCallback(callback, token); 1046 1047 ArraySet<Integer> types = mCallbackToServiceType.get(callback.asBinder()); 1048 if (types == null) { 1049 mCallbackToServiceType.put(callback.asBinder(), 1050 new ArraySet<>(Set.of(type))); 1051 } else { 1052 types.add(type); 1053 } 1054 } 1055 1056 /** Tells whether or not the latest EVS HAL event was requesting to start an activity. */ needToStartActivity()1057 boolean needToStartActivity() { 1058 synchronized (mLock) { 1059 return needToStartActivityLocked(); 1060 } 1061 } 1062 1063 @GuardedBy("mLock") needToStartActivityLocked()1064 boolean needToStartActivityLocked() { 1065 return mLastEvsHalEvent != null && mLastEvsHalEvent.isRequestingToStartActivity(); 1066 } 1067 1068 /** 1069 * Manually sets a current service state. 1070 */ 1071 @VisibleForTesting setServiceState(@arEvsServiceType int type, @CarEvsServiceState int newState)1072 void setServiceState(@CarEvsServiceType int type, @CarEvsServiceState int newState) { 1073 StateMachine instance = mServiceInstances.get(type); 1074 if (instance == null) { 1075 return; 1076 } 1077 1078 instance.setState(newState); 1079 } 1080 1081 /** 1082 * Manually chooses to use a gear selection property or not. 1083 */ 1084 @VisibleForTesting setToUseGearSelection(boolean useGearSelection)1085 void setToUseGearSelection(boolean useGearSelection) { 1086 mUseGearSelection = useGearSelection; 1087 } 1088 1089 /** 1090 * Manually sets the last EVS HAL event. 1091 */ 1092 @VisibleForTesting setLastEvsHalEvent(long timestamp, @CarEvsServiceType int type, boolean on)1093 void setLastEvsHalEvent(long timestamp, @CarEvsServiceType int type, boolean on) { 1094 synchronized (mLock) { 1095 mLastEvsHalEvent = new EvsHalEvent(timestamp, type, on); 1096 } 1097 } 1098 1099 /** 1100 * Manually sets the last EVS HAL event. 1101 */ 1102 @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE) 1103 @VisibleForTesting setSessionToken(@arEvsServiceType int type, @Nullable IBinder token)1104 void setSessionToken(@CarEvsServiceType int type, @Nullable IBinder token) { 1105 StateMachine instance = mServiceInstances.get(type); 1106 if (instance == null) { 1107 return; 1108 } 1109 1110 instance.setSessionToken(token); 1111 mSessionTokens.add(token); 1112 } 1113 1114 /** Notifies the service status gets changed */ broadcastStateTransition(int type, int state)1115 void broadcastStateTransition(int type, int state) { 1116 int idx = mStatusListeners.beginBroadcast(); 1117 while (idx-- > 0) { 1118 ICarEvsStatusListener listener = mStatusListeners.getBroadcastItem(idx); 1119 try { 1120 listener.onStatusChanged(new CarEvsStatus(type, state)); 1121 } catch (RemoteException e) { 1122 // Likely the binder death incident. 1123 Slogf.e(TAG_EVS, Log.getStackTraceString(e)); 1124 } 1125 } 1126 mStatusListeners.finishBroadcast(); 1127 } 1128 handlePropertyEvent(CarPropertyEvent event)1129 private void handlePropertyEvent(CarPropertyEvent event) { 1130 if (event.getEventType() != CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE) { 1131 // CarEvsService is interested only in the property change event. 1132 return; 1133 } 1134 1135 CarPropertyValue value = event.getCarPropertyValue(); 1136 if (value.getPropertyId() != VehiclePropertyIds.GEAR_SELECTION) { 1137 // CarEvsService is interested only in the GEAR_SELECTION property. 1138 return; 1139 } 1140 1141 long timestamp = value.getTimestamp(); 1142 boolean isReverseGear; 1143 synchronized (mLock) { 1144 if (timestamp != 0 && timestamp <= mLastEvsHalEvent.getTimestamp()) { 1145 if (DBG) { 1146 Slogf.d(TAG_EVS, 1147 "Ignoring GEAR_SELECTION change happened past, timestamp = " + 1148 timestamp + ", last event was at " + mLastEvsHalEvent.getTimestamp()); 1149 } 1150 return; 1151 } 1152 1153 1154 isReverseGear = (Integer) value.getValue() == VehicleGear.GEAR_REVERSE; 1155 mLastEvsHalEvent = new EvsHalEvent(timestamp, CarEvsManager.SERVICE_TYPE_REARVIEW, 1156 isReverseGear); 1157 } 1158 1159 StateMachine instance = mServiceInstances.get(CarEvsManager.SERVICE_TYPE_REARVIEW); 1160 if (instance == null) { 1161 Slogf.i(TAG_EVS, 1162 "Ignore a GEAR_SELECTION event because the rearview service is not available."); 1163 return; 1164 } 1165 1166 if (isReverseGear) { 1167 // Request to start the rearview activity when the gear is shifted into the reverse 1168 // position. 1169 if (instance.requestStartActivity(REQUEST_PRIORITY_HIGH) != ERROR_NONE) { 1170 Slogf.w(TAG_EVS, "Failed to request the rearview activity."); 1171 } 1172 } else { 1173 // Request to stop the rearview activity when the gear is shifted from the reverse 1174 // position to other positions. 1175 if (instance.requestStopActivity(REQUEST_PRIORITY_HIGH) != ERROR_NONE) { 1176 Slogf.i(TAG_EVS, "Failed to stop the rearview activity."); 1177 } 1178 } 1179 } 1180 1181 /** Handles a disconnection of a status monitoring client. */ handleClientDisconnected(ICarEvsStatusListener listener)1182 private void handleClientDisconnected(ICarEvsStatusListener listener) { 1183 mStatusListeners.unregister(listener); 1184 if (mStatusListeners.getRegisteredCallbackCount() == 0) { 1185 Slogf.d(TAG_EVS, "Last status listener has been disconnected."); 1186 } 1187 } 1188 } 1189