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