Maintaining legacy Android applications or auditing third-party binaries often requires elevating the package's targetSdkVersion to modern API levels such as Android 15 (API 35) or Android 16 (API 36). Google Play deployment policies and newer Android runtime environments strictly enforce modern platform behaviors on apps targeting recent API versions. When decompiling and bumping the target SDK version in an existing APK, developers frequently encounter immediate installation aborts, manifest parsing errors, and runtime crashes caused by behavioral changes introduced in Android 14 through 16.
In this tutorial, we explore the complete engineering workflow for upgrading targetSdkVersion directly within decompiled bytecode, addressing manifest attribute collisions, adapting foreground service declarations, and patching Smali invocations to comply with modern runtime restrictions.
Understanding Runtime Changes in API 35 and 36
Bumping targetSdkVersion does not merely alter a manifest header; it activates dormant platform enforcements baked into modern Android runtimes. The most critical enforcements include:
- Explicit BroadcastReceiver Export Flags: Invocations of
registerReceiver()for context-registered receivers must explicitly declare eitherRECEIVER_EXPORTED(0x2) orRECEIVER_NOT_EXPORTED(0x4). Omission causes an immediateSecurityExceptionat runtime. - Foreground Service Types: Any foreground service started with
startForeground()must define a specificandroid:foregroundServiceTypein bothAndroidManifest.xmland the runtime call. - Strict Intent Redirection and Component Security: Implicit intents targeting internal components are strictly rejected by the Android package manager.
- 16 KB Page-Size Memory Alignment: Native shared libraries (
.so) loaded within the package must align to 16 KB page boundaries when executing on modern 64-bit devices.
Step 1: Decompiling the APK with APKTool Studio
To inspect and modify the bytecode and manifest, upload your binary to APKTool Studio, a powerful browser-based suite designed for online decompilation, resource decoding, and recompilation.
- Navigate to APKTool Studio in your web browser.
- Upload your target
.apkfile to initiate disassembly. - The platform extracts decoded resources, human-readable
AndroidManifest.xml, and disassembled.smalifiles within your browser workspace, eliminating the need to configure local Java runtimes or SDK binaries.
Step 2: Updating the AndroidManifest.xml and apktool.yml
Once decompilation completes, access the root project structure. The SDK level is specified in two distinct locations: apktool.yml and AndroidManifest.xml.
Adjusting apktool.yml
Open apktool.yml and locate the targetSdkVersion property under sdkInfo:
sdkInfo:
minSdkVersion: '24'
targetSdkVersion: '35'Updating AndroidManifest.xml
Ensure the <uses-sdk> element inside AndroidManifest.xml reflects the target API level:
<uses-sdk
android:minSdkVersion='24'
android:targetSdkVersion='35' />Declaring Foreground Service Types
If your application employs background services, Android 15 requires explicit declaration of service types and permissions. Add the relevant permissions and attributes:
<uses-permission android:name='android.permission.FOREGROUND_SERVICE' />
<uses-permission android:name='android.permission.FOREGROUND_SERVICE_DATA_SYNC' />
<service
android:name='.services.SyncService'
android:exported='false'
android:foregroundServiceType='dataSync' />Step 3: Patching Smali for BroadcastReceiver Registration
The single most prevalent crash encountered after elevating an APK to targetSdkVersion 34+ is the missing export flag in dynamic receiver registration. Legacy code often calls the 2-parameter registerReceiver(receiver, filter) method, which throws an exception on modern API targets.
Identifying the Legacy Smali Call
Searching for registerReceiver across your Smali folders typically reveals invocations formatted like this:
# Legacy invocation with 2 parameters: Context.registerReceiver(BroadcastReceiver, IntentFilter)
invoke-virtual {p0, v0, v1}, Landroid/content/Context;->registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;Patching to the 4-Parameter or 3-Parameter Exported Variant
To resolve this, update the method descriptor to call the 3-parameter overload registerReceiver(BroadcastReceiver, IntentFilter, int) and supply the appropriate integer flag. For internal non-exported receivers, pass 0x4 (Context.RECEIVER_NOT_EXPORTED).
# Allocate constant 0x4 (RECEIVER_NOT_EXPORTED) into an unused register
const/4 v2, 0x4
# Invoke the modern 3-parameter registerReceiver method
invoke-virtual {p0, v0, v1, v2}, Landroid/content/Context;->registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;I)Landroid/content/Intent;Ensure the target method has sufficient local registers allocated via the .locals directive. If v2 was not previously allocated or collides with an existing variable, increment .locals by one at the top of the method definition.
Step 4: Recompiling and Signing with Modern Signature Schemes
Android 15 and 16 enforce strict APK signature validation. Binaries signed solely with Signature Scheme v1 (JAR signing) will fail package installation with INSTALL_PARSE_FAILED_NO_CERTIFICATES. Modern installations require full Scheme v2 and v3 signing.
- Within APKTool Studio, select the Build & Recompile option.
- Once the APK is assembled, navigate to the Signer module.
- Select or generate a compatible
PKCS#12orJKSkeystore. - Enable v2 and v3 Signing in the signing configuration to ensure complete compatibility across Android 7.0 through Android 16.
- Download the verified, re-signed APK directly to your testing environment.
Step 5: Verifying Runtime Behavior via ADB
After installing your patched APK, verify that the package manager recognizes the updated target SDK and monitor Logcat for runtime compliance warnings:
adb install -r my_updated_app.apk
adb shell dumpsys package com.example.app | grep -E "targetSdk|versionCode"
adb logcat -s AndroidRuntime:E ActivityThread:WConfirm that no SecurityException occurs during app initialization, background service start, or dynamic receiver registration.
Summary
Modernizing legacy APKs by updating targetSdkVersion involves more than bumping numeric values; it necessitates meticulous Smali bytecode auditing to fulfill contemporary Android sandboxing constraints. By using browser-native tools like APKTool Studio to decompile, audit manifest schemas, patch dynamic Smali receiver flags, and apply Scheme v2/v3 signatures, developers and security auditors can systematically upgrade binaries for modern Android deployments.