Nice portable devices record play counts and timestamps, like iPod or any player with Rockbox.
With that info you can scrobble your tracks somewhere by following audioscrobbler spec.
Walkman NW-A50 is not nice.
- getting current track info: artist, title, duration
- getting player status: playing/stopped
Not possible. Services communicate over binder interface; one session per client. It is possible to connect to running service, but you'll get a new empty session instead of already running.
You can recompile binder kernel module to print info or just read binder log to get a peek at what's happening; you'll get function id and a pointer to binder parcel, which has address of whatever data is passed.
Example (https://stackoverflow.com/a/39328585/1938027):
$ cd /sys/kernel/debug/tracing
$ echo > set_event # clear all unrelated events
$ echo 1 > events/binder/enable
$ echo 1 > tracing_on
# .. do your test jobs ..
$ cat trace
# refer to https://android.googlesource.com/kernel/common/+/android-3.10.y/Documentation/trace/ftrace.txt for more detail info.
There is no real data, just pointers.
You can inject your C++ code into HgrmMediaPlayerApp, libPlayerService or libPlayerServiceClient.
By hooking some promising functions like pst::services::playerservice::PlayController::GetCurrentStatus
you can get pointer to status
object.
Status object is complex and consists of nested structures; decompiling those takes too much time.
Possible, but not viable.
Carefully patch binary to jmp
somewhere else when needed and ret
back.
Most likely CurrentStatus
functions or OnNextTrack
?
Firmware-dependent, possibility of breaking current code, have to reverse engineer a lot of structures.
Not viable.
Inject C code before SELECT
function, or before submitting the query. Read the query, run it again, get track info.
Haven't tried this, but there are problems:
- query results might be cached; no repeated select if track is looped (workaround is possible if player state is available)
select
runs during library browsing, might be getting many selects even if track is unchangedselect
runs before the end of current track to prepare next track; there is no guarantee this track would be played
Promising, but complicated and unreliable.
Database software is called genesys-db, some kind of SQLite fork. You can adb pull
it from device, /db/MTPDB.dat.
Database provides track data and, possibly, track change data.
The idea is to make database write something into itself on select * from object_body ...
query.
However, triggers can be created only for DELETE, INSERT, UPDATE - https://www.sqlite.org/lang_createtrigger.html
Not possible.
Process /system/vendor/sony/bin/hagodaemon MediaStoreService PlayerService
opens currently played music file:
$ pid=$(ps ax | grep "[h]agodaemon MediaStore" | cut -d' ' -f3 | sort | tail -1)
$ ls -l /proc/$pid/fd/* | grep contents
lr-x------ system system 2024-04-15 03:06 9 -> /contents_ext/music/my/music/file.mp3
However:
- might be next track preparation; no guarantees that it would be played
- file is always opened no matter the player status
Possible, but unreliable. You'll have to use TagLib, id3v2lib or something else mature enough to be able to work with all formats and tags.
Duration is rarely present in tags, so you'll have to calculate it yourself.
Currently, there is no mature Go library for ID3v2.
https://developer.android.com/tools/logcat
Services and player write logs with current player status and file path.
You can read the logs by executing logcat utility or by reading log buffer yourself.
Logs are unreliable as a source of truth, but it's the most straightforward option out of all mentioned above.
Simplified log:
content URI: /data/file.mp3
status: [stopped]
status: [paused]
status: [playing]
Issues:
- incorrect order: there is no guarantee if
write play status
thread works afterwrite content uri
.Playing
status might be related to previous track. - information duplication: sometimes content uri might be duplicated (how?)
- too many logs (see
reading from logcat
below)
Example https://github.com/google/gapid/blob/11e56ff/core/os/android/adb/logcat.go
Poor performance due to constant process spawning.
Services may write too many logs, so your buffer has to be huge (hundreds of lines). Even then there is a risk of valuable lines (with status and content) not getting into buffer.
Having to parse same records again unless you flush everything after reading with logcat -c
, which I consider intrusive.
Viable, but clunky.
See source code.
Note to self: SoundServiceFw must have logs enabled and set to 8.