• 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 com.android.car.evs;
18 
19 import static android.car.evs.CarEvsManager.ERROR_BUSY;
20 import static android.car.evs.CarEvsManager.ERROR_NONE;
21 import static android.car.evs.CarEvsManager.ERROR_UNAVAILABLE;
22 import static android.car.evs.CarEvsManager.SERVICE_STATE_ACTIVE;
23 import static android.car.evs.CarEvsManager.SERVICE_STATE_INACTIVE;
24 import static android.car.evs.CarEvsManager.SERVICE_STATE_REQUESTED;
25 import static android.car.evs.CarEvsManager.SERVICE_STATE_UNAVAILABLE;
26 import static android.car.evs.CarEvsManager.STREAM_EVENT_STREAM_STOPPED;
27 
28 import static com.android.car.CarLog.TAG_EVS;
29 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
30 
31 import android.annotation.NonNull;
32 import android.annotation.Nullable;
33 import android.car.Car;
34 import android.car.evs.CarEvsBufferDescriptor;
35 import android.car.evs.CarEvsManager;
36 import android.car.evs.CarEvsManager.CarEvsError;
37 import android.car.evs.CarEvsManager.CarEvsServiceState;
38 import android.car.evs.CarEvsManager.CarEvsServiceType;
39 import android.car.evs.CarEvsManager.CarEvsStreamEvent;
40 import android.car.evs.CarEvsStatus;
41 import android.car.evs.ICarEvsStatusListener;
42 import android.car.evs.ICarEvsStreamCallback;
43 import android.car.hardware.CarPropertyValue;
44 import android.car.hardware.property.CarPropertyEvent;
45 import android.car.hardware.property.ICarPropertyEventListener;
46 import android.content.ComponentName;
47 import android.content.Context;
48 import android.content.Intent;
49 import android.content.pm.PackageManager.NameNotFoundException;
50 import android.hardware.HardwareBuffer;
51 import android.hardware.automotive.vehicle.V2_0.VehicleArea;
52 import android.hardware.automotive.vehicle.V2_0.VehicleGear;
53 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
54 import android.os.Binder;
55 import android.os.Build;
56 import android.os.Handler;
57 import android.os.IBinder;
58 import android.os.Looper;
59 import android.os.RemoteCallbackList;
60 import android.os.RemoteException;
61 import android.os.SystemClock;
62 import android.os.UserHandle;
63 import android.util.ArraySet;
64 import android.util.IndentingPrintWriter;
65 import android.util.Log;
66 import android.util.Slog;
67 
68 import com.android.car.CarPropertyService;
69 import com.android.car.CarServiceBase;
70 import com.android.car.ICarImpl;
71 import com.android.car.R;
72 import com.android.car.hal.EvsHalService;
73 import com.android.internal.annotations.GuardedBy;
74 
75 import java.lang.ref.WeakReference;
76 import java.util.List;
77 import java.util.Objects;
78 import java.util.concurrent.CountDownLatch;
79 
80 /**
81  * A service that listens to the Extended View System across a HAL boundary and exposes the data to
82  * system clients in Android via {@link android.car.evs.CarEvsManager}.
83  *
84  * Because of Fast Message Queue usages, android.hardware.automotive.evs@1.1 interfaces does not
85  * support Java backend and, therefore, actual API calls are done in native methods.
86  *
87  *
88  * CarEvsService consists of four states:
89  *
90  * UNAVAILABLE: CarEvsService is not connected to the Extended View System service.  In this
91  * state, any service request will be declined.
92  *
93  * INACTIVE: CarEvsService has a valid, live connection the Extended View System service and
94  * ready for any service requests.
95  *
96  * REQUESTED: CarEvsService received a service requeste from a privileged client and requested
97  * the System UI to launch the camera viewing activity.
98  *
99  * ACTIVE: CarEvsService is actively streaming a video to the client.
100  *
101  * See CarEvsService.StateMachine class for more details.
102  */
103 public final class CarEvsService extends android.car.evs.ICarEvsService.Stub
104         implements CarServiceBase, EvsHalService.EvsHalEventListener {
105 
106     private static final boolean DBG = Log.isLoggable(TAG_EVS, Log.DEBUG);
107 
108     // Integer value to indicate no buffer with a given id exists
109     private static final int BUFFER_NOT_EXIST = -1;
110 
111     // Timeout for a stream-stopped confirmation
112     private static final int STREAM_STOPPED_WAIT_TIMEOUT_MS = 500;
113 
114     // Timeout for a request to start a video stream with a valid token
115     private static final int STREAM_START_REQUEST_TIMEOUT_MS = 3000;
116 
117     // Interval for connecting to the EVS HAL service trial
118     private static final long EVS_HAL_SERVICE_BIND_RETRY_INTERVAL_MS = 1000;
119 
120     // Code to identify a message to request an activity again
121     private static final int MSG_CHECK_ACTIVITY_REQUEST_TIMEOUT = 0;
122 
123     // Service request priorities
124     private static final int REQUEST_PRIORITY_HIGH = 0;
125     private static final int REQUEST_PRIORITY_NORMAL = 1;
126     private static final int REQUEST_PRIORITY_LOW = 2;
127 
128     private static final class EvsHalEvent {
129         private long mTimestamp;
130         private int mServiceType;
131         private boolean mOn;
132 
EvsHalEvent(long timestamp, @CarEvsServiceType int type, boolean on)133         public EvsHalEvent(long timestamp, @CarEvsServiceType int type, boolean on) {
134             mTimestamp = timestamp;
135             mServiceType = type;
136             mOn = on;
137         }
138 
getTimestamp()139         public long getTimestamp() {
140             return mTimestamp;
141         }
142 
getServiceType()143         public @CarEvsServiceType int getServiceType() {
144             return mServiceType;
145         }
146 
isRequestingToStartActivity()147         public boolean isRequestingToStartActivity() {
148             return mOn;
149         }
150 
toString()151         public String toString() {
152             return "ServiceType = " + mServiceType + ", mOn = " + mOn +
153                     ", Timestamp = " + mTimestamp;
154         }
155     }
156 
157     private static final String COMMAND_TO_USE_DEFAULT_CAMERA = "default";
158 
159     private final Context mContext;
160     private final EvsHalService mEvsHalService;
161     private final CarPropertyService mPropertyService;
162     private final Object mLock = new Object();
163 
164     private final ComponentName mEvsCameraActivity;
165 
166     // This handler is to monitor the client sends a video stream request within a given time
167     // after a state transition to the REQUESTED state.
168     private final Handler mHandler = new Handler(Looper.getMainLooper());
169 
170     // Bookkeeps received frame buffers
171     private final ArraySet mBufferRecords = new ArraySet();
172 
173     private final class StatusListenerList extends RemoteCallbackList<ICarEvsStatusListener> {
174         private final WeakReference<CarEvsService> mService;
175 
StatusListenerList(CarEvsService evsService)176         StatusListenerList(CarEvsService evsService) {
177             mService = new WeakReference<>(evsService);
178         }
179 
180         /** Handle callback death */
181         @Override
onCallbackDied(ICarEvsStatusListener listener)182         public void onCallbackDied(ICarEvsStatusListener listener) {
183             Slog.w(TAG_EVS, "StatusListener has died: " + listener.asBinder());
184 
185             CarEvsService svc = mService.get();
186             if (svc != null) {
187                 svc.handleClientDisconnected(listener);
188             }
189         }
190     }
191 
192     @GuardedBy("mLock")
193     private final StatusListenerList mStatusListeners = new StatusListenerList(this);
194 
195     private final IBinder.DeathRecipient mStreamCallbackDeathRecipient =
196             new IBinder.DeathRecipient() {
197         @Override
198         public void binderDied() {
199             Slog.w(TAG_EVS, "StreamCallback has died");
200             synchronized (mLock) {
201                 if (requestActivityIfNecessaryLocked()) {
202                     Slog.i(TAG_EVS, "Requested to launch the activity.");
203                 }
204             }
205         }
206     };
207 
208     /**
209      * {@link CarPropertyEvent} listener registered with {@link CarPropertyService} to listen to
210      * {@link VehicleProperty.GEAR_SELECTION} change notifications.
211      */
212     private final ICarPropertyEventListener mGearSelectionPropertyListener =
213             new ICarPropertyEventListener.Stub() {
214                 @Override
215                 public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
216                     synchronized (mLock) {
217                         // Handle only the latest event
218                         Slog.e(TAG_EVS, "Handling GearSelection");
219                         handlePropertyEventLocked(events.get(events.size() - 1));
220                     }
221                 }
222             };
223 
224     // CarEvsService state machine implementation to handle all state transitions.
225     private final class StateMachine {
226         // Current state
227         @GuardedBy("mLock")
228         private int mState = SERVICE_STATE_UNAVAILABLE;
229 
230         // Current service type
231         @GuardedBy("mLock")
232         private int mServiceType = CarEvsManager.SERVICE_TYPE_REARVIEW;
233 
234         // Priority of a last service request
235         @GuardedBy("mLock")
236         private int mLastRequestPriority = REQUEST_PRIORITY_LOW;
237 
execute(int priority, int destination)238         public @CarEvsError int execute(int priority, int destination) {
239             return execute(priority, destination, mServiceType, null, null);
240         }
241 
execute(int priority, int destination, int service)242         public @CarEvsError int execute(int priority, int destination, int service) {
243             return execute(priority, destination, service, null, null);
244         }
245 
execute(int priority, int destination, ICarEvsStreamCallback callback)246         public @CarEvsError int execute(int priority, int destination,
247                 ICarEvsStreamCallback callback) {
248             return execute(priority, destination, mServiceType, null, callback);
249         }
250 
execute(int priority, int destination, int service, IBinder token, ICarEvsStreamCallback callback)251         public @CarEvsError int execute(int priority, int destination, int service, IBinder token,
252                 ICarEvsStreamCallback callback) {
253 
254             int result = ERROR_NONE;
255             synchronized (mLock) {
256                 // TODO(b/188970686): Reduce this lock duration.
257                 if (mState == destination && destination != SERVICE_STATE_REQUESTED) {
258                     // Nothing to do
259                     return ERROR_NONE;
260                 }
261 
262                 int previousState = mState;
263                 Slog.d(TAG_EVS, "Transition requested: " + toString(previousState) +
264                         " -> " + toString(destination));
265 
266                 switch (destination) {
267                     case SERVICE_STATE_UNAVAILABLE:
268                         result = handleTransitionToUnavailableLocked();
269                         break;
270 
271                     case SERVICE_STATE_INACTIVE:
272                         result = handleTransitionToInactiveLocked(priority, service, callback);
273                         break;
274 
275                     case SERVICE_STATE_REQUESTED:
276                         result = handleTransitionToRequestedLocked(priority, service);
277                         break;
278 
279                     case SERVICE_STATE_ACTIVE:
280                         result = handleTransitionToActiveLocked(priority, service, token, callback);
281                         break;
282 
283                     default:
284                         throw new IllegalStateException(
285                                 "CarEvsService is in the unknown state, " + previousState);
286                 }
287             }
288 
289             if (result == ERROR_NONE) {
290                 // Broadcasts current state
291                 broadcastStateTransition(mServiceType, mState);
292             }
293 
294             return result;
295         }
296 
getState()297         public @CarEvsServiceState int getState() {
298             synchronized (mLock) {
299                 return mState;
300             }
301         }
302 
getServiceType()303         public @CarEvsServiceType int getServiceType() {
304             synchronized (mLock) {
305                 return mServiceType;
306             }
307         }
308 
getStateAndServiceType()309         public CarEvsStatus getStateAndServiceType() {
310             synchronized (mLock) {
311                 return new CarEvsStatus(mServiceType, mState);
312             }
313         }
314 
315         @GuardedBy("mLock")
checkCurrentStateRequiresActivityLocked()316         public boolean checkCurrentStateRequiresActivityLocked() {
317             return mState == SERVICE_STATE_ACTIVE || mState == SERVICE_STATE_REQUESTED;
318         }
319 
320 
321         @GuardedBy("mLock")
handleTransitionToUnavailableLocked()322         private @CarEvsError int handleTransitionToUnavailableLocked() {
323             // This transition happens only when CarEvsService loses the active connection to the
324             // Extended View System service.
325             switch (mState) {
326                 case SERVICE_STATE_UNAVAILABLE:
327                     // Nothing to do
328                     break;
329 
330                 default:
331                     // Stops any active video stream
332                     stopService();
333                     break;
334             }
335 
336             mState = SERVICE_STATE_UNAVAILABLE;
337             return ERROR_NONE;
338         }
339 
340         @GuardedBy("mLock")
handleTransitionToInactiveLocked(int priority, int service, ICarEvsStreamCallback callback)341         private @CarEvsError int handleTransitionToInactiveLocked(int priority, int service,
342                 ICarEvsStreamCallback callback) {
343 
344             switch (mState) {
345                 case SERVICE_STATE_UNAVAILABLE:
346                     if (callback != null) {
347                         // We get a request to stop a video stream after losing a native EVS
348                         // service.  Simply unregister a callback and return.
349                         unlinkToDeathStreamCallbackLocked();
350                         mStreamCallback = null;
351                         return ERROR_NONE;
352                     } else {
353                         // Requested to connect to the Extended View System service
354                         if (!nativeConnectToHalServiceIfNecessary(mNativeEvsServiceObj)) {
355                             return ERROR_UNAVAILABLE;
356                         }
357                     }
358                     break;
359 
360                 case SERVICE_STATE_INACTIVE:
361                     // Nothing to do
362                     break;
363 
364                 case SERVICE_STATE_REQUESTED:
365                     // Requested to cancel a pending service request
366                     if (mServiceType != service || mLastRequestPriority > priority) {
367                         return ERROR_BUSY;
368                     }
369 
370                     // Reset a timer for this new request
371                     mHandler.removeMessages(MSG_CHECK_ACTIVITY_REQUEST_TIMEOUT);
372                     break;
373 
374                 case SERVICE_STATE_ACTIVE:
375                     // Requested to stop a current video stream
376                     if (mServiceType != service || mLastRequestPriority > priority) {
377                         return ERROR_BUSY;
378                     }
379 
380                     if (callback != null) {
381                         stopVideoStreamAndUnregisterCallback(callback);
382                     } else {
383                         stopService();
384                     }
385                     break;
386 
387                 default:
388                     throw new IllegalStateException("CarEvsService is in the unknown state.");
389             }
390 
391             mState = SERVICE_STATE_INACTIVE;
392             setSessionToken(null);
393             return ERROR_NONE;
394         }
395 
396         @GuardedBy("mLock")
handleTransitionToRequestedLocked(int priority, int service)397         private @CarEvsError int handleTransitionToRequestedLocked(int priority, int service) {
398             switch (mState) {
399                 case SERVICE_STATE_UNAVAILABLE:
400                     // Attempts to connect to the native EVS service and transits to the
401                     // REQUESTED state if it succeeds.
402                     if (!nativeConnectToHalServiceIfNecessary(mNativeEvsServiceObj)) {
403                         return ERROR_UNAVAILABLE;
404                     }
405                     break;
406 
407                 case SERVICE_STATE_INACTIVE:
408                     // Nothing to do
409                     break;
410 
411                 case SERVICE_STATE_REQUESTED:
412                     if (priority > mLastRequestPriority) {
413                         // A current service request has a lower priority than a previous
414                         // service request.
415                         Slog.e(TAG_EVS,
416                                 "CarEvsService is busy with a higher priority client.");
417                         return ERROR_BUSY;
418                     }
419 
420                     // Reset a timer for this new request
421                     mHandler.removeMessages(MSG_CHECK_ACTIVITY_REQUEST_TIMEOUT);
422                     break;
423 
424                 case SERVICE_STATE_ACTIVE:
425                     if (priority > mLastRequestPriority) {
426                         // We decline a request because CarEvsService is busy with a higher priority
427                         // client.
428                         return ERROR_BUSY;
429                     } else if (priority == mLastRequestPriority) {
430                         // We do not need to transit to the REQUESTED state because CarEvsService
431                         // was transited to the ACTIVE state by a request that has the same priority
432                         // with current request.
433                         return ERROR_NONE;
434                     } else {
435                         // Stop stream on all lower priority clients.
436                         processStreamEvent(STREAM_EVENT_STREAM_STOPPED);
437                     }
438                     break;
439 
440                 default:
441                     throw new IllegalStateException("CarEvsService is in the unknown state.");
442             }
443 
444             // Arms the timer
445             mHandler.sendMessageDelayed(obtainMessage(CarEvsService::handleActivityRequestTimeout,
446                     CarEvsService.this).setWhat(MSG_CHECK_ACTIVITY_REQUEST_TIMEOUT),
447                     STREAM_START_REQUEST_TIMEOUT_MS);
448 
449             mState = SERVICE_STATE_REQUESTED;
450             mServiceType = service;
451             mLastRequestPriority = priority;
452 
453             if (mEvsCameraActivity != null) {
454                 Intent evsIntent = new Intent(Intent.ACTION_MAIN)
455                         .setComponent(mEvsCameraActivity)
456                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
457                         .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
458                         .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
459                         .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
460                 if (priority == REQUEST_PRIORITY_HIGH) {
461                     mSessionToken = new Binder();
462                     evsIntent.putExtra(CarEvsManager.EXTRA_SESSION_TOKEN, mSessionToken);
463                 }
464                 mContext.startActivity(evsIntent);
465             }
466             return ERROR_NONE;
467         }
468 
469         @GuardedBy("mLock")
handleTransitionToActiveLocked(int priority, int service, IBinder token, ICarEvsStreamCallback callback)470         private @CarEvsError int handleTransitionToActiveLocked(int priority, int service,
471                 IBinder token, ICarEvsStreamCallback callback) {
472 
473             @CarEvsError int result = ERROR_NONE;
474             switch (mState) {
475                 case SERVICE_STATE_UNAVAILABLE:
476                     // We do not have a valid connection to the Extended View System service.
477                     return ERROR_UNAVAILABLE;
478 
479                 case SERVICE_STATE_INACTIVE:
480                     // CarEvsService receives a low priority request to start a video stream.
481                     result = startServiceAndVideoStream(service, callback);
482                     if (result != ERROR_NONE) {
483                         return result;
484                     }
485                     break;
486 
487                 case SERVICE_STATE_REQUESTED:
488                     // CarEvsService is reserved for higher priority clients.
489                     if (priority == REQUEST_PRIORITY_HIGH && !isSessionToken(token)) {
490                         // Declines a request with an expired token.
491                         return ERROR_BUSY;
492                     }
493 
494                     result = startServiceAndVideoStream(service, callback);
495                     if (result != ERROR_NONE) {
496                         return result;
497                     }
498                     break;
499 
500                 case SERVICE_STATE_ACTIVE:
501                     // CarEvsManager will transfer an active video stream to a new client with a
502                     // higher or equal priority.
503                     if (priority > mLastRequestPriority) {
504                         Slog.i(TAG_EVS, "Declines a service request with a lower priority.");
505                         break;
506                     }
507 
508                     if (mStreamCallback != null) {
509                         mHandler.sendMessage(obtainMessage(
510                                 CarEvsService::notifyStreamStopped, mStreamCallback));
511                     }
512 
513                     mStreamCallback = callback;
514                     break;
515 
516                 default:
517                     throw new IllegalStateException("CarEvsService is in the unknown state.");
518             }
519 
520             mState = SERVICE_STATE_ACTIVE;
521             mServiceType = service;
522             mLastRequestPriority = priority;
523             return ERROR_NONE;
524         }
525 
toString(@arEvsServiceState int state)526         private String toString(@CarEvsServiceState int state) {
527             switch (state) {
528                 case SERVICE_STATE_UNAVAILABLE:
529                     return "UNAVAILABLE";
530                 case SERVICE_STATE_INACTIVE:
531                     return "INACTIVE";
532                 case SERVICE_STATE_REQUESTED:
533                     return "REQUESTED";
534                 case SERVICE_STATE_ACTIVE:
535                     return "ACTIVE";
536                 default:
537                     return "UNKNOWN";
538             }
539         }
540 
toString()541         public String toString() {
542             return toString(mState);
543         }
544     }
545 
546     private final StateMachine mStateEngine = new StateMachine();
547 
548     @GuardedBy("mLock")
549     private ICarEvsStreamCallback mStreamCallback = null;
550 
551     // The latest session token issued to the privileged clients
552     @GuardedBy("mLock")
553     private IBinder mSessionToken = null;
554 
555     // This boolean flag is true if CarEvsService uses GEAR_SELECTION VHAL property instead of
556     // EVS_SERVICE_REQUEST.
557     private boolean mUseGearSelection = true;
558 
559     // When this is set, CarEvsService will attempt to open a camera device the user sets.
560     private boolean mUseCameraIdOverride = false;
561 
562     // This is a device name to be used when mUseCameraIdOverride is true.
563     private String mCameraIdOverride;
564 
setSessionToken(IBinder token)565     private void setSessionToken(IBinder token) {
566         synchronized (mLock) {
567             mSessionToken = token;
568         }
569     }
570 
isSessionToken(IBinder token)571     private boolean isSessionToken(IBinder token) {
572         synchronized (mLock) {
573             return token != null && token == mSessionToken;
574         }
575     }
576 
577     // Synchronization object for a stream-stopped confirmation
578     private CountDownLatch mStreamStoppedEvent = new CountDownLatch(0);
579 
580     // The last event EvsHalService reported.  This will be set to null when a related service
581     // request is handled.
582     @GuardedBy("mLock")
583     private EvsHalEvent mLastEvsHalEvent = new EvsHalEvent(SystemClock.elapsedRealtimeNanos(),
584             CarEvsManager.SERVICE_TYPE_REARVIEW, /* on = */false);
585 
586     // Stops a current video stream and unregisters a callback
stopVideoStreamAndUnregisterCallback(ICarEvsStreamCallback callback)587     private void stopVideoStreamAndUnregisterCallback(ICarEvsStreamCallback callback) {
588         synchronized (mLock) {
589             if (callback.asBinder() != mStreamCallback.asBinder()) {
590                 Slog.i(TAG_EVS, "Declines a request to stop a video not from a current client.");
591                 return;
592             }
593 
594             unlinkToDeathStreamCallbackLocked();
595             mStreamCallback = null;
596             Slog.i(TAG_EVS, "Last stream client has been disconnected.");
597             nativeRequestToStopVideoStream(mNativeEvsServiceObj);
598         }
599     }
600 
601     // Starts a service and its video stream
602     @GuardedBy("mLock")
startServiceAndVideoStream( @arEvsServiceType int service, ICarEvsStreamCallback callback)603     private @CarEvsError int startServiceAndVideoStream(
604             @CarEvsServiceType int service, ICarEvsStreamCallback callback) {
605         if (!startService(service)) {
606             return ERROR_UNAVAILABLE;
607         }
608 
609         mStreamCallback = callback;
610         linkToDeathStreamCallbackLocked();
611 
612         if (!nativeRequestToStartVideoStream(mNativeEvsServiceObj)) {
613             Slog.e(TAG_EVS, "Failed to start a video stream");
614             mStreamCallback = null;
615             return ERROR_UNAVAILABLE;
616         }
617 
618         return ERROR_NONE;
619     }
620 
621     @GuardedBy("mLock")
requestActivityIfNecessaryLocked()622     private boolean requestActivityIfNecessaryLocked() {
623         if (!mStateEngine.checkCurrentStateRequiresActivityLocked() || mLastEvsHalEvent == null ||
624                 !mLastEvsHalEvent.isRequestingToStartActivity()) {
625             return false;
626         }
627 
628         mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_REQUESTED,
629                 mLastEvsHalEvent.getServiceType());
630         return true;
631     }
632 
633     // Waits for a video stream request from the System UI with a valid token.
handleActivityRequestTimeout()634     private void handleActivityRequestTimeout() {
635         synchronized (mLock) {
636             // No client has responded to a state transition to the REQUESTED
637             // state before the timer expires.  CarEvsService sends a
638             // notification again if it's still needed.
639             if (requestActivityIfNecessaryLocked()) {
640                 Slog.w(TAG_EVS, "Timer expired.  Request to launch the activity again.");
641                 return;
642             } else if (mStateEngine.getState() == SERVICE_STATE_REQUESTED) {
643                 // If the service is no longer required by other services, we transit to
644                 // the INACTIVE state.
645                 mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE);
646             }
647         }
648     }
649 
650     @GuardedBy("mLock")
linkToDeathStreamCallbackLocked()651     private void linkToDeathStreamCallbackLocked() {
652         IBinder binder;
653         if (mStreamCallback == null) {
654             return;
655         }
656 
657         binder = mStreamCallback.asBinder();
658         if (binder == null) {
659             Slog.w(TAG_EVS, "Linking to a binder death recipient skipped");
660             return;
661         }
662 
663         try {
664             binder.linkToDeath(mStreamCallbackDeathRecipient, 0);
665         } catch (RemoteException e) {
666             Slog.w(TAG_EVS, "Failed to link a binder death recipient: " + e);
667         }
668     }
669 
670     @GuardedBy("mLock")
unlinkToDeathStreamCallbackLocked()671     private void unlinkToDeathStreamCallbackLocked() {
672         IBinder binder;
673         if (mStreamCallback == null) {
674             return;
675         }
676 
677         binder = mStreamCallback.asBinder();
678         if (binder == null) {
679             return;
680         }
681 
682         binder.unlinkToDeath(mStreamCallbackDeathRecipient, 0);
683     }
684 
685     /** Creates an Extended View System service instance given a {@link Context}. */
CarEvsService(Context context, EvsHalService halService, CarPropertyService propertyService)686     public CarEvsService(Context context, EvsHalService halService,
687             CarPropertyService propertyService) {
688         mContext = context;
689         mPropertyService = propertyService;
690         mEvsHalService = halService;
691 
692         String activityName = mContext.getResources().getString(R.string.config_evsCameraActivity);
693         if (!activityName.isEmpty()) {
694             mEvsCameraActivity = ComponentName.unflattenFromString(activityName);
695         } else {
696             mEvsCameraActivity = null;
697         }
698         if (DBG) Slog.d(TAG_EVS, "evsCameraActivity=" + mEvsCameraActivity);
699     }
700 
701     /** Implements EvsHalService.EvsHalEventListener to monitor VHAL properties. */
702     @Override
onEvent(@arEvsServiceType int type, boolean on)703     public void onEvent(@CarEvsServiceType int type, boolean on) {
704         if (DBG) {
705             Slog.d(TAG_EVS,
706                     "Received an event from EVS HAL: type = " + type + ", on = " + on);
707         }
708 
709         synchronized (mLock) {
710             int targetState = on ? SERVICE_STATE_REQUESTED : SERVICE_STATE_INACTIVE;
711             if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, targetState, type) !=
712                     ERROR_NONE) {
713                 Slog.e(TAG_EVS, "Failed to execute a service request.");
714             }
715 
716             // Stores the last event
717             mLastEvsHalEvent = new EvsHalEvent(SystemClock.elapsedRealtimeNanos(), type, on);
718         }
719     }
720 
721     @Override
init()722     public void init() {
723         if (DBG) {
724             Slog.d(TAG_EVS, "Initializing the service");
725         }
726 
727         if (mEvsHalService.isEvsServiceRequestSupported()) {
728             try {
729                 mEvsHalService.setListener(this);
730                 if (DBG) {
731                     Slog.d(TAG_EVS, "CarEvsService listens to EVS_SERVICE_REQUEST property.");
732                 }
733                 mUseGearSelection = false;
734             } catch (IllegalStateException e) {
735                 Slog.w(TAG_EVS,
736                         "Failed to set a EvsHalService listener.  Try to use GEAR_SELECTION.");
737             }
738         }
739 
740         if (mUseGearSelection) {
741             if (DBG) {
742                 Slog.d(TAG_EVS, "CarEvsService listens to GEAR_SELECTION property.");
743             }
744 
745             if (mPropertyService == null ||
746                 mPropertyService.getProperty(VehicleProperty.GEAR_SELECTION,
747                         VehicleArea.GLOBAL) == null) {
748                 Slog.e(TAG_EVS,
749                         "CarEvsService is disabled because GEAR_SELECTION is unavailable.");
750                 mUseGearSelection = false;
751                 return;
752             }
753 
754             mPropertyService.registerListener(VehicleProperty.GEAR_SELECTION, /*rate=*/0,
755                     mGearSelectionPropertyListener);
756         }
757 
758         // Creates a handle to use the native Extended View System service
759         mNativeEvsServiceObj = CarEvsService.nativeCreateServiceHandle();
760 
761         // Attempts to transit to the INACTIVE state
762         if (mNativeEvsServiceObj == 0 ||
763             mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE) != ERROR_NONE) {
764             Slog.e(TAG_EVS, "Failed to create a service handle or transit to the INACTIVE state,");
765             CarEvsService.nativeDestroyServiceHandle(mNativeEvsServiceObj);
766             mNativeEvsServiceObj = 0;
767 
768             if (mUseGearSelection && mPropertyService != null) {
769                 if (DBG) {
770                     Slog.d(TAG_EVS, "Unregister a property listener on init() failure.");
771                 }
772                 mPropertyService.unregisterListener(VehicleProperty.GEAR_SELECTION,
773                         mGearSelectionPropertyListener);
774             }
775         }
776     }
777 
778     @Override
release()779     public void release() {
780         if (DBG) {
781             Slog.d(TAG_EVS, "Finalizing the service");
782         }
783 
784         synchronized (mLock) {
785             if (mUseGearSelection && mPropertyService != null) {
786                 if (DBG) {
787                     Slog.d(TAG_EVS, "Unregister a property listener in release()");
788                 }
789                 mPropertyService.unregisterListener(VehicleProperty.GEAR_SELECTION,
790                         mGearSelectionPropertyListener);
791             }
792             mStatusListeners.kill();
793         }
794 
795         CarEvsService.nativeDestroyServiceHandle(mNativeEvsServiceObj);
796         mNativeEvsServiceObj = 0;
797     }
798 
799     @Override
dump(IndentingPrintWriter writer)800     public void dump(IndentingPrintWriter writer) {
801         writer.println("*CarEvsService*");
802         writer.printf("Current state = %s\n", mStateEngine);
803         writer.printf("%s to HAL service\n",
804                 mNativeEvsServiceObj == 0 ? "Not connected" : "Connected");
805 
806         ICarEvsStreamCallback cb;
807         synchronized (mLock) {
808             cb = mStreamCallback;
809         }
810         writer.printf("Active stream client = %s\n", cb == null? "null" : cb.asBinder());
811         writer.printf("%d service listeners subscribed.\n",
812                 mStatusListeners.getRegisteredCallbackCount());
813         writer.printf("Last HAL event = %s\n", mLastEvsHalEvent);
814         writer.printf("Current session token = %s\n", mSessionToken);
815     }
816 
817     /**
818      * Registers a {@link ICarEvsStatusListener} to listen requests to control the camera
819      * previewing activity.
820      *
821      * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to
822      * access.
823      *
824      * @param listener {@link ICarEvsStatusListener} listener to register.
825      */
826     @Override
registerStatusListener(@onNull ICarEvsStatusListener listener)827     public void registerStatusListener(@NonNull ICarEvsStatusListener listener) {
828         ICarImpl.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
829         Objects.requireNonNull(listener);
830 
831         if (DBG) {
832             Slog.d(TAG_EVS, "Registering a new service listener");
833         }
834         mStatusListeners.register(listener);
835     }
836 
837     /**
838      * Unregister the given {@link ICarEvsStatusListener} listener from receiving events.
839      *
840      * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to
841      * access.
842      *
843      * @param listener {@link ICarEvsStatusListener} listener to unregister.
844      */
845     @Override
unregisterStatusListener(@onNull ICarEvsStatusListener listener)846     public void unregisterStatusListener(@NonNull ICarEvsStatusListener listener) {
847         ICarImpl.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
848         Objects.requireNonNull(listener);
849 
850         mStatusListeners.unregister(listener);
851     }
852 
853     /**
854      * Requests the system to start an activity to show the preview from a given EVS service type.
855      *
856      * <p>Requires {@link android.car.Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY} permissions to
857      * access.
858      *
859      * @param type {@link android.car.evs.CarEvsManager#CarEvsServiceType}
860      * @return {@link android.car.evs.CarEvsManager#CarEvsError}
861      */
862     @Override
startActivity(int type)863     public @CarEvsError int startActivity(int type) {
864         ICarImpl.assertPermission(mContext, Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY);
865 
866         return mStateEngine.execute(REQUEST_PRIORITY_NORMAL, SERVICE_STATE_REQUESTED, type);
867     }
868 
869     /**
870      * Requests to stop a current previewing activity launched via {@link #startActivity}.
871      *
872      * <p>Requires {@link android.car.Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY} permissions to
873      * access.
874      */
875     @Override
stopActivity()876     public void stopActivity() {
877         ICarImpl.assertPermission(mContext, Car.PERMISSION_REQUEST_CAR_EVS_ACTIVITY);
878 
879         mStateEngine.execute(REQUEST_PRIORITY_NORMAL, SERVICE_STATE_INACTIVE);
880     }
881 
882     /**
883      * Starts a video stream.
884      *
885      * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access.
886      *
887      * @param type {@link android.car.evs.CarEvsManager#CarEvsServiceType}
888      * @param token IBinder object as a session token.  If this is not null, CarEvsService handles a
889      *              coming client as a privileged client.
890      * @param callback {@link ICarEvsStreamCallback} listener to register.
891      * @return {@link android.car.evs.CarEvsManager.CarEvsError}
892      */
893     @Override
startVideoStream(@arEvsServiceType int type, @Nullable IBinder token, @NonNull ICarEvsStreamCallback callback)894     public @CarEvsError int startVideoStream(@CarEvsServiceType int type, @Nullable IBinder token,
895             @NonNull ICarEvsStreamCallback callback) {
896         ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
897         Objects.requireNonNull(callback);
898 
899         if (isSessionToken(token)) {
900             mHandler.removeMessages(MSG_CHECK_ACTIVITY_REQUEST_TIMEOUT);
901         }
902 
903         final int priority = token != null ? REQUEST_PRIORITY_HIGH : REQUEST_PRIORITY_LOW;
904         return mStateEngine.execute(priority, SERVICE_STATE_ACTIVE, type, token, callback);
905     }
906 
907     /**
908      * Requests to stop a video stream from the current service.
909      *
910      * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access.
911      *
912      * @param callback {@link ICarEvsStreamCallback} listener to unregister.
913      */
914     @Override
stopVideoStream(@onNull ICarEvsStreamCallback callback)915     public void stopVideoStream(@NonNull ICarEvsStreamCallback callback) {
916         ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
917         Objects.requireNonNull(callback);
918 
919         synchronized (mLock) {
920             if (mStreamCallback == null || callback.asBinder() != mStreamCallback.asBinder()) {
921                 Slog.i(TAG_EVS, "Ignores a video stream request not from current stream client.");
922                 return;
923             }
924         }
925 
926         if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE, callback) !=
927                 ERROR_NONE) {
928             Slog.w(TAG_EVS, "Failed to stop a video stream");
929 
930             // We want to return if a video stop request fails.
931             return;
932         }
933     }
934 
935     /**
936      * Returns an used buffer to EVS service.
937      *
938      * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access.
939      *
940      * @param bufferId An unique 32-bit integer identifier of the buffer to return.
941      * @throws IllegalArgumentException if a passed buffer has an unregistered identifier.
942      */
943     @Override
returnFrameBuffer(int bufferId)944     public void returnFrameBuffer(int bufferId) {
945         ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
946 
947         boolean returnThisBuffer = false;
948         synchronized (mLock) {
949             if (!mBufferRecords.contains(bufferId)) {
950                 Slog.w(TAG_EVS,
951                         "Ignores a request to return a buffer with unknown id = " + bufferId);
952                 return;
953             }
954 
955             mBufferRecords.remove(bufferId);
956         }
957 
958         // This may throw a NullPointerException if the native EVS service handle is invalid.
959         nativeDoneWithFrame(mNativeEvsServiceObj, bufferId);
960     }
961 
962     /**
963      * Returns a current status of CarEvsService.
964      *
965      * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to
966      * access.
967      *
968      * @return {@link android.car.evs.CarEvsStatus}
969      */
970     @Override
971     @Nullable
getCurrentStatus()972     public CarEvsStatus getCurrentStatus() {
973         ICarImpl.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
974 
975         return mStateEngine.getStateAndServiceType();
976     }
977 
978     // TODO(b/157082995): Replaces below method with what PackageManager provides.
979     @Nullable
getSystemUiPackageName()980     private String getSystemUiPackageName() {
981         try {
982             ComponentName componentName = ComponentName.unflattenFromString(mContext.getResources()
983                     .getString(com.android.internal.R.string.config_systemUIServiceComponent));
984             return componentName.getPackageName();
985         } catch (RuntimeException e) {
986             throw new IllegalStateException("error while getting system UI package name.");
987         }
988     }
989 
990     /**
991      * Returns a session token to be used to request the services.
992      *
993      * <p>Requires {@link android.car.Car.PERMISSION_CONTROL_CAR_EVS_ACTIVITY} permission to access.
994      *
995      * @return IBinder object as a session token.
996      * @throws IllegalStateException if we fail to find System UI package.
997      */
998     @Override
generateSessionToken()999     public IBinder generateSessionToken() {
1000         ICarImpl.assertPermission(mContext, Car.PERMISSION_CONTROL_CAR_EVS_ACTIVITY);
1001 
1002         String systemUiPackageName = getSystemUiPackageName();
1003         IBinder token = new Binder();
1004         try {
1005             int systemUiUid = mContext.getPackageManager().getPackageUidAsUser(
1006                     systemUiPackageName, UserHandle.USER_SYSTEM);
1007             int callerUid = Binder.getCallingUid();
1008             if (systemUiUid == callerUid) {
1009                 setSessionToken(token);
1010             } else {
1011                 throw new SecurityException("SystemUI only can generate SessionToken.");
1012             }
1013         } catch (NameNotFoundException err) {
1014             throw new IllegalStateException(systemUiPackageName + " package not found.");
1015         } finally {
1016             return token;
1017         }
1018     }
1019 
handleClientDisconnected(ICarEvsStatusListener listener)1020     private void handleClientDisconnected(ICarEvsStatusListener listener) {
1021         synchronized (mLock) {
1022             mStatusListeners.unregister(listener);
1023             if (mStatusListeners.getRegisteredCallbackCount() == 0) {
1024                 Slog.d(TAG_EVS, "Last status listener has been disconnected.");
1025             }
1026         }
1027     }
1028 
1029     /**
1030      * Returns whether or not a given service type is supported.
1031      *
1032      * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to
1033      * access.
1034      */
1035     @Override
isSupported(@arEvsServiceType int type)1036     public boolean isSupported(@CarEvsServiceType int type) {
1037         ICarImpl.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
1038 
1039         switch (type) {
1040             case CarEvsManager.SERVICE_TYPE_REARVIEW:
1041                 // We store a handle to the native EVS service in mNativeEvsServiceObj variable.
1042                 return mNativeEvsServiceObj != 0;
1043 
1044             case CarEvsManager.SERVICE_TYPE_SURROUNDVIEW:
1045                 // TODO(b/179029031): Implements necessary logic when Surround View service is
1046                 // integrated.
1047                 return false;
1048 
1049             default:
1050                 throw new IllegalArgumentException("Unknown service type = " + type);
1051         }
1052     }
1053 
1054     /**
1055      * Sets a camera device for the rearview.
1056      *
1057      * <p>Requires {@link android.car.Car.PERMISSION_USE_CAR_EVS_CAMERA} permissions to access.
1058      *
1059      * @param id A string identifier of a target camera device.
1060      * @return This method return a false if this runs in a release build; otherwise, this returns
1061      *         true.
1062      */
setRearviewCameraIdFromCommand(@onNull String id)1063     public boolean setRearviewCameraIdFromCommand(@NonNull String id) {
1064         ICarImpl.assertPermission(mContext, Car.PERMISSION_USE_CAR_EVS_CAMERA);
1065         Objects.requireNonNull(id);
1066 
1067         if (!Build.IS_DEBUGGABLE) {
1068             // This method is not allowed in the release build.
1069             return false;
1070         }
1071 
1072         if (id.equalsIgnoreCase(COMMAND_TO_USE_DEFAULT_CAMERA)) {
1073             mUseCameraIdOverride = false;
1074             Slog.i(TAG_EVS, "CarEvsService is set to use the default device for the rearview.");
1075         } else {
1076             mCameraIdOverride = id;
1077             mUseCameraIdOverride = true;
1078             Slog.i(TAG_EVS, "CarEvsService is set to use " + id + " for the rearview.");
1079         }
1080 
1081         return true;
1082     }
1083 
1084     /**
1085      * Gets an identifier of a current camera device for the rearview.
1086      *
1087      * <p>Requires {@link android.car.Car.PERMISSION_MONITOR_CAR_EVS_STATUS} permissions to
1088      * access.
1089      *
1090      * @return A string identifier of current rearview camera device.
1091      */
1092     @NonNull
getRearviewCameraIdFromCommand()1093     public String getRearviewCameraIdFromCommand() {
1094         ICarImpl.assertPermission(mContext, Car.PERMISSION_MONITOR_CAR_EVS_STATUS);
1095         if (mUseCameraIdOverride) {
1096             return mCameraIdOverride;
1097         } else {
1098             return mContext.getString(R.string.config_evsRearviewCameraId);
1099         }
1100     }
1101 
1102     /** Handles client disconnections; may request to stop a video stream. */
handleClientDisconnected(ICarEvsStreamCallback callback)1103     private void handleClientDisconnected(ICarEvsStreamCallback callback) {
1104         // If the last stream client is disconnected before it stops a video stream, request to stop
1105         // current video stream.
1106         mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE, callback);
1107     }
1108 
1109     /** Notifies the service status gets changed */
broadcastStateTransition(int type, int state)1110     private void broadcastStateTransition(int type, int state) {
1111         int idx = mStatusListeners.beginBroadcast();
1112         while (idx-- > 0) {
1113             ICarEvsStatusListener listener = mStatusListeners.getBroadcastItem(idx);
1114             try {
1115                 listener.onStatusChanged(new CarEvsStatus(type, state));
1116             } catch (RemoteException e) {
1117                 // Likely the binder death incident
1118                 Slog.e(TAG_EVS, Log.getStackTraceString(e));
1119             }
1120         }
1121         mStatusListeners.finishBroadcast();
1122     }
1123 
1124     /** Starts a requested service */
startService(@arEvsServiceType int type)1125     private boolean startService(@CarEvsServiceType int type) {
1126         if (type == CarEvsManager.SERVICE_TYPE_SURROUNDVIEW) {
1127             // TODO(b/179029031): Removes below when Surround View service is integrated.
1128             Slog.e(TAG_EVS, "Surround view is not supported yet.");
1129             return false;
1130         }
1131 
1132         if (!nativeConnectToHalServiceIfNecessary(mNativeEvsServiceObj)) {
1133             Slog.e(TAG_EVS, "Failed to connect to EVS service");
1134             return false;
1135         }
1136 
1137         String cameraId;
1138         if (mUseCameraIdOverride) {
1139             cameraId = mCameraIdOverride;
1140         } else {
1141             cameraId = mContext.getString(R.string.config_evsRearviewCameraId);
1142         }
1143 
1144         if (!nativeOpenCamera(mNativeEvsServiceObj, cameraId)) {
1145             Slog.e(TAG_EVS, "Failed to open a target camera device");
1146             return false;
1147         }
1148 
1149         return true;
1150     }
1151 
1152     /** Stops a current service */
stopService()1153     private void stopService() {
1154         try {
1155             nativeRequestToStopVideoStream(mNativeEvsServiceObj);
1156         } catch (RuntimeException e) {
1157             Slog.w(TAG_EVS, Log.getStackTraceString(e));
1158         } finally {
1159             // Unregister all stream callbacks.
1160             synchronized (mLock) {
1161                 mStreamCallback = null;
1162             }
1163 
1164             // We simply drop all buffer records; the native method will return all pending buffers
1165             // to the native Extended System View service if it is alive.
1166             synchronized (mBufferRecords) {
1167                 mBufferRecords.clear();
1168             }
1169 
1170             // Cancel a pending message to check a request timeout
1171             mHandler.removeMessages(MSG_CHECK_ACTIVITY_REQUEST_TIMEOUT);
1172         }
1173     }
1174 
1175     @GuardedBy("mLock")
handlePropertyEventLocked(CarPropertyEvent event)1176     private void handlePropertyEventLocked(CarPropertyEvent event) {
1177         if (event.getEventType() != CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE) {
1178             // CarEvsService is interested only in the property change event.
1179             return;
1180         }
1181 
1182         CarPropertyValue value = event.getCarPropertyValue();
1183         if (value.getPropertyId() != VehicleProperty.GEAR_SELECTION) {
1184             // CarEvsService is interested only in the GEAR_SELECTION property.
1185             return;
1186         }
1187 
1188         long timestamp = value.getTimestamp();
1189         if (timestamp <= mLastEvsHalEvent.getTimestamp()) {
1190             if (DBG) {
1191                 Slog.d(TAG_EVS,
1192                         "Ignoring GEAR_SELECTION change happened past, timestamp = " + timestamp +
1193                         ", last event was at " + mLastEvsHalEvent.getTimestamp());
1194             }
1195             return;
1196         }
1197 
1198         // TODO(b/179029031): CarEvsService may need to process VehicleGear.GEAR_PARK when
1199         // Surround View service is integrated.
1200         int gear = (Integer) value.getValue();
1201         if (gear == VehicleGear.GEAR_REVERSE) {
1202             // Request to start the rearview activity when the gear is shifted into the reverse
1203             // position.
1204             if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_REQUESTED,
1205                     CarEvsManager.SERVICE_TYPE_REARVIEW) != ERROR_NONE) {
1206                 Slog.w(TAG_EVS, "Failed to request the rearview activity.");
1207             }
1208         } else {
1209             // Request to stop the rearview activity when the gear is shifted from the reverse
1210             // position to other positions.
1211             if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE,
1212                     CarEvsManager.SERVICE_TYPE_REARVIEW) != ERROR_NONE) {
1213                 Slog.d(TAG_EVS, "Failed to stop the rearview activity.");
1214             }
1215         }
1216 
1217         mLastEvsHalEvent = new EvsHalEvent(timestamp, CarEvsManager.SERVICE_TYPE_REARVIEW,
1218                 gear == VehicleGear.GEAR_REVERSE);
1219     }
1220 
1221     /** Processes a streaming event and propagates it to registered clients */
processStreamEvent(@arEvsStreamEvent int event)1222     private void processStreamEvent(@CarEvsStreamEvent int event) {
1223         synchronized (mLock) {
1224             if (mStreamCallback == null) {
1225                 return;
1226             }
1227 
1228             try {
1229                 mStreamCallback.onStreamEvent(event);
1230             } catch (RemoteException e) {
1231                 // Likely the binder death incident
1232                 Slog.e(TAG_EVS, Log.getStackTraceString(e));
1233             }
1234         }
1235     }
1236 
1237     /** Processes a streaming event and propagates it to registered clients */
processNewFrame(int id, @NonNull HardwareBuffer buffer)1238     private void processNewFrame(int id, @NonNull HardwareBuffer buffer) {
1239         Objects.requireNonNull(buffer);
1240 
1241         synchronized (mLock) {
1242             mBufferRecords.add(id);
1243             if (mStreamCallback == null) {
1244                 return;
1245             }
1246 
1247             try {
1248                 mStreamCallback.onNewFrame(new CarEvsBufferDescriptor(id, buffer));
1249             } catch (RemoteException e) {
1250                 // Likely the binder death incident
1251                 Slog.e(TAG_EVS, Log.getStackTraceString(e));
1252             }
1253         }
1254     }
1255 
1256     /** EVS stream event handler called after a native handler */
postNativeEventHandler(int eventType)1257     private void postNativeEventHandler(int eventType) {
1258         processStreamEvent(
1259                 CarEvsServiceUtils.convertToStreamEvent(eventType));
1260     }
1261 
1262     /** EVS frame handler called after a native handler */
postNativeFrameHandler(int id, HardwareBuffer buffer)1263     private void postNativeFrameHandler(int id, HardwareBuffer buffer) {
1264         processNewFrame(id, buffer);
1265     }
1266 
1267     /** EVS service death handler called after a native handler */
postNativeDeathHandler()1268     private void postNativeDeathHandler() {
1269         // We have lost the Extended View System service.
1270         mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_UNAVAILABLE);
1271         connectToHalServiceIfNecessary(EVS_HAL_SERVICE_BIND_RETRY_INTERVAL_MS);
1272     }
1273 
1274     /** Try to connect to the EVS HAL service until it succeeds at a given interval */
connectToHalServiceIfNecessary(long intervalInMillis)1275     private void connectToHalServiceIfNecessary(long intervalInMillis) {
1276         Slog.d(TAG_EVS, "Trying to connect to the EVS HAL service.");
1277         if (mStateEngine.execute(REQUEST_PRIORITY_HIGH, SERVICE_STATE_INACTIVE) != ERROR_NONE) {
1278             // Try to restore a connection again after a given amount of time
1279             mHandler.sendMessageDelayed(obtainMessage(
1280                     CarEvsService::connectToHalServiceIfNecessary, this, intervalInMillis),
1281                     intervalInMillis);
1282         }
1283     }
1284 
1285     /** Notify the client of a video stream loss */
notifyStreamStopped(@onNull ICarEvsStreamCallback callback)1286     private static void notifyStreamStopped(@NonNull ICarEvsStreamCallback callback) {
1287         Objects.requireNonNull(callback);
1288 
1289         try {
1290             callback.onStreamEvent(CarEvsManager.STREAM_EVENT_STREAM_STOPPED);
1291         } catch (RemoteException e) {
1292             // Likely the binder death incident
1293             Slog.w(TAG_EVS, Log.getStackTraceString(e));
1294         }
1295     }
1296 
1297     /**
1298      * Because of its dependency on FMQ type, android.hardware.automotive.evs@1.1 interface does
1299      * not support Java backend.  Therefore, all hwbinder transactions happen in native methods
1300      * declared below.
1301      */
1302 
1303     static {
1304         System.loadLibrary("carservicejni");
1305     }
1306 
1307     /** Stores a service handle initialized in native methods */
1308     private long mNativeEvsServiceObj = 0;
1309 
1310     /** Attempts to connect to the HAL service if it has not done yet */
nativeConnectToHalServiceIfNecessary(long handle)1311     private native boolean nativeConnectToHalServiceIfNecessary(long handle);
1312 
1313     /** Attempts to disconnect from the HAL service */
nativeDisconnectFromHalService(long handle)1314     private native void nativeDisconnectFromHalService(long handle);
1315 
1316     /** Attempts to open a target camera device */
nativeOpenCamera(long handle, String cameraId)1317     private native boolean nativeOpenCamera(long handle, String cameraId);
1318 
1319     /** Requests to close a target camera device */
nativeCloseCamera(long handle)1320     private native void nativeCloseCamera(long handle);
1321 
1322     /** Requests to start a video stream */
nativeRequestToStartVideoStream(long handle)1323     private native boolean nativeRequestToStartVideoStream(long handle);
1324 
1325     /** Requests to stop a video stream */
nativeRequestToStopVideoStream(long handle)1326     private native void nativeRequestToStopVideoStream(long handle);
1327 
1328     /** Request to return an used buffer */
nativeDoneWithFrame(long handle, int bufferId)1329     private native void nativeDoneWithFrame(long handle, int bufferId);
1330 
1331     /** Creates a EVS service handle */
nativeCreateServiceHandle()1332     private static native long nativeCreateServiceHandle();
1333 
1334     /** Destroys a EVS service handle */
nativeDestroyServiceHandle(long handle)1335     private static native void nativeDestroyServiceHandle(long handle);
1336 }
1337