An unofficial Android wrapper around ExifTool that runs it on-device using a Perl interpreter cross-compiled for Android.
The original motivation: on Pixel devices, screenshots and Snapchat images
arrive without EXIF timestamps, and Google Photos uses upload time as a
fallback rather than file modification time. This app embeds EXIF
DateTimeOriginal from either the filename (Android screenshots) or the
file modification date (Snapchat etc.) so Google Photos sorts them
correctly.
There is also an "advanced custom command" mode for arbitrary exiftool flags when the presets aren't enough.
This app modifies — and in some modes overwrites — your photo files. Make
backups. See DISCLAIMER.md for the full warranty /
liability disclaimer; the app shows a condensed version on first launch
and again before enabling advanced mode.
Install the latest signed APK from the Releases page, grant the storage permission on first launch, and pick files or a directory.
Pick the APK matching your device's ABI (most modern Android phones are
arm64-v8a); if unsure, the universal APK works on all four supported
ABIs at the cost of a larger download.
Each release ships with SHA256SUMS and a SLSA build-provenance
attestation. To verify what you downloaded was produced by this repo's CI
from a specific commit:
shasum -a 256 -c SHA256SUMS
gh attestation verify <apk> -R bestvibes/exiftoolwrapper-androidSee RELEASING.md for how releases are built and signed.
Earlier versions of this app committed pre-built perl_*.xz and
exiftool.xz blobs straight into app/src/main/res/raw/. Those blobs had no
provenance and no update path, which was both a security concern and the
reason ExifTool was stuck at a 7-year-old release
(#2).
The current build pipeline replaces that with:
native/PINS— exact pinned versions and SHA256s for perl, ExifTool, perl-cross, and the Android NDK.native/build.sh— cross-builds perl with perl-cross in the standard configuration (DynaLoader enabled), runsmake installto a staging tree, then renames the perl interpreter tolibperl.soand each XS module tolibperl_xs_<flat_name>.soso Android's installer ships them all via the standardjniLibsmechanism..github/workflows/native.yml— builds all four ABIs (arm64-v8a,armeabi-v7a,x86_64,x86) reproducibly on GitHub Actions, attaches SLSA build-provenance attestations, and opens a PR updating the in-tree binaries.
To verify a release:
- Pull the APK and SHA256 the embedded
lib/<abi>/libperl.soand anylib/<abi>/libperl_xs_*.so. - Compare against the SHA256s in the SLSA attestation attached to the
matching
native-v*GitHub release. - Compare against the bytes committed at
app/src/main/jniLibs/<abi>/lib*.soin this repository.
All three should match. See native/README.md for the
longer explanation of how the chain works.
libperl.so(the perl interpreter) andlibperl_xs_*.so(one per XS module:POSIX,Compress::Raw::Zlib,IO::Compress::*, etc.) all live underapplicationInfo.nativeLibraryDir, an exec mount populated by Android's installer at install time. Nochmodneeded; no W^X violation.assets/perl5.tar(the ExifTool script +Image::ExifTool/lib tree + perl's pure-perl@INC) is extracted tofilesDir/perl5/on first launch viaAssetExtractor.AssetExtractoralso creates symlinks atfilesDir/perl5/arch/auto/<dist>/<dist>.sopointing to the matchingnativeLibraryDir/libperl_xs_*.so, so perl'sDynaLoaderfinds each XS module at the canonical archlib path. The symlinks are recreated on every launch because Android randomizesnativeLibraryDiracross app installs.- Every invocation is
ProcessBuilder(libperl.so, -I arch, -I lib, exiftool, …)— argv list, no shell, no string interpolation. - File handling: SAF picker URIs are copied to a per-run cache subdir
(preserving the original filename, since some exiftool modes parse it),
exiftool reads/writes the cache copy, and for write modes the result is
copied back to the source URI via
openOutputStream("wt"). The picker usesACTION_OPEN_DOCUMENTso URIs come back with both read and write permission grants in one step. - The custom-command field is sanitized by
CommandSanitizer, which blocks the small set of exiftool flags that can load arbitrary Perl (-config,-@,-stay_open,-execute*). - Every executed command is appended to
filesDir/command_history.txtfor audit (capped at 1 MB).
- Build pipeline approach borrowed from Termux
(the
lib*.sojniLibs trick for shipping executables). - Cross-compilation by perl-cross.
- ExifTool by Phil Harvey.
