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