Deployment for Android

It is now possible to experimentally deploy a game for Android using bdist_apps, without building Panda from source. This process will produce an Android App Bundle, which can be uploaded straight to the Play Store (I tested that it works), or you can use bundletool to generate an .apk you can install on your device directly. I am posting some instructions here for those who want to try it out.

Note that it is NOT production-ready yet. Notably missing is support for serializing and deserializing application state, and there is a bug that the application will crash when the phone is rotated.

Please let me know if you have any questions or find other stuff that doesn’t work!

REQUIREMENTS

You must use Python 3.8, since that’s what I used to build the Android wheels.

You need to install the latest version of Panda3D on the host. One of these builds, fresh off the buildbot, should do the trick. Building the latest master branch from GitHub works too.

You also need to download compiled Panda3D wheels for Android. These aren’t really wheels that you could install with pip, since binary wheels aren’t supported on Android, but the deployment system needs to work with wheels:
https://rdb.name/panda3d-1.11.0-cp38-cp38-android_arm64.whl
https://rdb.name/panda3d-1.11.0-cp38-cp38-android_armv7a.whl
https://rdb.name/panda3d-1.11.0-cp38-cp38-android_x86_64.whl
https://rdb.name/panda3d-1.11.0-cp38-cp38-android_x86.whl

CREATING A CERTIFICATE

bdist_apps can automatically sign the bundle using an upload key. This is optional, but before it can be uploaded to the Play Store it needs to be signed one way or the other. You can alternatively use jarsigner (part of the JDK) to do this after the fact.

You can generate a certificate yourself, if you don’t have one already. The following openssl command can be used:

openssl genpkey -algorithm RSA -aes256 -out private.pem
openssl req -new -x509 -sha256 -days 365 -key private.pem > cert.pem

If you are already an Android app developer and want to use an existing key, you can export it to PEM using keytool, or sign it manually using jarsigner. Let me know if you need help with this.

CONFIGURING

There is some specific stuff needed in the setup.py file for Android. Here is an annotated version that packages the Android sample:

from setuptools import setup

# Necessary to switch Panda to the OpenGL ES 1 or 2 renderer
# pandagles2 supports shaders, but no fixed-function pipeline
# pandagles supports fixed-function pipeline, but no shaders
PRC_DATA = '''
load-display pandagles2
aux-display pandagles

notify-level info
gl-debug true
'''

setup(
    name="Asteroids",
    version="1.0.0",
    options={
        'build_apps': {
            # This field is required by Android and uniquely identifies the app.
            # It is usually based on the inverse of the developer's domain name
            # (e.g. "gamestudio.com" becomes "com.gamestudio"), followed by any
            # other components as needed to further identify the application.
            'application_id': 'org.panda3d.samples.asteroids',

            # This should be an integer that starts at 1 and is incremented with
            # every app update. This is just internal, whereas the ``version``
            # metadata field is used to show an arbitrary dot-separated version
            # string to the user. Every time you upload a new release to the
            # Play Console, this number must be updated.
            'android_version_code': 1,

            'gui_apps': {
                'asteroids': 'main.py',
            },
            'platforms': ['android'],
            'plugins': [
                # Note use of pandagles2/pandagles instead of pandagl
                'pandagles2',
                'pandagles',
                'p3openal_audio',
            ],
            'include_patterns': [
                '**/*.png',
                '**/*.jpg',
                '**/*.egg',
            ],
            'extra_prc_data': PRC_DATA,

            # Required icon resolutions: 48x48, 72x72, 96x96, 144x144, 192x192
            # ..but make sure you author the logo in at least 512x512 since that
            # is the required resolution for the Play Store
            'icons': {'*': 'logo.png'},
        },

        'bdist_apps': {
            'signing_certificate': 'cert.pem',
            'signing_private_key': 'private.pem',
            # optional: Panda will otherwise ask passphrase on command-line
            #'signing_passphrase': 'panda3d_is_cool',
        },
    },
    # Choosing a classifier in the Games category makes it marked a "Game"
    classifiers=['Topic :: Games/Entertainment'],
)

And change your requirements.txt to point it to the path where you downloaded the Android wheels:

-f /path/to/android/wheels
panda3d

BUILDING

Just run:

python3.8 setup.py bdist_apps

This will generate an .aab file in the dist directory. If you want to install it on a device, you first need to use bundletool to turn it into an .apks file (which is really a zip file containing multiple .apk files, one for each architecture):

bundletool build-apks
    --bundle dist/Asteroids-1.0.0_android.aab \
    --output packages.apks \
    --ks-key-alias androiddebugkey \
    --ks-pass pass:android \
    --ks debug.ks \
    --verbose

Note that bundletool also takes a signing key. This is used to sign the .apk files, which is normally done by the Play Store, and is usually a separate key from the upload key. Generate it using keytool:

keytool -genkey -v -keystore debug.ks -alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000

OK, now that you’ve got an .apks file, you can extract the individual .apk files using any zip utility, or you can install it directly to your device (requires adb to be set up):

bundletool install-apks --apks packages.apks

DEBUGGING

There is no stdout/stderr on Android. Instead, all output is being routed to Android’s log system. You can use adb to follow the log stream. The log is extremely verbose without any filters, so I recommend using a set of filters like this one:

adb logcat -v brief -v color 'Panda3D:V' 'Python:V' 'threaded_app:V' 'AndroidRuntime:I' 'linker:W' '*:F'

LIMITATIONS

Only Python 3.8 is supported for now. We can later add more recent versions when we automate the build process, but I don’t want to spend too much time creating builds right now. Older versions of Python probably won’t be supported (unless someone gives me a really good reason to).

No multi-touch support yet, only single touches supported.

No Cg shaders or shader generator supported. This will be addressed soon, when the shaderpipeline branch is merged.

Some thirdparty packages (ARToolKit, OpenSSL, OpenCV, OpenEXR, libsquish, FFMpeg) are not yet included.

Minimum supported version is Android 4.4 (API level 19), the lowest supported by the Play Store, but we can look into lowering this requirements if people find this necessary.

You can’t really use thirdparty Python packages that contain binary code, because binary wheels for Android aren’t really a thing (yet).

Doesn’t handle configuration changes (such as when rotating the phone) gracefully yet. Also see #1216.

BUILDING FROM SOURCE

If you want to build the Android wheels from source, use these thirdparty packages, built for the four major achitectures:

https://rdb.name/thirdparty-android.tar.gz

You need a build of Panda3D installed on the host so that the build process can find the interrogate tool. You also need to use Python 3.8 (or you need to rebuild a more recent version of Python using the patches and commands included in the README of the python directory in the thirdparty tools).

I used the following makepanda commands:

export ANDROID_SDK_ROOT=/home/rdb/local/android
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-arm64 --arch arm64 --target android-21 --threads 6 --wheel
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-armv7a --arch armv7a --target android-19 --threads 6 --wheel
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86_64 --arch x86_64 --target android-21 --threads 6 --wheel
python3.8 makepanda/makepanda.py --everything --outputdir built-droid-x86 --arch x86 --target android-19 --threads 6 --wheel
7 Likes