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