Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
86551be
android: add accessibility stuff
kavishdevar Sep 7, 2025
4ee9b27
docs: update transparency mode format
kavishdevar Sep 7, 2025
d1bf540
android: don't start service every time MainActivity is launched
kavishdevar Sep 9, 2025
df9f443
android: add basic multidevice capabilities
kavishdevar Sep 10, 2025
0e9aadd
android: clean up a bit of AI gen'd code
kavishdevar Sep 10, 2025
aecbb06
android: clean up main service and remove minimum API on head gestures
kavishdevar Sep 10, 2025
fa00620
android: clean up a lot of stuff
kavishdevar Sep 10, 2025
c53356f
android: implement the accessiblity settings page
kavishdevar Sep 11, 2025
9e6d971
android: add EQ settings for phone and media
kavishdevar Sep 15, 2025
5bef8c3
android: add toggle for DID hook
kavishdevar Sep 15, 2025
792629a
docs: add 'has ownership' control cmd
kavishdevar Sep 15, 2025
93328d2
android: fix balance NaN error when amplification L/R is both zero
kavishdevar Sep 18, 2025
65d074e
android: bring back some accessiblity settings and add listeners for …
kavishdevar Sep 19, 2025
5c9beeb
android: add header to ATTManager
kavishdevar Sep 19, 2025
032b94e
android: use device name sent by the connected device in island
kavishdevar Sep 19, 2025
3699ee6
android: fix track color in tone volume
kavishdevar Sep 19, 2025
b5103a2
android: remove unused composable
kavishdevar Sep 19, 2025
5eff5b9
android: update eq sliders style
kavishdevar Sep 19, 2025
63baa15
android: fix text color in selectors
kavishdevar Sep 19, 2025
71a1f83
android: add delay before starting head tracking again
kavishdevar Sep 19, 2025
bb69a74
android: add a few options
kavishdevar Sep 19, 2025
6fd3cc1
android: a small ui fix
kavishdevar Sep 19, 2025
3cca786
docs: a few more control cmds
kavishdevar Sep 19, 2025
5aeb47b
android: add microphone setting
kavishdevar Sep 20, 2025
ecfdc05
android: improve dropdowns
kavishdevar Sep 20, 2025
3ace0e1
android: move attmanager to service to avoid trying to connect multip…
kavishdevar Sep 21, 2025
fe69082
android: add ui for hearing stuff
kavishdevar Sep 21, 2025
ce229be
android: add media assist options in hearing aid
kavishdevar Sep 22, 2025
4751f70
android: add hearing aid adjustments
kavishdevar Sep 22, 2025
4bc76de
android: liquidglass sliders
kavishdevar Sep 22, 2025
8760757
android: improve liquid glass sliders
kavishdevar Sep 22, 2025
26de422
android: little more liquid glass
kavishdevar Sep 22, 2025
173e06c
android: fix hearing aid parsing
kavishdevar Sep 22, 2025
29a35ce
android: remove customdeviceactivity from manifest
kavishdevar Sep 23, 2025
5f08edd
android: remove unused strings
kavishdevar Sep 23, 2025
7e5ee67
android: small ui tweaks
kavishdevar Sep 23, 2025
86a6a28
android: a very big commit
kavishdevar Sep 25, 2025
ab55096
android: move padding to StyledScaffold's content
kavishdevar Sep 25, 2025
56307c9
android: revert accidental capitalization on toggle label
kavishdevar Sep 25, 2025
d9795c4
merge main into multi-device-and-accessibility
kavishdevar Sep 25, 2025
8dc7a97
android: update usages for toggle
kavishdevar Sep 26, 2025
08738a1
android: liquidglass, maybe?
kavishdevar Sep 28, 2025
3f582b8
remove bleonly mode, use CAPod instead
kavishdevar Sep 28, 2025
1152f45
remove bleonly mode, use CAPod instead
kavishdevar Sep 28, 2025
5bc1dd2
android: fix switch styling
kavishdevar Sep 28, 2025
55cb69f
android: remove fade from transition
kavishdevar Sep 28, 2025
1076218
android: add A16's new bluetooth identifier for log collection
kavishdevar Sep 28, 2025
e9da7a2
android: fix crash in head gestures screen
kavishdevar Sep 28, 2025
147e511
android: show head gestures status in the navigation button
kavishdevar Sep 28, 2025
e158ba1
android: don't crash if att not available
kavishdevar Sep 28, 2025
5ec300a
android: use lazycolumn in airpods settings for better performance an…
kavishdevar Sep 28, 2025
48b715a
android: fix text color in troubshooting button and pressandhold sett…
kavishdevar Sep 28, 2025
504e703
android: bring back original confirmation dialog
kavishdevar Sep 28, 2025
bdb93ef
android: prevent hearing aid turning off itself
kavishdevar Sep 28, 2025
3a388da
android: hide media assist, not implemented
kavishdevar Sep 28, 2025
c2ebbef
docs: update README with new features
kavishdevar Sep 28, 2025
9d60dc3
docs: add demo video
kavishdevar Sep 28, 2025
b43e5f7
docs: add new screenshots for android
kavishdevar Sep 28, 2025
78ae31c
docs: update demo video position
kavishdevar Sep 28, 2025
6914dab
docs: app3 compatibility
kavishdevar Sep 29, 2025
395feab
docs: new control cmds '25 (again)
kavishdevar Sep 29, 2025
650b128
docs: change section title in control cmd doc
kavishdevar Sep 29, 2025
993f022
android: ui tweaks
kavishdevar Sep 30, 2025
8b49440
android: update styled slider thumb
kavishdevar Sep 30, 2025
342745e
android: add accessiblity service for camera control
kavishdevar Sep 30, 2025
c7dc545
android: add camera control, finally
kavishdevar Sep 30, 2025
b799cd1
android: add option to change camera app id
kavishdevar Sep 30, 2025
d9469c2
android: not use relative paths for executing commands
kavishdevar Sep 30, 2025
37313fb
android: fix transparency and noise cancellation flags
kavishdevar Sep 30, 2025
3a9c118
android: revert to using relative paths for su
kavishdevar Oct 1, 2025
993ba1b
android: bump version
kavishdevar Oct 1, 2025
0e0af35
android: don't crash if self MAC is not available
kavishdevar Oct 1, 2025
b7cc27f
android: remove unused LOCAL_ADDRESS permission
kavishdevar Oct 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 14 additions & 37 deletions AAP Definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,50 +279,27 @@ duplicated thrice for some reason
## Customize Transparency mode

