diff --git a/app/src/main/java/com/chiller3/rsaf/rclone/Authorizer.kt b/app/src/main/java/com/chiller3/rsaf/rclone/Authorizer.kt index d588ccd..3b79c46 100644 --- a/app/src/main/java/com/chiller3/rsaf/rclone/Authorizer.kt +++ b/app/src/main/java/com/chiller3/rsaf/rclone/Authorizer.kt @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Andrew Gunnerson + * SPDX-FileCopyrightText: 2023-2026 Andrew Gunnerson * SPDX-License-Identifier: GPL-3.0-only */ @@ -49,14 +49,18 @@ object Authorizer { .drop(2) private fun logAsIfGo(msg: String) { - Log.i(GO_TAG, msg) + // This is intentionally logging at the error level because HarmonyOS devices seem to only + // preserve error logs. + Log.e(GO_TAG, msg) } private fun authorizeBlockingLocked(cmd: String, listener: AuthorizeListener) { val args = parseCmd(cmd) Log.d(TAG, "Starting logcat") - val logcat = ProcessBuilder("logcat", "-v", "raw", "*:S", "$GO_TAG:V") + // We intentionally only capture error logs so that the HarmonyOS behavior is used + // everywhere for easier testing. + val logcat = ProcessBuilder("logcat", "-v", "raw", "*:S", "$GO_TAG:E") .redirectOutput(ProcessBuilder.Redirect.PIPE) .redirectErrorStream(true) .start() @@ -79,7 +83,7 @@ object Authorizer { try { processLogs(logcat.inputStream, markerNow, listener) - } catch (e: Exception) { + } catch (_: Exception) { cancelServer() } finally { server.join() diff --git a/rcbridge/rcbridge.go b/rcbridge/rcbridge.go index 1809f48..c6d49c4 100644 --- a/rcbridge/rcbridge.go +++ b/rcbridge/rcbridge.go @@ -61,6 +61,7 @@ var ( vfsInstances = make(map[string]*vfs.VFS) vfsOptValidKeys = make(map[string]bool) vfsOptStringKeys = make(map[string]bool) + stdoutHijackLock goSync.Mutex ) func init() { @@ -1307,15 +1308,41 @@ func (rbfile *RbFile) GetSize(errOut *RbError) int64 { // None of the underlying functions are available outside of the oauthutil // package and the higher level functions print to stdout and can only be killed // by sending a bad request to it. But since that's what we have to work with, -// we'll deal with it on the Android side by parsing logcat. +// we'll deal with it. +// +// There are two pieces of information we need: the local server URL and the +// oauth refresh token once authorized. The former can only be obtained via +// logcat since oauthutil.configSetup() uses fs.Logf(). The latter gets printed +// to stdout, which normally shows up in logcat due to gomobile, but some OS's +// like HarmonyOS only show messages with error priority. We work around this by +// temporarily sending stdout to stderr. func RbAuthorize(argsNullSep string, errOut *RbError) bool { + stdoutHijackLock.Lock() + defer stdoutHijackLock.Unlock() + var args = strings.Split(argsNullSep, "\x00") - err := config.Authorize(context.Background(), args, true, "") + origStdout, err := syscall.Dup(int(os.Stdout.Fd())) if err != nil { assignError(errOut, err, syscall.EIO) return false } + defer syscall.Close(origStdout) + + if err = syscall.Dup3(int(os.Stderr.Fd()), int(os.Stdout.Fd()), 0); err != nil { + assignError(errOut, err, syscall.EIO) + return false + } + + if err = config.Authorize(context.Background(), args, true, ""); err != nil { + assignError(errOut, err, syscall.EIO) + return false + } + + if err = syscall.Dup3(int(origStdout), int(os.Stdout.Fd()), 0); err != nil { + assignError(errOut, err, syscall.EIO) + return false + } return true }