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.

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

REQUIREMENTS

You must use Python 3.13, since that’s the first version of Python to officially support Android, and 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-cp313-cp313-android_arm64.whl
https://rdb.name/panda3d-1.11.0-cp313-cp313-android_armv7a.whl
https://rdb.name/panda3d-1.11.0-cp313-cp313-android_x86_64.whl
https://rdb.name/panda3d-1.11.0-cp313-cp313-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.13 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.stdout:V' 'python.stderr:V' 'threaded_app:V' 'AndroidRuntime:I' 'linker:W' '*:F'

LIMITATIONS

Only Python 3.13 is supported for now. 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, VRPN) are not yet included.

Minimum supported version is Android 5.0 (API level 21), the lowest supported by the Play Store, but we can look into lowering this requirement 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).

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

This includes Python 3.13, you also need to use Python 3.13 on the host. I built Python with some patches, which you can get from this repo.

I used the following makepanda commands:

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

Thirdparty packages for Android are now being built automatically, and can be obtained by going to the latest build here and checking out the Artifacts:

Missing from this are: Python (use the one from here for now), FCollada, VRPN, ARToolKit.

2 Likes
I'm having a problem to deploy on Android using this tutorial https://discourse.panda3d.org/t/deployment-for-android/28226 and the Asteroids sample. 

I'm using this build of panda3d Panda3D-SDK-1.11.0pre-68f0931-x64.exe which is available at this link https://buildbot.panda3d.org/downloads/68f0931f43284345893a90d5bba9ba5df8aa53bb/ .

I'm using Python 3.8.6rc1




My setup.py is this:

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'],
)




my requirements.txt is this:

-f wheels/
panda3d





When a run python setup.py bdist_apps i get this:

C:\Users\count_zero\Desktop\asteroidsAndroid>python setup.py bdist_apps
running bdist_apps
running build_apps
Building platforms: android
Gathering wheels for platform: android_arm64
Looking in indexes: https://pypi.org/simple, https://archive.panda3d.org/simple/
opt, https://archive.panda3d.org/thirdparty
Looking in links: c:\Users\count_zero\Desktop\asteroidsAndroid\wheels/
Processing c:\users\count_zero\desktop\asteroidsandroid\wheels\panda3d-1.11.0-cp
38-cp38-android_arm64.whl
  File was already downloaded c:\users\count_zero\desktop\asteroidsandroid\build
\__whl_cache__\android_arm64_cp38\panda3d-1.11.0-cp38-cp38-android_arm64.whl
Successfully downloaded panda3d
Could not find an optimized wheel (using index https://archive.panda3d.org/simpl
e/opt) for platform: android_arm64
Building runtime for platform: android_arm64
There are some missing modules: ['_bz2', '_hashlib', '_lzma', 'android_log']
Traceback (most recent call last):
  File "setup.py", line 14, in <module>
    setup(
  File "C:\Users\count_zero\AppData\Local\Programs\Python\Python38\lib\site-pack
ages\setuptools\__init__.py", line 165, in setup
    return distutils.core.setup(**attrs)
  File "C:\Users\count_zero\AppData\Local\Programs\Python\Python38\lib\distutils
\core.py", line 148, in setup
    dist.run_commands()
  File "C:\Users\count_zero\AppData\Local\Programs\Python\Python38\lib\distutils
\dist.py", line 966, in run_commands
    self.run_command(cmd)
  File "C:\Users\count_zero\AppData\Local\Programs\Python\Python38\lib\distutils
\dist.py", line 985, in run_command
    cmd_obj.run()
  File "C:\Panda3D-1.11.0-x64\direct\dist\commands.py", line 1669, in run
    self.run_command('build_apps')
  File "C:\Users\count_zero\AppData\Local\Programs\Python\Python38\lib\distutils
\cmd.py", line 313, in run_command
    self.distribution.run_command(command)
  File "C:\Users\count_zero\AppData\Local\Programs\Python\Python38\lib\distutils
\dist.py", line 985, in run_command
    cmd_obj.run()
  File "C:\Panda3D-1.11.0-x64\direct\dist\commands.py", line 530, in run
    self.build_binaries(lib_dir, platform + suffix)
  File "C:\Panda3D-1.11.0-x64\direct\dist\commands.py", line 1112, in build_bina
ries
    self.copy_with_dependencies(source_path, target_path, search_path)
  File "C:\Panda3D-1.11.0-x64\direct\dist\commands.py", line 1378, in copy_with_
dependencies
    self.copy(source_path, target_path)
  File "C:\Panda3D-1.11.0-x64\direct\dist\commands.py", line 1364, in copy
    data = whlfile.read(wf.replace(os.path.sep, '/'))
  File "C:\Users\count_zero\AppData\Local\Programs\Python\Python38\lib\zipfile.p
y", line 1475, in read
    with self.open(name, "r", pwd) as fp:
  File "C:\Users\count_zero\AppData\Local\Programs\Python\Python38\lib\zipfile.p
y", line 1514, in open
    zinfo = self.getinfo(name)
  File "C:\Users\count_zero\AppData\Local\Programs\Python\Python38\lib\zipfile.p
y", line 1441, in getinfo
    raise KeyError(
KeyError: "There is no item named 'deploy_libs/_sha3.so' in the archive"

I think I have fixed that problem in this commit, you could grab this version of commands.py and use it to replace the version in the direct/dist folder:
https://raw.githubusercontent.com/panda3d/panda3d/master/direct/src/dist/commands.py

I have just updated the thirdparty download and the instructions for Python 3.13, since Python 3.8 is EOL and Python 3.13 introduces proper Android support.