Skip to content

Commit

Permalink
some fun edits
Browse files Browse the repository at this point in the history
  • Loading branch information
nift4 committed Feb 25, 2025
1 parent 21b5aa2 commit 33b04bc
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 69 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ dependencies {
implementation("com.google.android.material:material:1.12.0")
implementation("me.zhanghai.android.fastscroll:library:1.3.0")
implementation("io.coil-kt.coil3:coil:3.0.4")
implementation("io.github.rk700:dlfunc:0.1.1") // TODO 0.1.1 does not seem to work on O/P?
implementation("io.github.rk700:dlfunc:0.1.0") // TODO 0.1.1 does not seem to work on O/P? but required for newer versions!
//noinspection GradleDependency newer versions need java.nio which is api 26+
//implementation("com.github.albfernandez:juniversalchardet:2.0.3") TODO
implementation("androidx.profileinstaller:profileinstaller:1.4.1")
Expand Down
17 changes: 12 additions & 5 deletions app/src/main/cpp/android_linker_ns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ extern "C" {
#include <dlfunc.h>
}
#include <android/log.h>
#include <pthread.h>
#include "android_linker_ns.h"
#include <sys/mman.h>

using loader_android_create_namespace_t = android_namespace_t *(*)(const char *, const char *, const char *, uint64_t, const char *, android_namespace_t *, const void *);
static loader_android_create_namespace_t loader_android_create_namespace;
Expand All @@ -30,7 +32,8 @@ struct android_namespace_t *android_create_namespace_escape(const char *name,
android_namespace_t *parent_namespace) {
auto caller{reinterpret_cast<void *>(&dlopen)};
return loader_android_create_namespace(name, ld_library_path, default_library_path, type,
permitted_when_isolated_path, parent_namespace, caller);
permitted_when_isolated_path, parent_namespace,
caller);
}

