Harden playback state checks against IPC race

Bug: 271413253

Automated crash detection found a NPE of the form:

Process: com.android.car
PID: 1634
UID: 1000
Flags: 0x30883e4d
Package: com.android.car v32 (12)
Foreground: No
Process-Runtime: 104844637
Build: Phoenix/belford_gas/belford:12/V1.0.0-2022.07-130/PU5T-14H648-QAA5:userdebug/dev-keys
Loading-Progress: 1.0

java.lang.NullPointerException: Attempt to invoke virtual method 'int android.media.session.PlaybackState.getState()' on a null object reference
	at com.android.car.CarMediaService.updatePrimaryMediaSourceWithCurrentlyPlaying(CarMediaService.java:887)
	at com.android.car.CarMediaService.access$2000(CarMediaService.java:95)
	at com.android.car.CarMediaService$MediaSessionUpdater.registerCallbacks(CarMediaService.java:759)
	at com.android.car.CarMediaService$MediaSessionUpdater.access$1300(CarMediaService.java:726)
	at com.android.car.CarMediaService$SessionChangedListener.onActiveSessionsChanged(CarMediaService.java:685)
	at android.media.session.MediaSessionManager$SessionsChangedWrapper.callOnActiveSessionsChangedListener(MediaSessionManager.java:1287)
	at android.media.session.MediaSessionManager$SessionsChangedWrapper.access$600(MediaSessionManager.java:1255)
	at android.media.session.MediaSessionManager$SessionsChangedWrapper$1.lambda$onActiveSessionsChanged$0$MediaSessionManager$SessionsChangedWrapper$1(MediaSessionManager.java:1272)
	at android.media.session.MediaSessionManager$SessionsChangedWrapper$1$$ExternalSyntheticLambda0.run(Unknown Source:4)
	at android.os.Handler.handleCallback(Handler.java:938)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loopOnce(Looper.java:201)
	at android.os.Looper.loop(Looper.java:288)
	at android.os.HandlerThread.run(HandlerThread.java:67)

Root Cause:

CarMediaService uses an unsafe pattern of NULL checking when the
getPlaybackState() call is a binder IPC call.  The first null check with

  controller.getPlaybackState()

returns a non-null value and then the next line calls the same IPC call
again :

  controller.getPlaybackState().isActive()

but from the 1st IPC call to the 2nd, the state in the MediaSession
process has changed and so the 2nd call returns NULL from
getPlaybackState().

race condition diagram looks like :

CarService/MediaController Process  | ==|         MediaSession Pocess
----------------------------------------------------------------------
1st IPC call:

  controller.getPlaybackState()    --->

                                   <---             return non-NULL

  result is non-NULL, proceed.

                                                    change in media
                                                    process such that
                                                    the value is now
                                                    NULL

2nd IPC call:

  controller.getPlaybackState()    --->

                                   <--              return NULL

  result.isActive() ->   NULL deref causes crash on 2nd IPC execution

Make one single IPC call into a local state variable (like the rest of
the file) and use that local variable for the 2nd operation so that the
value can not change between uses.

This change hardens the playback state changes to protect against
potential NULL dereference path in small but real time window caused by
IPC callss.

Change-Id: Ibfb4dedc3a66fc4a789b764117e93f80bb8b7872
Signed-off-by: Matt Maynard <mmayna26@ford.com>
1 file changed
tree: 02bfe113d495f4dcb04072e08d232be4b747b35f
  1. apex_car_framework/
  2. car-admin-ui-lib/
  3. car-builtin-lib/
  4. car-evs-helper-lib/
  5. car-helper-lib/
  6. car-lib/
  7. car-lib-module/
  8. car-maps-placeholder/
  9. car-test-lib/
  10. car-usb-handler/
  11. car_product/
  12. cpp/
  13. data/
  14. experimental/
  15. FrameworkPackageStubs/
  16. obd2-lib/
  17. packages/
  18. procfs-inspector/
  19. service/
  20. service-builtin/
  21. tests/
  22. tools/
  23. vehicle-hal-support-lib/
  24. .clang-format
  25. .gitignore
  26. Android.mk
  27. CleanSpec.mk
  28. CPPLINT.cfg
  29. OWNERS
  30. OWNERS_networking
  31. PREUPLOAD.cfg
  32. README.md
  33. TEST_MAPPING
README.md

AAOS

Source code for Android Automotive OS.

Structure

car_product/           - AAOS product
car-builtin-lib/       - A helper library for CarService to access hidden
                         framework APIs
car-lib/               - Car API
car-lib-module/        - Car API module
cpp/                   - Native services
experimental/          - Experimental Car API and services
packages/              - Apps and services for cars
service/               - Car service module
service-builint        - Platform builtin component that runs CarService module
tests/                 - Tests and sample apps
tools/                 - Helper scripts

C++

Native (C++) code format is required to be compatible with .clang-format file. The formatter is already integrated to repo tool. To run manually, use:

git clang-format --style=file --extension='h,cpp,cc' HEAD~

Note that clang-format is not desirable for Android java files. Therefore the command line above is limited to specific extensions.

Debugging CarService

Dumpsys and car shell can be useful when debugging CarService integration issues.

dumpsys

adb shell dumpsys car_service # to dump all car service information
adb shell dumpsys car_service --services [service name] # to dump a specific service information
adb shell dumpsys car_service --list # get list of available services

Dumpsys for CarService includes the following (more information is availble in dumpsys, below are just highlights):

  • Enabled features
  • Current power policy and list of registered power policies
  • Power state of componens of power policy
  • Silent mode status
  • Garage mode status
  • I/O stats
  • List of available vehicle properties

car shell

adb shell cmd car_service

CarService supports commands via car shell:
(list is not complete, run adb shell cmd car_service -h for more details)

  • Injection of vhal events
  • Toggling garage mode on/off
  • Toggling of suspend/hibernation/resume
  • Injection of input events
  • User managemnet/switching
  • Power policy control/manipulation

Helpful command for Garage mode

Start Garage mode

adb shell cmd car_service garage-mode on

Finish Garage mode

adb shell cmd car_service garage-mode on

Get Garage mode status

adb shell cmd car_service garage-mode query

Change Garage mode max duration (only eng and debug builds)

adb shell setprop android.car.garagemodeduration <seconds>