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