たくさんの自由帳

Hello Android 15。16KB ページサイズ編

投稿日 : | 0 日前

文字数(だいたい) : 5924

どうもこんばんわ。
前回貼り付け忘れた画像です。かわいい

Imgur

Imgur

本題

前回の続きっちゃそうですが、単品で読めます。

Android と Rust - たくさんの自由帳

https://takusan.negitoro.dev/posts/android_rust/

前回NDKを使うアプリを作りました。そうです、各 CPU アーキテクチャ向けにビルドするあれです。
NDKを使う(使った)アプリを開発する際、この16KB ページサイズに対応する必要があります。

こうしき

対応する必要があるか確認

リリース APKを作った後に、Analyze APK ...を押すことで、APKの中を見れます。

Imgur

で、こんな感じに、libフォルダがあれば対応が必要です。
が、今のアプリ開発だとlibandroidx.graphics.path.soが入ってしまう?

Imgur

はじめに

この記事で言うネイティブコードは、クロスプラットフォームの人たちが言う 各プラットフォームの開発言語 (Swift / Kotlin) の事では無く、
C++Rustのような Android NDK が必要なコードのことを指します。

ネイティブライブラリに関しても同じ。

AGP をバージョンアップ

AGPバージョン8.5.1以上にすると共有ライブラリのパッケージを更新するのセクションは完了しそう。
ネイティブコードを使ってる場合は、自分で対応するにしろ、対応してもらうにしろ何にしろAGPのアップデートが必要そう?(よくわからない)

私の環境のAndroid Gradle Plugin8.8.2
Android StudioにはAGP Upgrade Assistantがついているので古い場合は上げちゃえば良さそう。

Imgur

対応パターン

多分このよっつ?

  • ライブラリがネイティブコードを使ってる場合
  • AndroidXlibandroidx.graphics.path.solibdatastore_shared_counter.soだけ入ってる場合
    • androidx.から始まる(Android Jetpack)のライブラリが.soファイルに依存している
  • Android StudioCMakeLists.txtとかを書いてC++をコンパイルしている場合
  • 共有ライブラリを他のプロジェクトでビルドして、Androidへ持ち込んでいる場合

使ってるライブラリがネイティブコードを使ってる場合

ライブラリ作者にお願いしてきてください
よろしくお願いします

AndroidX の .so ファイルのみが表示されている場合

libandroidx.graphics.path.solibdatastore_shared_counter.soとかが表示されている場合。androidx.のライブラリを入れたら付いてきた場合。
この場合は、すでに 16KB ページサイズでライブラリを配布しているので問題ない(はず)です。観測してる範囲では。

証拠は

後述するのですが、C++のビルド時に参照するCMakeLists.txtを見ると、16KB ページサイズでビルドするように追記されてました

俺がライブラリ作者だ / ネイティブコードをビルドしている場合

がんばろう

すでに 16KB ページサイズに対応しているか確認

macOS / Linuxの場合はシェルスクリプトで確認できます。適当にテキストファイルを.shで作り、シェルスクリプトを貼り付けてください。
vimだとペーストモードが便利。

説明通りここからシェルスクリプトをコピーしてきます。
https://cs.android.com/android/platform/superproject/main/+/main:system/extras/tools/check_elf_alignment.sh

Linuxしか確認できていませんが、chmodで実行権限をつけた後、./check_elf_alignment.sh {APKのパス}コマンドで動くはず。

chmod +x check_elf_alignment.sh
./check_elf_alignment.sh app-release.apk
Windowsの場合はWSL2か、あとはコマンドを愚直に叩く方法もある→

Compatible avec les tailles de page de 16 ko  |  Compatibility  |  Android Developers

Préparez les applications Android pour les tailles de page de 16 ko afin de répondre aux exigences de compatibilité de Google Play pour Android 15 et versions ultérieures.

https://developer.android.com/guide/practices/page-sizes?hl=fr


