From 8a08e9bd31f3bc763f30ee1bf9c32204d0a89a53 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sun, 24 Dec 2023 00:15:40 +0500 Subject: [PATCH] Fixed|Revert: Revert and still use `TermuxAm` for broadcast commands on Android `>= 14` but do not wait for result and always exit with exit code 0 When `broadcastIntent()` is called with a `IIntentReceiver resultTo` to be able to get back the broadcast result, Android uses the `IApplicationThread caller` passed to get the `ProcessRecord` of the app from its internal cache (`getRecordForAppLOSP()`) based on the `caller`. Since `TermuxAm` uses a fake `IApplicationThread` that does not belong to an app, the `resultToApp` would not be found. For this, android logs the `Sending broadcast with resultTo requires resultToApp` exception and terminates the process. To prevent this from happening, we do not pass the `resultTo` so that Android uses the `system_server` `ProcessRecord` instead at the cost of not being able to get back the result for the broadcast. This will trigger the following exception in `logcat`, but if broadcast was valid, then it will still have been sent. ``` java.lang.RuntimeException: BroadcastReceiver trying to return result during a non-ordered broadcast at android.content.BroadcastReceiver$PendingResult.checkSynchronousHint(BroadcastReceiver.java:351) at android.content.BroadcastReceiver$PendingResult.setResultCode(BroadcastReceiver.java:154) at com.termux.api.util.ResultReturner.lambda$returnData$0(ResultReturner.java:196) at com.termux.api.util.ResultReturner$$ExternalSyntheticLambda0.run(Unknown Source:8) at java.lang.Thread.run(Thread.java:1012) ``` - https://cs.android.com/android/_/android/platform/frameworks/base/+/a51563231919be01f2d3de53a88df70154ac8992 - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/core/java/android/content/BroadcastReceiver.java;l=350 - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java;l=14391 https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java;l=15309 We cannot use `/system/bin/am` on Android `14` December security patch as `am` commands can only be run with `adb` and `root` users now. Normally, no output will be received when the command will be run and exit code will be `255`. I got the exception once, not sure how to reproduce, there is likely some AOSP bug, using subshell hack and even disabling selinux does not work, but it did get logged while selinux was disabled. - https://cs.android.com/android/_/android/platform/frameworks/base/+/3ef3f18ba3094c4cc4f954ba23d1da421f9ca8b0 ``` java.lang.SecurityException: Shell commands are only callable by root or shell at android.os.Parcel.createExceptionOrNull(Parcel.java:3057) at android.os.Parcel.createException(Parcel.java:3041) at android.os.Parcel.readException(Parcel.java:3024) at android.os.Parcel.readException(Parcel.java:2966) at android.os.BinderProxy.shellCommand(BinderProxy.java:692) at com.android.commands.am.Am.runAmCmd(Am.java:146) at com.android.commands.am.Am.onRun(Am.java:87) at com.android.internal.os.BaseCommand.run(BaseCommand.java:62) at com.android.commands.am.Am.main(Am.java:54) at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:359) Caused by: android.os.RemoteException: Remote stack trace: at com.android.server.am.ActivityManagerService.onShellCommand(ActivityManagerService.java:9763) at android.os.Binder.shellCommand(Binder.java:1071) at android.os.Binder.onTransact(Binder.java:888) at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:5267) at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2763) ``` Closes termux/termux-app#3754 Related termux/termux-app#3647, termux/termux-api#638 --- am-libexec-packaged | 40 ------------------- app/src/main/java/com/termux/termuxam/Am.java | 17 ++++++-- 2 files changed, 13 insertions(+), 44 deletions(-) diff --git a/am-libexec-packaged b/am-libexec-packaged index 9fef7d0..2f04a7e 100755 --- a/am-libexec-packaged +++ b/am-libexec-packaged @@ -2,51 +2,11 @@ TERMUX_AM_VERSION=0.6.0 AM_APK_PATH="@TERMUX_PREFIX@/libexec/termux-am/am.apk" -is_int() { - case "$1" in - ''|*[!0-9]*) return 1;; - *) return 0;; - esac -} - if [ "$1" = "--version" ]; then echo "$TERMUX_AM_VERSION" exit 0 fi -# If sdk version is not exported by app, then get value with getprop instead. -if ! is_int "$ANDROID__BUILD_VERSION_SDK"; then - ANDROID__BUILD_VERSION_SDK="$(getprop "ro.build.version.sdk")" - if ! is_int "$ANDROID__BUILD_VERSION_SDK"; then - echo "Failed to get android build version sdk with getprop" 1>&2 - exit 1 - fi -fi - -# Do not use TermuxAm for broadcast commands on Android >= 14 since it -# will trigger the `Sending broadcast with resultTo requires resultToApp` -# exception in logcat by ActivityManagerService and will hang the -# app_process command forever since no result callback is received -# by TermuxAm `IIntentReceiver.performReceive()`. -# - https://github.com/termux/TermuxAm/issues/9#issuecomment-1649867810 -if [ "$ANDROID__BUILD_VERSION_SDK" -ge 34 ] && [ "$1" = "broadcast" ]; then - is_int "$TERMUX__USER_ID" || TERMUX__USER_ID=0 - shift 1 # Remove the first `broadcast` argument - - # Use a subshell with specified redirection to prevent - # `cmd: Failure calling service activity` error on Android >= 8 - # due to selinux restrictions where the `system_server` source - # domain does not have access to `untrusted_app_all_devpts` - # `pty` devices when a source transition is made from `untrusted_app*` - # domain when `/system/bin/am` is executed. - # - https://github.com/termux/termux-packages/discussions/8292#discussioncomment-5102555 - output="$(/system/bin/am broadcast --user "$TERMUX__USER_ID" "$@" 2>&1 /dev/null - exit $exit_code -fi - # If apk file is writable and current effective user is not root (0), # system (1000) and shell (2000), then remove write bit from apk # permissions for current used for Android >= 14 since it will trigger diff --git a/app/src/main/java/com/termux/termuxam/Am.java b/app/src/main/java/com/termux/termuxam/Am.java index 7d56618..f7d6c5d 100644 --- a/app/src/main/java/com/termux/termuxam/Am.java +++ b/app/src/main/java/com/termux/termuxam/Am.java @@ -221,6 +221,7 @@ public void onShowUsage(PrintStream out) { " --user | all | current: Specify which user to send to; if not\n" + " specified then send to all users.\n" + " --receiver-permission : Require receiver to hold permission.\n" + + "On Android '>= 14', broadcast result will not be waited for and exit code will always be 0.\n" + "\n" + /* "am instrument: start an Instrumentation. Typically this target \n" + @@ -856,11 +857,19 @@ private Integer sendBroadcast() throws Exception { String[] requiredPermissions = mReceiverPermission == null ? null : new String[] {mReceiverPermission}; System.out.println("Broadcasting: " + intent); - mAm.broadcastIntent(/*null,*/ intent, /*null,*/ receiver, /*0, null, null,*/ requiredPermissions, - /*android.app.AppOpsManager.OP_NONE, null,*/ true, false, mUserId); - receiver.waitForFinish(); - return null; // Do not exit in main() + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU) { + mAm.broadcastIntent(/*null,*/ intent, /*null,*/ null, /*0, null, null,*/ requiredPermissions, + /*android.app.AppOpsManager.OP_NONE, null,*/ true, false, mUserId); + System.out.println("Broadcast sent without waiting for result"); + return 0; + } else { + mAm.broadcastIntent(/*null,*/ intent, /*null,*/ receiver, /*0, null, null,*/ requiredPermissions, + /*android.app.AppOpsManager.OP_NONE, null,*/ true, false, mUserId); + receiver.waitForFinish(); + + return null; // Do not exit in main() + } } /*