{"record":{"id":"a4e0c203949110ad","repo":"Tencent/tinker","slug":"cannot-query-transaction-code-of-performdexoptseco","errorCode":null,"errorMessage":"Cannot query transaction code of performDexOptSecondary.","messagePattern":"Cannot query transaction code of performDexOptSecondary\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java","lineNumber":375,"sourceCode":"    }\n\n    private static final int[] sPerformDexOptSecondaryTransactionCode = {-1};\n\n    private static void performDexOptSecondaryByTransactionCode(Context context) throws IllegalStateException {\n        synchronized (sPerformDexOptSecondaryTransactionCode) {\n            if (sPerformDexOptSecondaryTransactionCode[0] == -1) {\n                try {\n                    final Method getDeclaredFieldMethod = ShareReflectUtil.findMethod(\n                            Class.class, \"getDeclaredField\", String.class);\n                    getDeclaredFieldMethod.setAccessible(true);\n                    final Field cstField = (Field) getDeclaredFieldMethod.invoke(\n                            Class.forName(\"android.content.pm.IPackageManager$Stub\"),\n                            \"TRANSACTION_performDexOptSecondary\"\n                    );\n                    cstField.setAccessible(true);\n                    sPerformDexOptSecondaryTransactionCode[0] = (int) cstField.get(null);\n                } catch (Throwable thr) {\n                    throw new IllegalStateException(\"Cannot query transaction code of performDexOptSecondary.\", thr);\n                }\n            }\n        }\n\n        ShareTinkerLog.i(TAG, \"[+] performDexOptSecondaryByTransactionCode, code: %s\",\n                sPerformDexOptSecondaryTransactionCode[0]);\n\n        final IBinder pmsBinder = getPMSBinderProxy(context);\n        Parcel data = null;\n        Parcel reply = null;\n        try {\n            data = Parcel.obtain();\n            reply = Parcel.obtain();\n            boolean result;\n            try {\n                data.writeInterfaceToken(pmsBinder.getInterfaceDescriptor());\n                data.writeString(context.getPackageName());\n                final String compileFilter = ShareTinkerInternals.isNewerOrEqualThanVersion(31 /* Android S */, true)","sourceCodeStart":357,"sourceCodeEnd":393,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/TinkerDexOptimizer.java#L357-L393","documentation":"To trigger secondary dex optimization on demand, Tinker reflects into the hidden android.content.pm.IPackageManager$Stub class to read the TRANSACTION_performDexOptSecondary constant (the binder transaction code). This error means the reflection chain (Class.getDeclaredField via a bootstrapped Method, then reading the static field) threw, so the transaction code could not be determined and the raw binder call cannot be built.","triggerScenarios":"Running performDexOptSecondaryByTransactionCode on a ROM where IPackageManager$Stub no longer declares TRANSACTION_performDexOptSecondary, where the hidden-API greylist blocks access to it, or where an OEM renamed/obfuscated framework internals. Also on Android versions that changed the binder stub layout.","commonSituations":"Android 9+ hidden API restrictions (dark greylist); OEM frameworks (MIUI, EMUI) that alter AOSP internals; newer Android releases where the transaction constant was removed.","solutions":["Update Tinker to the latest release, which adapts the reflection for new API levels.","Enable interpret mode so the dex-opt-by-binder path is never taken.","As a last resort for a pinned old Tinker, catch this IllegalStateException around patch load and fall back to interpret-only loading."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Pre-flight: can we see the transaction constant at all?\ntry {\n    Class<?> stub = Class.forName(\"android.content.pm.IPackageManager$Stub\");\n    Field f = stub.getDeclaredField(\"TRANSACTION_performDexOptSecondary\");\n} catch (Throwable t) {\n    // binder-dexopt path unavailable: use interpret mode\n}","typeGuard":null,"tryCatchPattern":"try {\n    performDexOptSecondaryByTransactionCode(context);\n} catch (IllegalStateException e) {\n    // reflection blocked on this ROM; degrade to interpret mode\n    useInterpretMode = true;\n}","preventionTips":["Stay on the latest Tinker SDK so reflection targets cover new API levels.","Enable interpret mode on ROMs known to block hidden API access.","Treat this exception as a signal to skip binder-based dexopt, not to crash patch loading."],"tags":["android","reflection","binder","hidden-api","dexopt","tinker"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}