```
52 18 00
For left bud
[Enabled]
12 18 00 [enabled]
<left bud>
[EQ1][EQ2][EQ3][EQ4][EQ5][EQ6][EQ7][EQ8]
[Amplification]
[Tone]
[Conversation Boost]
[Ambient Noise Reduction]
00 0080 3F
<same for the right bud>
<repeat for right bud>
```

<!-- demo packet
52 18 00 00 00 00 00 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 00 00 80 3f 00 00 80 3f 00 00 80 3f 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 62 10 DA 41 00 00 80 3f 00 00 80 3f 00 00 80 3f
-->
<!--
5218 0000 0080 3F62 10DA 413D 0AF0 4160
E50C 42AC 9C1E 421B 2F29 429E 6F33 4293
1846 4293 1846 4206 9476 BF00 576E BB00
0080 3F00 0080 3F62 10DA 413D 0AF0 4160
E50C 42AC 9C1E 421B 2F29 429E 6F33 4293
1846 4293 1846 4200 0080 BF00 576E BB00
0080 3F00 0080 3F
-->

<!--
5218 0000 0000 0062 10DA 413D 0AF0 4160
E50C 42AC 9C1E 421B 2F29 429E 6F33 4293
1846 4293 1846 4206 9476 BF00 576E BB00
0080 3F00 0080 3F62 10DA 413D 0AF0 4160
E50C 42AC 9C1E 421B 2F29 429E 6F33 4293
1846 4293 1846 4200 0080 BF00 576E BB00
0080 3F00 0080 3F
-->

