diff --git a/.githooks/post-commit b/.githooks/post-commit new file mode 100755 index 0000000..39b8c46 --- /dev/null +++ b/.githooks/post-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail +ROOT="$(git rev-parse --show-toplevel)" +exec python3 "$ROOT/scripts/header-post-commit.py" diff --git a/app/src/main/java/com/foxx/androidcast/crash/CrashReportStore.java b/app/src/main/java/com/foxx/androidcast/crash/CrashReportStore.java index 3363cd4..3e6f70c 100644 --- a/app/src/main/java/com/foxx/androidcast/crash/CrashReportStore.java +++ b/app/src/main/java/com/foxx/androidcast/crash/CrashReportStore.java @@ -13,6 +13,8 @@ package com.foxx.androidcast.crash; **********************************************************************/ import org.json.JSONObject; +import com.foxx.androidcast.util.IoUtils; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -82,7 +84,7 @@ public final class CrashReportStore { public static JSONObject readJson(File file) { try (FileInputStream fis = new FileInputStream(file)) { - return new JSONObject(new String(fis.readAllBytes(), StandardCharsets.UTF_8)); + return new JSONObject(new String(IoUtils.readAllBytes(fis), StandardCharsets.UTF_8)); } catch (Exception e) { return null; } diff --git a/app/src/main/java/com/foxx/androidcast/crash/CrashWatcherService.java b/app/src/main/java/com/foxx/androidcast/crash/CrashWatcherService.java index 25c7bb2..20175e4 100644 --- a/app/src/main/java/com/foxx/androidcast/crash/CrashWatcherService.java +++ b/app/src/main/java/com/foxx/androidcast/crash/CrashWatcherService.java @@ -20,6 +20,8 @@ import android.os.Build; import android.os.IBinder; import android.util.Log; +import com.foxx.androidcast.util.IoUtils; + import org.json.JSONObject; import java.io.File; @@ -117,7 +119,7 @@ public final class CrashWatcherService extends Service { try { byte[] buf; try (FileInputStream fis = new FileInputStream(stub)) { - buf = fis.readAllBytes(); + buf = IoUtils.readAllBytes(fis); } String text = new String(buf, StandardCharsets.UTF_8); String signal = "UNKNOWN"; diff --git a/app/src/main/java/com/foxx/androidcast/ota/OtaBundleInstaller.java b/app/src/main/java/com/foxx/androidcast/ota/OtaBundleInstaller.java index a69500d..afb198b 100644 --- a/app/src/main/java/com/foxx/androidcast/ota/OtaBundleInstaller.java +++ b/app/src/main/java/com/foxx/androidcast/ota/OtaBundleInstaller.java @@ -14,6 +14,8 @@ package com.foxx.androidcast.ota; import android.content.Context; import android.util.Log; +import com.foxx.androidcast.util.IoUtils; + import org.json.JSONObject; import java.io.BufferedInputStream; @@ -81,7 +83,7 @@ public final class OtaBundleInstaller { File signFile = new File(extractDir, ENTRY_SIGN); if (signFile.isFile()) { try (FileInputStream fis = new FileInputStream(signFile)) { - byte[] buf = fis.readAllBytes(); + byte[] buf = IoUtils.readAllBytes(fis); OtaSignBundle sign = OtaSignBundle.fromJson( new JSONObject(new String(buf, StandardCharsets.UTF_8))); expectedSha = sign.sha256Hex; diff --git a/app/src/main/java/com/foxx/androidcast/ota/OtaSettingsStore.java b/app/src/main/java/com/foxx/androidcast/ota/OtaSettingsStore.java index 18e117d..fbd0151 100644 --- a/app/src/main/java/com/foxx/androidcast/ota/OtaSettingsStore.java +++ b/app/src/main/java/com/foxx/androidcast/ota/OtaSettingsStore.java @@ -13,8 +13,9 @@ package com.foxx.androidcast.ota; import android.content.Context; import com.foxx.androidcast.AppPreferences; -import com.foxx.androidcast.crash.CrashSettings; import com.foxx.androidcast.BuildConfig; +import com.foxx.androidcast.crash.CrashSettings; +import com.foxx.androidcast.util.IoUtils; import org.json.JSONArray; import org.json.JSONObject; @@ -90,7 +91,7 @@ final class OtaSettingsStore { private static JSONObject readSettingsJson(Context context) { File f = new File(context.getFilesDir(), SETTINGS_FILE); try (FileInputStream fis = new FileInputStream(f)) { - byte[] data = fis.readAllBytes(); + byte[] data = IoUtils.readAllBytes(fis); String text = new String(data, StandardCharsets.UTF_8); return new JSONObject(text); } catch (Exception ignored) { diff --git a/app/src/main/java/com/foxx/androidcast/settings/AppSettingsJson.java b/app/src/main/java/com/foxx/androidcast/settings/AppSettingsJson.java index 1886171..f9b1fca 100644 --- a/app/src/main/java/com/foxx/androidcast/settings/AppSettingsJson.java +++ b/app/src/main/java/com/foxx/androidcast/settings/AppSettingsJson.java @@ -15,6 +15,8 @@ import android.content.Context; import org.json.JSONObject; +import com.foxx.androidcast.util.IoUtils; + import java.io.File; import java.io.FileInputStream; import java.nio.charset.StandardCharsets; @@ -31,8 +33,8 @@ public final class AppSettingsJson { return new JSONObject(); } try (FileInputStream fis = new FileInputStream(f)) { - return new JSONObject(new String(fis.readAllBytes(), StandardCharsets.UTF_8)); - } catch (Exception ignored) { + return new JSONObject(new String(IoUtils.readAllBytes(fis), StandardCharsets.UTF_8)); + } catch (Throwable ignored) { return new JSONObject(); } } diff --git a/app/src/main/java/com/foxx/androidcast/util/IoUtils.java b/app/src/main/java/com/foxx/androidcast/util/IoUtils.java new file mode 100644 index 0000000..734d1d7 --- /dev/null +++ b/app/src/main/java/com/foxx/androidcast/util/IoUtils.java @@ -0,0 +1,36 @@ +package com.foxx.androidcast.util; + +/********************************************************************* + * IoUtils.java + * Created at: Wed 20 May 2026 12:00:00 +0200 + * Contributors: + * - Cursor Agent (project assistant) + **********************************************************************/ +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** Stream/file reads compatible with minSdk 29 (no {@code InputStream.readAllBytes()}). */ +public final class IoUtils { + private static final int BUF_SIZE = 8192; + + private IoUtils() {} + + public static byte[] readAllBytes(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[BUF_SIZE]; + int n; + while ((n = in.read(buf)) != -1) { + out.write(buf, 0, n); + } + return out.toByteArray(); + } + + public static byte[] readAllBytes(File file) throws IOException { + try (FileInputStream fis = new FileInputStream(file)) { + return readAllBytes(fis); + } + } +} diff --git a/pull_stats.sh b/pull_stats.sh index b81632e..87a37d6 100755 --- a/pull_stats.sh +++ b/pull_stats.sh @@ -5,11 +5,37 @@ DEVICES_LIST="$(adb devices -l)" echo "${DEVICES_LIST}" LOG_ROOT="log_capture/session_stats/" +CRASH_ROOT="log_capture/crash_reports/" CLEAN_LOGS="no" +pull_dir() { + local serial="$1" + local remote_subdir="$2" + local local_base="$3" + local local_path="${local_base}/${serial}/${remote_subdir}/" + + mapfile -t FILES < <(adb -s "${serial}" shell run-as com.foxx.androidcast ls "files/${remote_subdir}/" "${local_path}/${f}" + if [[ "${CLEAN_LOGS}" == "yes" ]]; then + adb -s "${serial}" shell run-as com.foxx.androidcast rm -f "files/${remote_subdir}/${f}" ${local_path}" +} + while true; do yn="" - read -rp "clean logs on device after fetch? [Y/n] " yn + read -rp "clean logs on device after fetch? [y/N] " yn yn="${yn,,}" case "${yn}" in y|yes) @@ -20,7 +46,7 @@ while true; do break ;; "") - CLEAN_LOGS="yes" + CLEAN_LOGS="no" break ;; *) @@ -40,29 +66,20 @@ while IFS= read -r DEV; do STATE="$(awk '{print $2}' <<<"${DEV}")" [[ "${STATE}" != "device" ]] && continue - LOG_PATH="${LOG_ROOT}/${SERIAL}/" - echo "------------ dumping in ${LOG_PATH}" - mkdir -p "${LOG_PATH}" - - mapfile -t FILES < <(adb -s "${SERIAL}" shell run-as com.foxx.androidcast ls files/session_stats/ "${LOG_PATH}/${f}" - if [[ "${CLEAN_LOGS}" == "yes" ]]; then - adb -s "${SERIAL}" shell run-as com.foxx.androidcast rm -f "files/session_stats/${f}" /dev/null | sort || true +echo "" +echo "Crash reports: ${CRASH_ROOT}" +[[ -d "${CRASH_ROOT}" ]] && find "${CRASH_ROOT}" -type f 2>/dev/null | sort || true +echo "" +echo "run './gradlew :session-studio:run' to analyze session logs"