ちなみにGitBashじゃ動かなかった、unzip dir lib/*の箇所でエラーになるのと、GitBashでもobjdumpwindowsに無い。

以下が実行結果です、
UNALIGNED16KB ページサイズ未対応で、ALIGNEDが対応済みです。もっぱら自分が用意した.soが未対応ですね、、

/tmp/app-release_out_6q4W9/lib/arm64-v8a/libandroid_jni.so: UNALIGNED (2**12)
/tmp/app-release_out_6q4W9/lib/arm64-v8a/libandroid_jni_without_simd.so: UNALIGNED (2**12)
/tmp/app-release_out_6q4W9/lib/arm64-v8a/libandroidx.graphics.path.so: ALIGNED (2**14)
/tmp/app-release_out_6q4W9/lib/arm64-v8a/libjnidispatch.so: ALIGNED (2**16)
/tmp/app-release_out_6q4W9/lib/arm64-v8a/libandroid_rust_uniffi.so: UNALIGNED (2**12)

対応方法

対応する必要があるCPU アーキテクチャARM 64 ビット(arm64-v8a)x86_64です。
32bitはいいらしい。

この3つ?

  • Android StudioCMakeLists.txtを使ってC++をビルドしている場合
  • 他のC++プロジェクトをAndroid NDKでビルドして.so.hAndroid Studioへ持ち込んでいる場合
  • Android NDKRustとかからクロスコンパイルしている場合

環境

Android NDK r27を使います。
後述しますがr28を使えれば一番早いです。

Imgur

最短ルート

Android NDK r28以上を使って、あとはいつも通りビルドする。
バージョンが上げられる場合は多分これが一番早い。

Android Studio以外でビルドする場合はNDKのパスをr28に置き換えれば良さそう。
Android StudioC++ビルドする場合は、app/build.gradle (.kts)ndkVersion = ""28のものにする必要があります。以下例。

android {
    // 以下省略...

    defaultConfig {
        // 以下省略...

        ndkVersion = "28.0.13004108"
    }

    // 以下省略...
}

Android Studio で有効にする

一番多そう雰囲気(なぜか変換できる)。

C++コードをAndroid Studioで書いている場合。
この場合はappフォルダ内のbuild.gradle (.kts)arguments += listOf("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")を書き足せばいいはず。

多分こう。

android {
    namespace = "io.github.takusan23.cppnativesample"
    compileSdk = 35

    defaultConfig {
        applicationId = "io.github.takusan23.cppnativesample"
        minSdk = 21
        targetSdk = 35
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

        // これを足す
        // This block is different from the one you use to link Gradle
        // to your CMake or ndk-build script.
        externalNativeBuild {
            // For ndk-build, instead use the ndkBuild block.
            cmake {
                // Passes optional arguments to CMake.
                arguments += listOf("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
            }
        }
    }

    // 以下省略...

これでAPKを作成しもう一度チェックしてみると、ちゃんとarm64-v8ax86_64ALIGNEDになってました。

takusan23@DESKTOP-ULEKIDB:~$ ./check_elf_alignment.sh app-release-cpp.apk

Recursively analyzing app-release-cpp.apk

NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher.
  You can install the latest build-tools by running the below command
  and updating your $PATH:

    sdkmanager "build-tools;35.0.0-rc3"

=== ELF alignment ===
/tmp/app-release-cpp_out_BGTqn/lib/x86_64/libcppnativesample.so: ALIGNED (2**14)
/tmp/app-release-cpp_out_BGTqn/lib/arm64-v8a/libcppnativesample.so: ALIGNED (2**14)
/tmp/app-release-cpp_out_BGTqn/lib/x86/libcppnativesample.so: UNALIGNED (2**12)
/tmp/app-release-cpp_out_BGTqn/lib/armeabi-v7a/libcppnativesample.so: UNALIGNED (2**12)
Found 2 unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).

Android Studio を使わない他の C++ プロジェクトで CMakeLists.txt に指定する

次はこのパターン
Android Studio以外でCMakeLists.txtを使って共有ライブラリをビルドしている場合。

CMakeLists.txtを使ってリンカーに引数をつければいいらしい。
というわけでAndroid向けにビルドできる適当なプロジェクトで試します。今回はUltraHDR画像を作るlibultrahdrをビルドしてみる。Android向けが公式にサポートされてるので。
Androidの機能だからそれはそう)

building.mdに従えば出来るはず。
cmakeninjaaptからinstallして、Android NDKをダウンロードして、指示通りコマンドを叩くだけ感。

しかし、これでビルドして.soAPKに入れても16K ページサイズに対応してないのでUNALIGNEDになります。

/tmp/app-release-other-project_out_7tCyK/lib/arm64-v8a/libuhdr.so: UNALIGNED (2**12)

というわけでAndroidビルドで使ってる?CMakeLists.txtを探します。
libultrahdrの場合はandroid.cmakeがそれだったので、一番下にこれを書き足しました。target_link_optionsはなんかエラーになってしまった。

# Android 15 16K page-size support.
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-z,max-page-size=16384")

あとはこれでビルドすればいいはず。出来た.somain/jniLibsCPU アーキテクチャのところに格納して終わりのはず。
以下は面倒くさがってarm64-v8aしかビルドしてませんが、これでALIGNEDになりました。libuhdr.soです。

takusan23@DESKTOP-ULEKIDB:~$ ./check_elf_alignment.sh app-release-other-project-16k.apk

Recursively analyzing app-release-other-project-16k.apk

NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher.
  You can install the latest build-tools by running the below command
  and updating your $PATH:

    sdkmanager "build-tools;35.0.0-rc3"

=== ELF alignment ===
/tmp/app-release-other-project-16k_out_EFo2u/lib/x86_64/libcppnativesample.so: ALIGNED (2**14)
/tmp/app-release-other-project-16k_out_EFo2u/lib/arm64-v8a/libcppnativesample.so: ALIGNED (2**14)
/tmp/app-release-other-project-16k_out_EFo2u/lib/arm64-v8a/libuhdr.so: ALIGNED (2**14)
/tmp/app-release-other-project-16k_out_EFo2u/lib/x86/libcppnativesample.so: UNALIGNED (2**12)
/tmp/app-release-other-project-16k_out_EFo2u/lib/armeabi-v7a/libcppnativesample.so: UNALIGNED (2**12)
Found 2 unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).
=====================

Imgur

Rust などのクロスコンパイルで Android 向けビルドを 16KB ページサイズに対応させる

Rustcargoその他のビルドシステムに当たるので、-Wl,-z,max-page-size=16384をどうにかして渡す必要があります。

cargo buildの際に、RUSTFLAGS-C link-arg=に突っ込めば良さそう感。なのでこれでいいはず。

RUSTFLAGS='-C link-arg=-Wl,-z,max-page-size=16384' cargo build --release --target aarch64-linux-android
RUSTFLAGS='-C link-arg=-Wl,-z,max-page-size=16384' cargo build --release --target x86_64-linux-android

Imgur

できた.soを同様にjniLibsに配置すればいいはず。
APK作ってチェックしてみたけど大丈夫そう。libandroid_rust_jni.sox86_64arm64-v8aALIGNEDになってます!!

takusan23@DESKTOP-ULEKIDB:~$ ./check_elf_alignment.sh app-release-rust-jni.apk

Recursively analyzing app-release-rust-jni.apk

NOTICE: Zip alignment check requires build-tools version 35.0.0-rc3 or higher.
  You can install the latest build-tools by running the below command
  and updating your $PATH:

    sdkmanager "build-tools;35.0.0-rc3"

=== ELF alignment ===
/tmp/app-release-rust-jni_out_bm5NF/lib/x86_64/libandroid_rust_jni.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/x86_64/libandroidx.graphics.path.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/arm64-v8a/libandroid_rust_jni.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/arm64-v8a/libandroidx.graphics.path.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/x86/libandroid_rust_jni.so: UNALIGNED (2**12)
/tmp/app-release-rust-jni_out_bm5NF/lib/x86/libandroidx.graphics.path.so: ALIGNED (2**14)
/tmp/app-release-rust-jni_out_bm5NF/lib/armeabi-v7a/libandroid_rust_jni.so: UNALIGNED (2**12)
/tmp/app-release-rust-jni_out_bm5NF/lib/armeabi-v7a/libandroidx.graphics.path.so: ALIGNED (2**14)
Found 2 unaligned libs (only arm64-v8a/x86_64 libs need to be aligned).

Imgur

16KB ページサイズの動作確認

エミュレーターで試せるらしい。
起動できた。

Imgur

逆に16KB ページサイズに対応してないと以下のような例外で落ちる。

FATAL EXCEPTION: main
Process: io.github.takusan23.androidrustjni, PID: 16320
java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH/DT_GNU_HASH in "/data/app/~~S5iS_B60dhn6WlqTEzvegg==/io.github.takusan23.androidrustjni-HG0y1lLFYvVg6MvQNC3k2g==/lib/x86_64/libandroid_rust_jni.so" (new hash type from the future?)
    at java.lang.Runtime.loadLibrary0(Runtime.java:1081)
    at java.lang.Runtime.loadLibrary0(Runtime.java:1003)
    at java.lang.System.loadLibrary(System.java:1765)
    at io.github.takusan23.androidrustjni.MainActivity.<clinit>(MainActivity.kt:42)
    at java.lang.Class.newInstance(Native Method)
    at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:95)
    at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:44)
    at android.app.Instrumentation.newActivity(Instrumentation.java:1448)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3941)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4235)
    at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:112)
    at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:174)
    at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:109)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:81)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2636)
    at android.os.Handler.dispatchMessage(Handler.java:107)
    at android.os.Looper.loopOnce(Looper.java:232)
    at android.os.Looper.loop(Looper.java:317)
    at android.app.ActivityThread.main(ActivityThread.java:8705)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)

16KB ページサイズでビルドしたら古い端末で動くの?

で書かれてる通り、16KB ページサイズでビルドした共有ライブラリは4KB デバイスでも動くそうな。

一応古い端末を起動して試してみたけど、Galaxy S7 EdgeSnapdragon 820 (64bit) / Android 7)で起動できた。
16KB ページサイズ.soに置き換えるで問題なさそう。

Imgur

おわりです。お疲れ様でした。888