All values are formatted as Little Endian from float values.
| Data | Type | Value range |
|---------------------|---------------|-------------|
| Enabled | Little Endian | 0 or 1 |
| EQ | Little Endian | 0 to 100 |
| Amplification | Little Endian | -1 to 1 |
| Tone | Little Endian | -1 to 1 |
| Conversation Boost | Little Endian | 0 or 1 |

All values are formatted as IEEE 754 floats in little endian order.
| Data | Type | Range |
|-------------------------|---------------|-------|
| Enabled | IEEE754 Float | 0/1 |
| EQ | IEEE754 Float | 0-100 |
| Amplification | IEEE754 Float | 0-2 |
| Tone | IEEE754 Float | 0-2 |
| Conversation Boost | IEEE754 Float | 0/1 |
| Ambient Noise Reduction | IEEE754 Float | 0-1 |
| Ambient Noise Reduction | IEEE754 Float | 0-1 |

> [!IMPORTANT]
> Also send the [Headphone Accomodation](#headphone-accomodation) after this.
Expand Down
7 changes: 4 additions & 3 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS"
tools:ignore="ProtectedPermissions" />

<protected-broadcast android:name="batterywidget.impl.action.update_bluetooth_data" />

Expand Down Expand Up @@ -90,13 +91,13 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter android:autoVerify="true">
<!-- <intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="librepods"
android:host="add-magic-keys" />
</intent-filter>
</intent-filter> -->
</activity>

<activity
Expand Down
68 changes: 68 additions & 0 deletions android/app/src/main/cpp/l2c_fcr_hook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
#include <string>
#include <sys/system_properties.h>
#include "l2c_fcr_hook.h"
#include <cerrno>
#include <cstdlib>

#define LOG_TAG "AirPodsHook"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
Expand Down Expand Up @@ -126,6 +128,9 @@ static void (*original_l2cu_process_our_cfg_req)(tL2C_CCB* p_ccb, tL2CAP_CFG_INF
static void (*original_l2c_csm_config)(tL2C_CCB* p_ccb, uint8_t event, void* p_data) = nullptr;
static void (*original_l2cu_send_peer_info_req)(tL2C_LCB* p_lcb, uint16_t info_type) = nullptr;

// Add original pointer for BTA_DmSetLocalDiRecord
static tBTA_STATUS (*original_BTA_DmSetLocalDiRecord)(tSDP_DI_RECORD* p_device_info, uint32_t* p_handle) = nullptr;

uint8_t fake_l2c_fcr_chk_chan_modes(void* p_ccb) {
LOGI("l2c_fcr_chk_chan_modes hooked, returning true.");
return 1;
Expand Down Expand Up @@ -156,6 +161,53 @@ void fake_l2cu_send_peer_info_req(tL2C_LCB* p_lcb, uint16_t info_type) {
return;
}

// New loader for SDP hook offset (persist.librepods.sdp_offset)
uintptr_t loadSdpOffset() {
const char* property_name = "persist.librepods.sdp_offset";
char value[PROP_VALUE_MAX] = {0};

int len = __system_property_get(property_name, value);
if (len > 0) {
LOGI("Read sdp offset from property: %s", value);
uintptr_t offset;
char* endptr = nullptr;

const char* parse_start = value;
if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) {
parse_start = value + 2;
}

errno = 0;
offset = strtoul(parse_start, &endptr, 16);

if (errno == 0 && endptr != parse_start && *endptr == '\0' && offset > 0) {
LOGI("Parsed sdp offset: 0x%x", offset);
return offset;
}

LOGE("Failed to parse sdp offset from property value: %s", value);
}

LOGI("No sdp offset property present - skipping SDP hook");
return 0;
}

// Fake BTA_DmSetLocalDiRecord: set vendor/vendor_id_source then call original
tBTA_STATUS fake_BTA_DmSetLocalDiRecord(tSDP_DI_RECORD* p_device_info, uint32_t* p_handle) {
LOGI("BTA_DmSetLocalDiRecord hooked - forcing vendor fields");
if (p_device_info) {
p_device_info->vendor = 0x004C;
p_device_info->vendor_id_source = 0x0001;
}
LOGI("Set vendor=0x%04x, vendor_id_source=0x%04x", p_device_info->vendor, p_device_info->vendor_id_source);
if (original_BTA_DmSetLocalDiRecord) {
return original_BTA_DmSetLocalDiRecord(p_device_info, p_handle);
}

LOGE("Original BTA_DmSetLocalDiRecord not available");
return BTA_FAILURE;
}

uintptr_t loadHookOffset([[maybe_unused]] const char* package_name) {
const char* property_name = "persist.librepods.hook_offset";
char value[PROP_VALUE_MAX] = {0};
Expand Down Expand Up @@ -320,6 +372,7 @@ bool findAndHookFunction(const char *library_name) {
uintptr_t l2cu_process_our_cfg_req_offset = loadL2cuProcessCfgReqOffset();
uintptr_t l2c_csm_config_offset = loadL2cCsmConfigOffset();
uintptr_t l2cu_send_peer_info_req_offset = loadL2cuSendPeerInfoReqOffset();
uintptr_t sdp_offset = loadSdpOffset();

bool success = false;

Expand Down Expand Up @@ -392,6 +445,21 @@ bool findAndHookFunction(const char *library_name) {
LOGI("Skipping l2cu_send_peer_info_req hook as offset is not available");
}

if (sdp_offset > 0) {
void* target = reinterpret_cast<void*>(base_addr + sdp_offset);
LOGI("Hooking BTA_DmSetLocalDiRecord at offset: 0x%x, base: %p, target: %p",
sdp_offset, (void*)base_addr, target);

int result = hook_func(target, (void*)fake_BTA_DmSetLocalDiRecord, (void**)&original_BTA_DmSetLocalDiRecord);
if (result != 0) {
LOGE("Failed to hook BTA_DmSetLocalDiRecord, error: %d", result);
} else {
LOGI("Successfully hooked BTA_DmSetLocalDiRecord (SDP)");
}
} else {
LOGI("Skipping BTA_DmSetLocalDiRecord hook as sdp offset is not available");
}

return success;
}

Expand Down
22 changes: 22 additions & 0 deletions android/app/src/main/cpp/l2c_fcr_hook.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,25 @@ uintptr_t loadL2cuProcessCfgReqOffset();
uintptr_t loadL2cCsmConfigOffset();
uintptr_t loadL2cuSendPeerInfoReqOffset();
bool findAndHookFunction(const char *library_path);

#define SDP_MAX_ATTR_LEN 400

typedef struct t_sdp_di_record {
uint16_t vendor;
uint16_t vendor_id_source;
uint16_t product;
uint16_t version;
bool primary_record;
char client_executable_url[SDP_MAX_ATTR_LEN];
char service_description[SDP_MAX_ATTR_LEN];
char documentation_url[SDP_MAX_ATTR_LEN];
} tSDP_DI_RECORD;

typedef enum : uint8_t {
BTA_SUCCESS = 0, /* Successful operation. */
BTA_FAILURE = 1, /* Generic failure. */
BTA_PENDING = 2, /* API cannot be completed right now */
BTA_BUSY = 3,
BTA_NO_RESOURCES = 4,
BTA_WRONG_MODE = 5,
} tBTA_STATUS;
Loading