Skip to content

Commit

Permalink
Fixed|Revert: Revert and still use TermuxAm for broadcast commands …
Browse files Browse the repository at this point in the history
…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 <action> 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
  • Loading branch information
agnostic-apollo committed Dec 23, 2023
1 parent d8d6f23 commit 8a08e9b
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 44 deletions.
40 changes: 0 additions & 40 deletions am-libexec-packaged
Original file line number Diff line number Diff line change
Expand Up @@ -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 <action> 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_code=$?
# Do not log `echo: I/0 error` if stdout is closed
echo "$output" 2>/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
Expand Down
17 changes: 13 additions & 4 deletions app/src/main/java/com/termux/termuxam/Am.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ public void onShowUsage(PrintStream out) {
" --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
" specified then send to all users.\n" +
" --receiver-permission <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 <COMPONENT>\n" +
Expand Down Expand Up @@ -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()
}
}

/*
Expand Down

0 comments on commit 8a08e9b

Please sign in to comment.