void *linkernsbypass_namespace_dlopen(const char *filename, int flags, android_namespace_t *ns) {
Expand All @@ -42,9 +45,8 @@ void *linkernsbypass_namespace_dlopen(const char *filename, int flags, android_n
return android_dlopen_ext(filename, flags, &extInfo);
}

/* Private */
void linkernsbypass_load(JNIEnv* env) {
if (lib_loaded || android_get_device_api_level() < 26)
if (lib_loaded)
return;

if (!dlfunc_loaded && dlfunc_init(env) != JNI_OK) {
Expand All @@ -53,6 +55,9 @@ void linkernsbypass_load(JNIEnv* env) {
}
dlfunc_loaded = true;

if (android_get_device_api_level() < 26)
return;

void* libdlAndroidHandle = dlfunc_dlopen(env, "libdl_android.so", RTLD_NOW);
if (!libdlAndroidHandle) {
libdlAndroidHandle = dlfunc_dlopen(env, "libdl.so", RTLD_NOW);
Expand All @@ -63,10 +68,12 @@ void linkernsbypass_load(JNIEnv* env) {
}
}

loader_android_create_namespace = reinterpret_cast<loader_android_create_namespace_t>(dlsym(libdlAndroidHandle, "__loader_android_create_namespace"));
loader_android_create_namespace = reinterpret_cast<loader_android_create_namespace_t>(dlsym(
libdlAndroidHandle, "__loader_android_create_namespace"));
if (!loader_android_create_namespace) {
__android_log_print(ANDROID_LOG_ERROR, "linkernsbypass",
"dlsym of __loader_android_create_namespace in libdl_android.so failed: %s", dlerror());
"dlsym of __loader_android_create_namespace in libdl_android.so failed: %s",
dlerror());
return;
}

Expand Down
69 changes: 31 additions & 38 deletions app/src/main/cpp/gramophone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <string>
extern "C" {
#include "audio-hal-enums.h"
#include <dlfunc.h>
}
#include <aaudio/AAudio.h>

Expand All @@ -27,36 +28,37 @@ static AAudioConvert_androidToAAudioDataFormat_t AAudioConvert_androidToAAudioDa
bool initLib(JNIEnv* env) {
if (init_done)
return true;
// TODO support 24/25 namespace bypass?
if (android_get_device_api_level() < 26) {
if (android_get_device_api_level() < 24) {
if (!handle) {
handle = dlopen("libaudioclient.so", RTLD_GLOBAL);
handle = dlopen("libmedia.so", RTLD_GLOBAL);
if (handle == nullptr) {
__android_log_print(ANDROID_LOG_ERROR, "AudioTrackHalInfo(JNI)",
"dlopen returned nullptr for libaudioclient.so: %s", dlerror());
"dlopen returned nullptr for libmedia.so: %s", dlerror());
return false;
}
}
if (!handle2) {
handle2 = dlopen("libaaudio.so", RTLD_GLOBAL);
if (handle2 == nullptr) {
init_done = true;
return true;
}
linkernsbypass_load(env);
if (android_get_device_api_level() < 26) {
if (!handle) {
handle = dlfunc_dlopen(env, "libmedia.so", RTLD_GLOBAL);
if (handle == nullptr) {
__android_log_print(ANDROID_LOG_ERROR, "AudioTrackHalInfo(JNI)",
"dlopen returned nullptr for libaaudio.so: %s", dlerror());
"dlopen returned nullptr for libmedia.so: %s", dlerror());
return false;
}
}
init_done = true;
return true;
}
linkernsbypass_load(env);
if (!linkernsbypass_load_status()) {
__android_log_print(ANDROID_LOG_ERROR, "AudioTrackHalInfo(JNI)", "linker namespace bypass init failed");
return false;
}
android_namespace_t* ns = nullptr;
android_namespace_t* ns = android_create_namespace_escape("default_copy", nullptr, nullptr,
ANDROID_NAMESPACE_TYPE_SHARED, nullptr, nullptr);
if (!handle) {
ns = android_create_namespace_escape("default_copy", nullptr, nullptr,
ANDROID_NAMESPACE_TYPE_SHARED, nullptr, nullptr);
handle = linkernsbypass_namespace_dlopen("libaudioclient.so", RTLD_GLOBAL, ns);
if (handle == nullptr) {
__android_log_print(ANDROID_LOG_ERROR, "AudioTrackHalInfo(JNI)",
Expand All @@ -65,9 +67,6 @@ bool initLib(JNIEnv* env) {
}
}
if (!handle2) {
if (!ns)
ns = android_create_namespace_escape("default_copy", nullptr,
nullptr, ANDROID_NAMESPACE_TYPE_SHARED, nullptr, nullptr);
handle2 = linkernsbypass_namespace_dlopen("libaaudio.so", RTLD_GLOBAL, ns);
if (handle2 == nullptr) {
__android_log_print(ANDROID_LOG_ERROR, "AudioTrackHalInfo(JNI)",
Expand Down Expand Up @@ -118,21 +117,12 @@ Java_org_akanework_gramophone_logic_utils_AudioTrackHalInfoDetector_getHalChanne
}

extern "C" JNIEXPORT jint JNICALL
Java_org_akanework_gramophone_logic_utils_AudioTrackHalInfoDetector_getHalFormatInternal(
JNIEnv* env, jobject, jlong audioTrack) {
Java_org_akanework_gramophone_logic_utils_AudioTrackHalInfoDetector_audioFormatToAAudioFormat(
JNIEnv* env, jobject, jint audioFormat) {
if (!initLib(env))
return -1;
if (!ZNK7android10AudioTrack12getHalFormatEv) {
ZNK7android10AudioTrack12getHalFormatEv =
(ZNK7android10AudioTrack12getHalFormatEv_t)
dlsym(handle, "_ZNK7android10AudioTrack12getHalFormatEv");
if (ZNK7android10AudioTrack12getHalFormatEv == nullptr) {
__android_log_print(ANDROID_LOG_ERROR, "AudioTrackHalInfo(JNI)",
"dlsym returned nullptr for _ZNK7android10AudioTrack12getHalFormatEv: %s",
dlerror());
return -1;
}
}
if (android_get_device_api_level() < 26)
return -1;
if (!AAudioConvert_androidToAAudioDataFormat) {
// _Z46AAudioConvert_androidToNearestAAudioDataFormat14audio_format_t is ABI compatible with
// _Z39AAudioConvert_androidToAAudioDataFormat14audio_format_t.
Expand All @@ -151,15 +141,14 @@ Java_org_akanework_gramophone_logic_utils_AudioTrackHalInfoDetector_getHalFormat
}
}
}
return AAudioConvert_androidToAAudioDataFormat(
(audio_format_t)ZNK7android10AudioTrack12getHalFormatEv((void*) audioTrack));
return AAudioConvert_androidToAAudioDataFormat((audio_format_t)audioFormat);
}

extern "C" JNIEXPORT jstring JNICALL
Java_org_akanework_gramophone_logic_utils_AudioTrackHalInfoDetector_getHalFormatInternal2(
extern "C" JNIEXPORT jint JNICALL
Java_org_akanework_gramophone_logic_utils_AudioTrackHalInfoDetector_getHalFormatInternal(
JNIEnv* env, jobject, jlong audioTrack) {
if (!initLib(env))
return nullptr;
return 0;
if (!ZNK7android10AudioTrack12getHalFormatEv) {
ZNK7android10AudioTrack12getHalFormatEv =
(ZNK7android10AudioTrack12getHalFormatEv_t)
Expand All @@ -168,12 +157,16 @@ Java_org_akanework_gramophone_logic_utils_AudioTrackHalInfoDetector_getHalFormat
__android_log_print(ANDROID_LOG_ERROR, "AudioTrackHalInfo(JNI)",
"dlsym returned nullptr for _ZNK7android10AudioTrack12getHalFormatEv: %s",
dlerror());
return nullptr;
return 0;
}
}
const char* ret = audio_format_to_string((audio_format_t)
ZNK7android10AudioTrack12getHalFormatEv((void*) audioTrack));
return env->NewStringUTF(ret);
return (int32_t) ZNK7android10AudioTrack12getHalFormatEv((void*) audioTrack);
}

extern "C" JNIEXPORT jstring JNICALL
Java_org_akanework_gramophone_logic_utils_AudioTrackHalInfoDetector_audioFormatToString(
JNIEnv* env, jobject, jint format) {
return env->NewStringUTF(audio_format_to_string((audio_format_t) format));
}

extern "C" JNIEXPORT jint JNICALL
Expand Down
131 changes: 106 additions & 25 deletions app/src/main/kotlin/org/akanework/gramophone/logic/utils/MediaRoutes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import android.media.AudioTrack
import android.media.MediaRoute2Info
import android.media.MediaRouter2
import android.os.Build
import android.os.IBinder
import android.os.Parcel
import android.text.TextUtils
import android.util.Log
import androidx.annotation.RequiresApi
Expand Down Expand Up @@ -287,7 +289,6 @@ object MediaRoutes {
}
}

// TODO support versions before U
object AudioTrackHalInfoDetector {
private const val TAG = "AudioTrackHalInfoDetect"

Expand All @@ -307,16 +308,44 @@ object AudioTrackHalInfoDetector {
return field.get(audioTrack) as Long
}

fun getHalSampleRate(audioTrack: AudioTrack): Int? =
fun getHalSampleRate(audioTrack: AudioTrack): Int? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
val ret = try {
getHalSampleRateInternal(getAudioTrackPtr(audioTrack))
} catch (e: Throwable) {
Log.e(TAG, Log.getStackTraceString(e))
null
}
if (ret != null && ret != 0)
return ret
}
val output = getOutput(audioTrack)
if (output == null)
return null
val af = getAfService()
if (af == null)
return null
val inParcel = obtainParcel(af)
val outParcel = obtainParcel(af)
try {
getHalSampleRateInternal(getAudioTrackPtr(audioTrack))
} catch (e: Throwable) {
Log.e(TAG, Log.getStackTraceString(e))
null
inParcel.writeInterfaceToken(af.interfaceDescriptor!!)
inParcel.writeInt(output)
af.transact(3, inParcel, outParcel, 0)
if (!readStatus(outParcel))
return null
return outParcel.readInt()
} finally {
inParcel.recycle()
outParcel.recycle()
}
}
private external fun getHalSampleRateInternal(audioTrackPtr: Long): Int

fun obtainParcel(binder: IBinder) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
Parcel.obtain(binder) else Parcel.obtain()

fun getHalChannelCount(audioTrack: AudioTrack): Int? =
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) null else
try {
getHalChannelCountInternal(getAudioTrackPtr(audioTrack))
} catch (e: Throwable) {
Expand All @@ -331,19 +360,16 @@ object AudioTrackHalInfoDetector {
* The conversion to aaudio_format_t is done using system libraries, so this method has the
* advantage of being valid for the indefinite future and not relying on magic numbers.
*/
fun getHalFormat(audioTrack: AudioTrack): AAudioFormat? =
try {
val fmt = getHalFormatInternal(getAudioTrackPtr(audioTrack))
AAudioFormat.entries.find { it.format == fmt }
?: run {
Log.e(TAG, "got aaudio_format_t $fmt which is missing on kotlin side")
AAudioFormat.AAUDIO_FORMAT_INVALID
}
} catch (e: Throwable) {
Log.e(TAG, Log.getStackTraceString(e))
null
}
private external fun getHalFormatInternal(audioTrackPtr: Long): Int
fun getHalFormat(audioTrack: AudioTrack): AAudioFormat? {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
return AAudioFormat.AAUDIO_FORMAT_INVALID
val fmt = getHalFormatInner(audioTrack)?.let { audioFormatToAAudioFormat(it) }
return AAudioFormat.entries.find { it.format == fmt }
?: run {
Log.e(TAG, "got aaudio_format_t $fmt which is missing on kotlin side")
AAudioFormat.AAUDIO_FORMAT_INVALID
}
}

/* Public NDK API from AAudio.h */
enum class AAudioFormat(val format: Int) {
Expand Down Expand Up @@ -450,20 +476,75 @@ object AudioTrackHalInfoDetector {
* disadvantage of being invalid in future AOSP versions because its reliance on magic numbers.
*/
fun getHalFormat2(audioTrack: AudioTrack): String? =
getHalFormatInner(audioTrack)?.let { audioFormatToString(it) }

private fun getHalFormatInner(audioTrack: AudioTrack): Int? {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
val ret = try {
getHalFormatInternal(getAudioTrackPtr(audioTrack))
} catch (e: Throwable) {
Log.e(TAG, Log.getStackTraceString(e))
null
}
if (ret != null && ret != 0)
return ret
}
val output = getOutput(audioTrack)
if (output == null)
return null
val af = getAfService()
if (af == null)
return null
val inParcel = obtainParcel(af)
val outParcel = obtainParcel(af)
try {
getHalFormatInternal2(getAudioTrackPtr(audioTrack))
} catch (e: Throwable) {
Log.e(TAG, Log.getStackTraceString(e))
null
inParcel.writeInterfaceToken(af.interfaceDescriptor!!)
inParcel.writeInt(output)
af.transact(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) 4 else 5,
inParcel, outParcel, 0
)
if (!readStatus(outParcel))
return null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
TODO()
} else
return outParcel.readInt()
} finally {
inParcel.recycle()
outParcel.recycle()
}
private external fun getHalFormatInternal2(audioTrackPtr: Long): String?
}
private external fun getHalFormatInternal(audioTrackPtr: Long): Int
private external fun audioFormatToString(format: Int): String?
private external fun audioFormatToAAudioFormat(format: Int): Int

fun getOutput(audioTrack: AudioTrack): Int? =
private fun getOutput(audioTrack: AudioTrack): Int? =
try {
getOutputInternal(getAudioTrackPtr(audioTrack))
} catch (e: Throwable) {
Log.e(TAG, Log.getStackTraceString(e))
null
}
private external fun getOutputInternal(audioTrackPtr: Long): Int

@SuppressLint("PrivateApi")
private fun getAfService(): IBinder? {
return try {
Class.forName("android.os.ServiceManager").getMethod(
"getService", String::class.java
).invoke(null, "media.audio_flinger") as IBinder?
} catch (e: Throwable) {
Log.e(TAG, Log.getStackTraceString(e))
null
}
}

private fun readStatus(parcel: Parcel): Boolean {
if (Build.VERSION.SDK_INT < 31) return true
val status = parcel.readInt()
if (status == 0) return true
Log.e(TAG, "binder transaction failed with status $status")
return false
}
}

0 comments on commit 33b04bc

Please sign in to comment.