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