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 android.car.evs; 18 19 import static android.car.feature.Flags.FLAG_CAR_EVS_QUERY_SERVICE_STATUS; 20 import static android.car.feature.Flags.FLAG_CAR_EVS_STREAM_MANAGEMENT; 21 22 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; 23 24 import android.annotation.CallbackExecutor; 25 import android.annotation.FlaggedApi; 26 import android.annotation.IntDef; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.RequiresPermission; 30 import android.annotation.SuppressLint; 31 import android.annotation.SystemApi; 32 import android.car.Car; 33 import android.car.CarManagerBase; 34 import android.car.annotation.RequiredFeature; 35 import android.car.builtin.os.TraceHelper; 36 import android.car.builtin.util.Slogf; 37 import android.car.feature.FeatureFlags; 38 import android.car.feature.FeatureFlagsImpl; 39 import android.os.Binder; 40 import android.os.IBinder; 41 import android.os.RemoteException; 42 import android.os.Trace; 43 import android.util.Log; 44 import android.util.SparseArray; 45 import android.util.SparseIntArray; 46 47 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 48 import com.android.car.internal.ICarBase; 49 import com.android.car.internal.evs.CarEvsUtils; 50 import com.android.internal.annotations.GuardedBy; 51 52 import java.lang.annotation.Retention; 53 import java.lang.annotation.RetentionPolicy; 54 import java.lang.ref.WeakReference; 55 import java.util.Objects; 56 import java.util.concurrent.Executor; 57 import java.util.concurrent.Semaphore; 58 import java.util.concurrent.TimeUnit; 59 60 /** 61 * Provides an application interface for interativing with the Extended View System service. 62 * 63 * @hide 64 */ 65 @RequiredFeature(Car.CAR_EVS_SERVICE) 66 @SystemApi 67 public final class CarEvsManager extends CarManagerBase { 68 public static final String EXTRA_SESSION_TOKEN = "android.car.evs.extra.SESSION_TOKEN"; 69 70 private static final String TAG = CarEvsManager.class.getSimpleName(); 71 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 72 73 private final ICarEvsService mService; 74 private final Object mStreamLock = new Object(); 75 76 private final FeatureFlags mFeatureFlags; 77 78 // This array maintains mappings between service type and its client. 79 @GuardedBy("mStreamLock") 80 private SparseArray<CarEvsStreamCallback> mStreamCallbacks = new SparseArray<>(); 81 82 @GuardedBy("mStreamLock") 83 private Executor mStreamCallbackExecutor; 84 85 private final CarEvsStreamListenerToService mStreamListenerToService = 86 new CarEvsStreamListenerToService(this); 87 88 private final Object mStatusLock = new Object(); 89 90 @GuardedBy("mStatusLock") 91 private CarEvsStatusListener mStatusListener; 92 93 @GuardedBy("mStatusLock") 94 private Executor mStatusListenerExecutor; 95 96 private final CarEvsStatusListenerToService mStatusListenerToService = 97 new CarEvsStatusListenerToService(this); 98 99 /** 100 * This literal represents an unknown service type and is added for the backward compatibility. 101 */ 102 @FlaggedApi(FLAG_CAR_EVS_STREAM_MANAGEMENT) 103 public static final int SERVICE_TYPE_UNKNOWN = -1; 104 105 /** 106 * Service type to represent the rearview camera service. 107 */ 108 public static final int SERVICE_TYPE_REARVIEW = 0; 109 110 /** 111 * Service type to represent the surround view service. 112 */ 113 public static final int SERVICE_TYPE_SURROUNDVIEW = 1; 114 115 /** 116 * Service type to represent the front exterior view camera service. 117 */ 118 public static final int SERVICE_TYPE_FRONTVIEW = 2; 119 120 /** 121 * Service type to represent the left exterior view camera service such as 122 * the virtual side mirror. 123 */ 124 public static final int SERVICE_TYPE_LEFTVIEW = 3; 125 126 /** 127 * Service type to represent the right exterior view camera service such as 128 * the virtual side mirror. 129 */ 130 public static final int SERVICE_TYPE_RIGHTVIEW = 4; 131 132 /** 133 * Service type to represent the camera service that captures the scene 134 * with the driver. 135 */ 136 public static final int SERVICE_TYPE_DRIVERVIEW = 5; 137 138 /** 139 * Service type to represent the camera service that captures the scene 140 * with the front-seat passengers. 141 */ 142 public static final int SERVICE_TYPE_FRONT_PASSENGERSVIEW = 6; 143 144 /** 145 * Service type to represent the camera service that captures the scene 146 * with the rear-seat passengers. 147 */ 148 public static final int SERVICE_TYPE_REAR_PASSENGERSVIEW = 7; 149 150 /** 151 * Service type to represent the camera service that captures the scene 152 * the user defines. 153 */ 154 public static final int SERVICE_TYPE_USER_DEFINED = 1000; 155 156 /** @hide */ 157 @IntDef (prefix = {"SERVICE_TYPE_"}, value = { 158 SERVICE_TYPE_UNKNOWN, 159 SERVICE_TYPE_REARVIEW, 160 SERVICE_TYPE_SURROUNDVIEW, 161 SERVICE_TYPE_FRONTVIEW, 162 SERVICE_TYPE_LEFTVIEW, 163 SERVICE_TYPE_RIGHTVIEW, 164 SERVICE_TYPE_DRIVERVIEW, 165 SERVICE_TYPE_FRONT_PASSENGERSVIEW, 166 SERVICE_TYPE_REAR_PASSENGERSVIEW, 167 SERVICE_TYPE_USER_DEFINED, 168 }) 169 @Retention(RetentionPolicy.SOURCE) 170 public @interface CarEvsServiceType {} 171 172 /** 173 * State that a corresponding service type is not available. 174 */ 175 public static final int SERVICE_STATE_UNAVAILABLE = 0; 176 177 /** 178 * State that a corresponding service type is inactive; it's available but not used 179 * by any clients. 180 */ 181 public static final int SERVICE_STATE_INACTIVE = 1; 182 183 /** 184 * State that CarEvsManager received a service request from the client. 185 */ 186 public static final int SERVICE_STATE_REQUESTED = 2; 187 188 /** 189 * State that a corresponding service type is actively being used. 190 */ 191 public static final int SERVICE_STATE_ACTIVE = 3; 192 193 /** @hide */ 194 @IntDef (prefix = {"SERVICE_STATE_"}, value = { 195 SERVICE_STATE_UNAVAILABLE, 196 SERVICE_STATE_INACTIVE, 197 SERVICE_STATE_REQUESTED, 198 SERVICE_STATE_ACTIVE 199 }) 200 @Retention(RetentionPolicy.SOURCE) 201 public @interface CarEvsServiceState {} 202 203 /** 204 * This is a default EVS stream event type. 205 */ 206 public static final int STREAM_EVENT_NONE = 0; 207 208 /** 209 * EVS stream event to notify a video stream has been started. 210 */ 211 public static final int STREAM_EVENT_STREAM_STARTED = 1; 212 213 /** 214 * EVS stream event to notify a video stream has been stopped. 215 */ 216 public static final int STREAM_EVENT_STREAM_STOPPED = 2; 217 218 /** 219 * EVS stream event to notify that a video stream is dropped. 220 */ 221 public static final int STREAM_EVENT_FRAME_DROPPED = 3; 222 223 /** 224 * EVS stream event occurs when a timer for a new frame's arrival is expired. 225 */ 226 public static final int STREAM_EVENT_TIMEOUT = 4; 227 228 /** 229 * EVS stream event occurs when a camera parameter is changed. 230 */ 231 public static final int STREAM_EVENT_PARAMETER_CHANGED = 5; 232 233 /** 234 * EVS stream event to notify the primary owner has been changed. 235 */ 236 public static final int STREAM_EVENT_PRIMARY_OWNER_CHANGED = 6; 237 238 /** 239 * Other EVS stream errors 240 */ 241 public static final int STREAM_EVENT_OTHER_ERRORS = 7; 242 243 /** @hide */ 244 @IntDef(prefix = {"STREAM_EVENT_"}, value = { 245 STREAM_EVENT_NONE, 246 STREAM_EVENT_STREAM_STARTED, 247 STREAM_EVENT_STREAM_STOPPED, 248 STREAM_EVENT_FRAME_DROPPED, 249 STREAM_EVENT_TIMEOUT, 250 STREAM_EVENT_PARAMETER_CHANGED, 251 STREAM_EVENT_PRIMARY_OWNER_CHANGED, 252 STREAM_EVENT_OTHER_ERRORS 253 }) 254 @Retention(RetentionPolicy.SOURCE) 255 public @interface CarEvsStreamEvent {} 256 257 /** 258 * Status to tell that a request is successfully processed. 259 */ 260 public static final int ERROR_NONE = 0; 261 262 /** 263 * Status to tell a requested service is not available. 264 */ 265 public static final int ERROR_UNAVAILABLE = -1; 266 267 /** 268 * Status to tell CarEvsService is busy to serve the privileged client. 269 */ 270 public static final int ERROR_BUSY = -2; 271 272 /** @hide */ 273 @IntDef(prefix = {"ERROR_"}, value = { 274 ERROR_NONE, 275 ERROR_UNAVAILABLE, 276 ERROR_BUSY 277 }) 278 @Retention(RetentionPolicy.SOURCE) 279 public @interface CarEvsError {} 280 281 /** 282 * Gets an instance of CarEvsManager 283 * 284 * CarEvsManager manages {@link com.android.car.evs.CarEvsService} and provides APIs that the 285 * clients can use the Extended View System service. 286 * 287 * This must not be obtained directly by clients, use {@link Car#getCarManager(String)} instead. 288 * 289 * @hide 290 */ CarEvsManager(ICarBase car, IBinder service, @Nullable FeatureFlags featureFlags)291 public CarEvsManager(ICarBase car, IBinder service, @Nullable FeatureFlags featureFlags) { 292 super(car); 293 294 mFeatureFlags = Objects.requireNonNullElse(featureFlags, new FeatureFlagsImpl()); 295 // Gets CarEvsService 296 mService = ICarEvsService.Stub.asInterface(service); 297 } 298 299 /** @hide */ 300 @Override onCarDisconnected()301 public void onCarDisconnected() { 302 synchronized (mStatusLock) { 303 mStatusListener = null; 304 mStatusListenerExecutor = null; 305 } 306 307 synchronized (mStreamLock) { 308 stopVideoStreamLocked(); 309 } 310 } 311 312 /** 313 * Application registers {@link #CarEvsStatusListener} object to receive requests to control 314 * the activity and monitor the status of the EVS service. 315 */ 316 public interface CarEvsStatusListener { 317 /** 318 * Called when the status of EVS service is changed. 319 * 320 * @param type A type of EVS service; e.g. the rearview. 321 * @param state Updated service state; e.g. the service is started. 322 */ onStatusChanged(@onNull CarEvsStatus status)323 void onStatusChanged(@NonNull CarEvsStatus status); 324 } 325 326 /** 327 * Class implementing the listener interface {@link com.android.car.ICarEvsStatusListener} 328 * to listen status updates across the binder interface. 329 */ 330 private static class CarEvsStatusListenerToService extends ICarEvsStatusListener.Stub { 331 private final WeakReference<CarEvsManager> mManager; 332 CarEvsStatusListenerToService(CarEvsManager manager)333 CarEvsStatusListenerToService(CarEvsManager manager) { 334 mManager = new WeakReference<>(manager); 335 } 336 337 @Override onStatusChanged(@onNull CarEvsStatus status)338 public void onStatusChanged(@NonNull CarEvsStatus status) { 339 Objects.requireNonNull(status); 340 341 CarEvsManager mgr = mManager.get(); 342 if (mgr != null) { 343 mgr.handleServiceStatusChanged(status); 344 } 345 } 346 } 347 348 /** 349 * Gets the {@link #CarEvsStatus} from the service listener {@link 350 * #CarEvsStatusListenerToService} and forwards it to the client. 351 * 352 * @param status {@link android.car.evs.CarEvsStatus} 353 */ handleServiceStatusChanged(CarEvsStatus status)354 private void handleServiceStatusChanged(CarEvsStatus status) { 355 if (DBG) { 356 Slogf.d(TAG, "Service state changed: service = " + status.getServiceType() 357 + ", state = " + status.getState()); 358 } 359 360 final CarEvsStatusListener listener; 361 final Executor executor; 362 synchronized (mStatusLock) { 363 listener = mStatusListener; 364 executor = mStatusListenerExecutor; 365 } 366 367 if (listener != null) { 368 executor.execute(() -> listener.onStatusChanged(status)); 369 } else if (DBG) { 370 Slogf.w(TAG, "No client seems active; a received event is ignored."); 371 } 372 } 373 374 /** 375 * Sets {@link #CarEvsStatusListener} object to receive requests to control the activity 376 * view and EVS data. 377 * 378 * @param executor {@link java.util.concurrent.Executor} to execute callbacks. 379 * @param listener {@link #CarEvsStatusListener} to register. 380 * @throws IllegalStateException if this method is called while a registered status listener 381 * exists. 382 */ 383 @RequiresPermission(Car.PERMISSION_MONITOR_CAR_EVS_STATUS) setStatusListener(@onNull @allbackExecutor Executor executor, @NonNull CarEvsStatusListener listener)384 public void setStatusListener(@NonNull @CallbackExecutor Executor executor, 385 @NonNull CarEvsStatusListener listener) { 386 if (DBG) { 387 Slogf.d(TAG, "Registering a service monitoring listener."); 388 } 389 390 Objects.requireNonNull(listener); 391 Objects.requireNonNull(executor); 392 393 synchronized (mStatusLock) { 394 if (mStatusListener != null) { 395 throw new IllegalStateException("A status listener is already registered."); 396 } 397 mStatusListener = listener; 398 mStatusListenerExecutor = executor; 399 } 400 401 try { 402 mService.registerStatusListener(mStatusListenerToService); 403 } catch (RemoteException err) { 404 handleRemoteExceptionFromCarService(err); 405 } 406 } 407 408 /** 409 * Stops getting callbacks to control the camera viewing activity by clearing 410 * {@link #CarEvsStatusListener} object. 411 */ 412 @RequiresPermission(Car.PERMISSION_MONITOR_CAR_EVS_STATUS) clearStatusListener()413 public void clearStatusListener() { 414 if (DBG) { 415 Slogf.d(TAG, "Unregistering a service monitoring callback."); 416 } 417 418 synchronized (mStatusLock) { 419 mStatusListener = null; 420 } 421 422 try{ 423 mService.unregisterStatusListener(mStatusListenerToService); 424 } catch (RemoteException err) { 425 handleRemoteExceptionFromCarService(err); 426 } 427 } 428 429 /** 430 * Application registers {@link #CarEvsStreamCallback} object to listen to EVS streams. 431 * 432 * CarEvsManager supports two client types; one is a System UI type client and another is a 433 * normal Android activity type client. The former client type has a priority over 434 * the latter type client and CarEvsManager allows only a single client of each type to 435 * subscribe. 436 */ 437 // TODO(b/174572385): Removes below lint suppression 438 @SuppressLint("CallbackInterface") 439 public interface CarEvsStreamCallback { 440 /** 441 * Called when any EVS stream events occur. 442 * 443 * @param event {@link #CarEvsStreamEvent}; e.g. a stream started 444 * 445 * @deprecated Use {@link CarEvsStreamCallback#onStreamEvent(origin, event) instead. 446 */ 447 @Deprecated 448 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) onStreamEvent(@arEvsStreamEvent int event)449 default void onStreamEvent(@CarEvsStreamEvent int event) {} 450 451 /** 452 * Called when any EVS stream events occur with its origin and event type. 453 * 454 * @param origin {@link #CarEvsServiceType}; e.g. SERVICE_TYPE_REARVIEW 455 * @param event {@link #CarEvsStreamEvent}; e.g. a stream started 456 */ 457 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 458 @FlaggedApi(FLAG_CAR_EVS_STREAM_MANAGEMENT) onStreamEvent(@arEvsServiceType int origin, @CarEvsStreamEvent int event)459 default void onStreamEvent(@CarEvsServiceType int origin, @CarEvsStreamEvent int event) { 460 // By default, we forward this event callback to 461 // {@link CarEvsStreamCallback#onStreamEvent(int)}. 462 onStreamEvent(CarEvsUtils.putTag(origin, event)); 463 } 464 465 /** 466 * Called when new frame arrives. 467 * 468 * @param buffer {@link android.car.evs.CarEvsBufferDescriptor} contains a EVS frame 469 */ 470 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) onNewFrame(@onNull CarEvsBufferDescriptor buffer)471 default void onNewFrame(@NonNull CarEvsBufferDescriptor buffer) {} 472 } 473 474 /** 475 * Class implementing the listener interface and gets callbacks from the 476 * {@link com.android.car.ICarEvsStreamCallback} across the binder interface. 477 */ 478 private static class CarEvsStreamListenerToService extends ICarEvsStreamCallback.Stub { 479 private static final int DEFAULT_STREAM_EVENT_WAIT_TIMEOUT_IN_SEC = 1; 480 private static final int KEY_NOT_EXIST = Integer.MIN_VALUE; 481 private final WeakReference<CarEvsManager> mManager; 482 private final Semaphore mStreamEventOccurred = new Semaphore(/* permits= */ 0); 483 private final SparseIntArray mLastStreamEvent = new SparseIntArray(); 484 private final Object mLock = new Object(); 485 CarEvsStreamListenerToService(CarEvsManager manager)486 CarEvsStreamListenerToService(CarEvsManager manager) { 487 mManager = new WeakReference<>(manager); 488 } 489 490 @Override onStreamEvent(@arEvsServiceType int origin, @CarEvsStreamEvent int event)491 public void onStreamEvent(@CarEvsServiceType int origin, @CarEvsStreamEvent int event) { 492 Trace.asyncTraceBegin(TraceHelper.TRACE_TAG_CAR_EVS_SERVICE, 493 "CarEvsManager#onStreamEvent", origin); 494 if (DBG) { 495 Slogf.d(TAG, "Received an event %d from %d.", event, origin); 496 } 497 synchronized (mLock) { 498 mLastStreamEvent.append(origin, event); 499 mStreamEventOccurred.release(); 500 } 501 502 CarEvsManager manager = mManager.get(); 503 if (manager != null) { 504 manager.handleStreamEvent(origin, event); 505 } 506 Trace.asyncTraceEnd(TraceHelper.TRACE_TAG_CAR_EVS_SERVICE, 507 "CarEvsManager#onStreamEvent", origin); 508 } 509 510 @Override onNewFrame(CarEvsBufferDescriptor buffer)511 public void onNewFrame(CarEvsBufferDescriptor buffer) { 512 CarEvsManager manager = mManager.get(); 513 if (manager != null) { 514 manager.handleNewFrame(buffer); 515 } 516 } 517 waitForStreamEvent(@arEvsServiceType int from, @CarEvsStreamEvent int expected)518 public boolean waitForStreamEvent(@CarEvsServiceType int from, 519 @CarEvsStreamEvent int expected) { 520 return waitForStreamEvent(from, expected, DEFAULT_STREAM_EVENT_WAIT_TIMEOUT_IN_SEC); 521 } 522 waitForStreamEvent(@arEvsServiceType int from, @CarEvsStreamEvent int expected, int timeoutInSeconds)523 public boolean waitForStreamEvent(@CarEvsServiceType int from, 524 @CarEvsStreamEvent int expected, int timeoutInSeconds) { 525 Trace.asyncTraceBegin(TraceHelper.TRACE_TAG_CAR_EVS_SERVICE, 526 "CarEvsManager#waitForStreamEvent", from); 527 try { 528 while (true) { 529 if (!mStreamEventOccurred.tryAcquire(timeoutInSeconds, TimeUnit.SECONDS)) { 530 Slogf.w(TAG, "Timer for a new stream event expired."); 531 return false; 532 } 533 534 int lastEvent; 535 synchronized (mLock) { 536 lastEvent = mLastStreamEvent.get(from, KEY_NOT_EXIST); 537 538 if (lastEvent == KEY_NOT_EXIST) { 539 // We have not received any event from a target service type yet. 540 continue; 541 } 542 543 if (lastEvent == expected) { 544 mLastStreamEvent.delete(from); 545 return true; 546 } 547 } 548 } 549 } catch (InterruptedException e) { 550 Slogf.w(TAG, "Interrupted while waiting for an event %d.\nException = %s", 551 expected, Log.getStackTraceString(e)); 552 return false; 553 } finally { 554 Trace.asyncTraceEnd(TraceHelper.TRACE_TAG_CAR_EVS_SERVICE, 555 "CarEvsManager#waitForStreamEvent", from); 556 } 557 } 558 } 559 560 /** 561 * Gets the {@link #CarEvsStreamEvent} from the service listener 562 * {@link #CarEvsStreamListenerToService} and dispatches it to an executor provided 563 * to the manager. 564 * 565 * @param event {@link #CarEvsStreamEvent} from the service this manager subscribes to. 566 */ handleStreamEvent(@arEvsServiceType int origin, @CarEvsStreamEvent int event)567 private void handleStreamEvent(@CarEvsServiceType int origin, @CarEvsStreamEvent int event) { 568 synchronized(mStreamLock) { 569 handleStreamEventLocked(origin, event); 570 } 571 } 572 573 @GuardedBy("mStreamLock") handleStreamEventLocked(@arEvsServiceType int origin, @CarEvsStreamEvent int event)574 private void handleStreamEventLocked(@CarEvsServiceType int origin, 575 @CarEvsStreamEvent int event) { 576 if (DBG) { 577 Slogf.d(TAG, "Received: " + event); 578 } 579 580 CarEvsStreamCallback callback = mStreamCallbacks.get(origin); 581 Executor executor = mStreamCallbackExecutor; 582 if (callback != null) { 583 handleStreamEventLocked(origin, event, callback, executor); 584 } 585 586 if (DBG) { 587 Slogf.w(TAG, "No client seems active; a current stream event is ignored."); 588 } 589 } 590 591 @GuardedBy("mStreamLock") handleStreamEventLocked(@arEvsServiceType int origin, @CarEvsStreamEvent int event, CarEvsStreamCallback cb, Executor executor)592 private void handleStreamEventLocked(@CarEvsServiceType int origin, 593 @CarEvsStreamEvent int event, CarEvsStreamCallback cb, Executor executor) { 594 if (mFeatureFlags.carEvsStreamManagement()) { 595 executor.execute(() -> cb.onStreamEvent(origin, event)); 596 } else { 597 executor.execute(() -> cb.onStreamEvent(CarEvsUtils.putTag(origin, event))); 598 } 599 } 600 601 /** 602 * Gets the {@link android.car.evs.CarEvsBufferDescriptor} from the service listener 603 * {@link #CarEvsStreamListenerToService} and dispatches it to an executor provided 604 * to the manager. 605 * 606 * @param buffer {@link android.car.evs.CarEvsBufferDescriptor} 607 */ handleNewFrame(@onNull CarEvsBufferDescriptor buffer)608 private void handleNewFrame(@NonNull CarEvsBufferDescriptor buffer) { 609 int type = mFeatureFlags.carEvsStreamManagement() ? 610 buffer.getType() : CarEvsUtils.getTag(buffer.getId()); 611 Trace.asyncTraceBegin(TraceHelper.TRACE_TAG_CAR_EVS_SERVICE, 612 "CarEvsManager#handleNewFrame", type); 613 614 Objects.requireNonNull(buffer); 615 if (DBG) { 616 Slogf.d(TAG, "Received a buffer: " + buffer); 617 } 618 619 final CarEvsStreamCallback callback; 620 final Executor executor; 621 622 synchronized (mStreamLock) { 623 callback = mStreamCallbacks.get(type); 624 executor = mStreamCallbackExecutor; 625 } 626 627 if (callback != null) { 628 executor.execute(() -> callback.onNewFrame(buffer)); 629 Trace.asyncTraceEnd(TraceHelper.TRACE_TAG_CAR_EVS_SERVICE, 630 "CarEvsManager#handleNewFrame", type); 631 return; 632 } 633 634 if (DBG) { 635 Slogf.w(TAG, "A buffer is being returned back to the service because no active " 636 + "clients exist."); 637 } 638 returnFrameBuffer(buffer); 639 Trace.asyncTraceEnd(TraceHelper.TRACE_TAG_CAR_EVS_SERVICE, 640 "CarEvsManager#handleNewFrame", type); 641 } 642 643 644 /** Stops all active stream callbacks. */ 645 @GuardedBy("mStreamLock") stopVideoStreamLocked()646 private void stopVideoStreamLocked() { 647 if (mStreamCallbacks.size() < 1) { 648 Slogf.i(TAG, "No stream to stop."); 649 return; 650 } 651 652 try { 653 mService.stopVideoStream(mStreamListenerToService); 654 } catch (RemoteException err) { 655 handleRemoteExceptionFromCarService(err); 656 } 657 658 // Wait for a confirmation. 659 for (int i = 0; i < mStreamCallbacks.size(); i++) { 660 if (!mStreamListenerToService.waitForStreamEvent(mStreamCallbacks.keyAt(i), 661 STREAM_EVENT_STREAM_STOPPED)) { 662 Slogf.w(TAG, "EVS did not notify us that target streams are stopped " 663 + "before a time expires."); 664 } 665 666 // Notify clients that streams are stopped. 667 handleStreamEventLocked(mStreamCallbacks.keyAt(i), STREAM_EVENT_STREAM_STOPPED, 668 mStreamCallbacks.valueAt(i), mStreamCallbackExecutor); 669 } 670 671 // We're not interested in frames and events anymore. The client can safely assume 672 // the service is stopped properly. 673 mStreamCallbacks.clear(); 674 mStreamCallbackExecutor = null; 675 } 676 677 /** Stops all active stream callbacks. */ 678 @GuardedBy("mStreamLock") stopVideoStreamLocked(@arEvsServiceType int type)679 private void stopVideoStreamLocked(@CarEvsServiceType int type) { 680 CarEvsStreamCallback cb = mStreamCallbacks.get(type); 681 if (cb == null) { 682 Slogf.i(TAG, "A requested service type " + type + " is not active."); 683 return; 684 } 685 686 try { 687 mService.stopVideoStreamFrom(type, mStreamListenerToService); 688 } catch (RemoteException err) { 689 handleRemoteExceptionFromCarService(err); 690 } 691 692 // Wait for a confirmation. 693 // TODO(b/321913871): Check whether or not we need to verify the origin of a received event. 694 if (!mStreamListenerToService.waitForStreamEvent(type, STREAM_EVENT_STREAM_STOPPED)) { 695 Slogf.w(TAG, "EVS did not notify us that target streams are stopped " 696 + "before a time expires."); 697 } 698 699 // Notify clients that streams are stopped. 700 handleStreamEventLocked(type, STREAM_EVENT_STREAM_STOPPED); 701 702 // We're not interested in frames and events anymore from a given stream type. 703 mStreamCallbacks.remove(type); 704 if (mStreamCallbacks.size() < 1) { 705 // Remove an executor if we stopped listening from the last active stream. 706 mStreamCallbackExecutor = null; 707 } 708 } 709 710 /** 711 * Returns a consumed {@link android.car.evs.CarEvsBufferDescriptor}. 712 * 713 * @param buffer {@link android.car.evs.CarEvsBufferDescriptor} to be returned to 714 * the EVS service. 715 */ 716 @RequiresPermission(Car.PERMISSION_USE_CAR_EVS_CAMERA) returnFrameBuffer(@onNull CarEvsBufferDescriptor buffer)717 public void returnFrameBuffer(@NonNull CarEvsBufferDescriptor buffer) { 718 int type = mFeatureFlags.carEvsStreamManagement() ? 719 buffer.getType() : CarEvsUtils.getTag(buffer.getId()); 720 Trace.asyncTraceBegin(TraceHelper.TRACE_TAG_CAR_EVS_SERVICE, 721 "CarEvsManager#returnFrameBuffer", type); 722 Objects.requireNonNull(buffer); 723 try { 724 mService.returnFrameBuffer(buffer); 725 } catch (RemoteException err) { 726 handleRemoteExceptionFromCarService(err); 727 } finally { 728 // We are done with this HardwareBuffer object. 729 buffer.getHardwareBuffer().close(); 730 Trace.asyncTraceEnd(TraceHelper.TRACE_TAG_CAR_EVS_SERVICE, 731 "CarEvsManager#returnFrameBuffer", type); 732 } 733 } 734 735 /** 736 * Requests the system to start an activity for {@link #CarEvsServiceType}. 737 * 738 * @param type A type of EVS service to start. 739 * @return {@link #CarEvsError} to tell the result of the request. 740 * {@link #ERROR_UNAVAILABLE} will be returned if the CarEvsService is not connected to 741 * the native EVS service or the binder transaction fails. 742 * {@link #ERROR_BUSY} will be returned if the CarEvsService is in the 743 * {@link #SERVICE_STATE_REQUESTED} for a different service type. 744 * If the same service type is running, this will return {@link #ERROR_NONE}. 745 * {@link #ERROR_NONE} will be returned for all other cases. 746 */ 747 @RequiresPermission(Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY) startActivity(@arEvsServiceType int type)748 public @CarEvsError int startActivity(@CarEvsServiceType int type) { 749 try { 750 return mService.startActivity(type); 751 } catch (RemoteException err) { 752 handleRemoteExceptionFromCarService(err); 753 } 754 755 return ERROR_UNAVAILABLE; 756 } 757 758 /** 759 * Requests the system to stop a current activity launched via {@link #startActivity}. 760 */ 761 @RequiresPermission(Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY) stopActivity()762 public void stopActivity() { 763 try { 764 mService.stopActivity(); 765 } catch (RemoteException err) { 766 handleRemoteExceptionFromCarService(err); 767 } 768 } 769 770 /** 771 * Requests to start a video stream from {@link #CarEvsServiceType}. 772 * 773 * @param type A type of EVS service. 774 * @param token A session token that is issued to privileged clients. SystemUI must obtain this 775 * token obtain this via {@link #generateSessionToken} and pass it to the activity, to 776 * prioritize its service requests. 777 * TODO(b/179517136): Defines an Intent extra 778 * @param callback {@link #CarEvsStreamCallback} to listen to the stream. 779 * @param executor {@link java.util.concurrent.Executor} to run a callback. 780 * @return {@link #CarEvsError} to tell the result of the request. 781 * {@link #ERROR_UNAVAILABLE} will be returned if the CarEvsService is not connected to 782 * the native EVS service or the binder transaction fails. 783 * {@link #ERROR_BUSY} will be returned if the CarEvsService is handling a service 784 * request with a valid session token or the same service is already active via another 785 * version of callback object. 786 * {@link #ERROR_NONE} for all other cases. 787 */ 788 @RequiresPermission(Car.PERMISSION_USE_CAR_EVS_CAMERA) startVideoStream( @arEvsServiceType int type, @Nullable IBinder token, @NonNull @CallbackExecutor Executor executor, @NonNull CarEvsStreamCallback callback)789 public @CarEvsError int startVideoStream( 790 @CarEvsServiceType int type, 791 @Nullable IBinder token, 792 @NonNull @CallbackExecutor Executor executor, 793 @NonNull CarEvsStreamCallback callback) { 794 if (DBG) { 795 Slogf.d(TAG, "Received a request to start a video stream: " + type); 796 } 797 798 Objects.requireNonNull(executor); 799 Objects.requireNonNull(callback); 800 801 synchronized (mStreamLock) { 802 mStreamCallbacks.put(type, callback); 803 // TODO(b/321913871): Check whether we want to allow the clients to use more than a 804 // single executor or not. 805 mStreamCallbackExecutor = executor; 806 } 807 808 int status = ERROR_UNAVAILABLE; 809 try { 810 // Requests the service to start a video stream 811 status = mService.startVideoStream(type, token, mStreamListenerToService); 812 } catch (RemoteException err) { 813 handleRemoteExceptionFromCarService(err); 814 } finally { 815 return status; 816 } 817 } 818 819 /** 820 * Requests to stop all active {@link #CarEvsServiceType} streams. 821 */ 822 @RequiresPermission(Car.PERMISSION_USE_CAR_EVS_CAMERA) stopVideoStream()823 public void stopVideoStream() { 824 synchronized (mStreamLock) { 825 stopVideoStreamLocked(); 826 } 827 } 828 829 /** 830 * Requests to stop a given {@link #CarEvsServiceType}. 831 */ 832 @FlaggedApi(FLAG_CAR_EVS_STREAM_MANAGEMENT) 833 @RequiresPermission(Car.PERMISSION_USE_CAR_EVS_CAMERA) stopVideoStream(@arEvsServiceType int type)834 public void stopVideoStream(@CarEvsServiceType int type) { 835 synchronized (mStreamLock) { 836 stopVideoStreamLocked(type); 837 } 838 } 839 840 /** 841 * Queries the current status of the rearview CarEvsService type. 842 * 843 * @return {@link android.car.evs.CarEvsStatus} that describes current status of 844 * the rearview CarEvsService type. 845 */ 846 @RequiresPermission(Car.PERMISSION_MONITOR_CAR_EVS_STATUS) 847 @NonNull getCurrentStatus()848 public CarEvsStatus getCurrentStatus() { 849 try { 850 return mService.getCurrentStatus(SERVICE_TYPE_REARVIEW); 851 } catch (RemoteException err) { 852 Slogf.e(TAG, "Failed to read a status of the service."); 853 return new CarEvsStatus(SERVICE_TYPE_REARVIEW, SERVICE_STATE_UNAVAILABLE); 854 } 855 } 856 857 /** 858 * Queries the current status of a given CarEvsService type. 859 * 860 * @return {@link android.car.evs.CarEvsStatus} that describes current status of 861 * a given CarEvsService type. 862 */ 863 @FlaggedApi(FLAG_CAR_EVS_QUERY_SERVICE_STATUS) 864 @RequiresPermission(Car.PERMISSION_MONITOR_CAR_EVS_STATUS) 865 @Nullable getCurrentStatus(@arEvsServiceType int type)866 public CarEvsStatus getCurrentStatus(@CarEvsServiceType int type) { 867 try { 868 return mService.getCurrentStatus(type); 869 } catch (RemoteException err) { 870 Slogf.e(TAG, "Failed to read a status of the service."); 871 return null; 872 } 873 } 874 875 /** 876 * Generates a service session token. 877 * 878 * @return {@link IBinder} object as a service session token. 879 */ 880 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_EVS_ACTIVITY) 881 @NonNull generateSessionToken()882 public IBinder generateSessionToken() { 883 IBinder token = null; 884 try { 885 token = mService.generateSessionToken(); 886 if (token == null) { 887 token = new Binder(); 888 } 889 } catch (RemoteException err) { 890 Slogf.e(TAG, "Failed to generate a session token."); 891 token = new Binder(); 892 } finally { 893 return token; 894 } 895 896 } 897 898 /** 899 * Returns whether or not a given service type is supported. 900 * 901 * @param type {@link CarEvsServiceType} to query 902 * @return true if a given service type is available on the system. 903 */ 904 @RequiresPermission(Car.PERMISSION_MONITOR_CAR_EVS_STATUS) isSupported(@arEvsServiceType int type)905 public boolean isSupported(@CarEvsServiceType int type) { 906 try { 907 return mService.isSupported(type); 908 } catch (RemoteException err) { 909 Slogf.e(TAG, "Failed to query a service availability"); 910 return false; 911 } 912 } 913 } 914