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