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