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