docs: add Claiming an issue section to CONTRIBUTING.md #24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: BAOSP Full Build | ||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| build_target: | ||
| description: 'AOSP build target (lunch target)' | ||
| required: false | ||
| default: 'aosp_x86_64-userdebug' | ||
| aosp_branch: | ||
| description: 'AOSP branch to build' | ||
| required: false | ||
| default: 'android-14.0.0_r1' | ||
| jobs: | ||
| # ────────────────────────────────────────────── | ||
| # Job 1: Build andrdscren Screen Reader APK | ||
| # Always pulls the latest commit from main. | ||
| # ────────────────────────────────────────────── | ||
| build-screenreader: | ||
| name: Build Screen Reader (andrdscren) | ||
| runs-on: ubuntu-22.04 | ||
| steps: | ||
| - name: Checkout andrdscren — latest main | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| repository: tech-master33/andrdscren | ||
| ref: main | ||
| fetch-depth: 0 | ||
| path: andrdscren | ||
| - name: Print latest commit being built | ||
| run: | | ||
| cd andrdscren | ||
| echo "Building andrdscren commit:" | ||
| git log -1 --format="%H %s (%ci)" | ||
| - name: Set up Java 17 | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| java-version: '17' | ||
| distribution: 'temurin' | ||
| - name: Set up Android SDK | ||
| uses: android-actions/setup-android@v3 | ||
| - name: Grant Gradle execute permission | ||
| run: chmod +x andrdscren/gradlew | ||
| - name: Build screen reader APK | ||
| working-directory: andrdscren | ||
| run: | | ||
| ./gradlew clean assembleDebug assembleRelease \ | ||
| --no-daemon \ | ||
| --stacktrace || ./gradlew clean assembleDebug --no-daemon --stacktrace | ||
| - name: Collect screen reader APK | ||
| run: | | ||
| mkdir -p /tmp/screenreader-output | ||
| RELEASE=$(find andrdscren/app/build/outputs/apk/release -name "*.apk" 2>/dev/null | head -1) | ||
| DEBUG=$(find andrdscren/app/build/outputs/apk/debug -name "*.apk" 2>/dev/null | head -1) | ||
| if [ -n "$RELEASE" ]; then | ||
| cp "$RELEASE" /tmp/screenreader-output/andrdscren.apk | ||
| echo "Using release APK: $RELEASE" | ||
| elif [ -n "$DEBUG" ]; then | ||
| cp "$DEBUG" /tmp/screenreader-output/andrdscren.apk | ||
| echo "Using debug APK: $DEBUG" | ||
| else | ||
| echo "ERROR: No APK found" | ||
| exit 1 | ||
| fi | ||
| ls -lh /tmp/screenreader-output/ | ||
| - name: Upload screen reader APK | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: andrdscren-apk | ||
| path: /tmp/screenreader-output/andrdscren.apk | ||
| retention-days: 30 | ||
| if-no-files-found: error | ||
| # ────────────────────────────────────────────── | ||
| # Job 2: Build AOTTS TTS Engine APK | ||
| # Always pulls the latest commit from main. | ||
| # ────────────────────────────────────────────── | ||
| build-aotts: | ||
| name: Build AOTTS TTS Engine | ||
| runs-on: ubuntu-22.04 | ||
| steps: | ||
| - name: Checkout AOTTS — latest main | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| repository: tech-master33/aotts | ||
| ref: main | ||
| fetch-depth: 0 | ||
| path: aotts | ||
| - name: Print latest commit being built | ||
| run: | | ||
| cd aotts | ||
| echo "Building aotts commit:" | ||
| git log -1 --format="%H %s (%ci)" | ||
| - name: Set up Java 17 | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| java-version: '17' | ||
| distribution: 'temurin' | ||
| - name: Set up Android SDK | ||
| uses: android-actions/setup-android@v3 | ||
| - name: Install Android NDK | ||
| run: | | ||
| echo "y" | sdkmanager "ndk;23.1.7779620" "cmake;3.22.1" | ||
| echo "NDK_HOME=$ANDROID_SDK_ROOT/ndk/23.1.7779620" >> $GITHUB_ENV | ||
| - name: Clone SVOX Pico native source | ||
| run: | | ||
| mkdir -p aotts/src/main/cpp/pico | ||
| git clone --depth=1 \ | ||
| https://android.googlesource.com/platform/external/svox \ | ||
| aotts/src/main/cpp/pico_src || true | ||
| if [ -d aotts/src/main/cpp/pico_src/pico ]; then | ||
| cp -r aotts/src/main/cpp/pico_src/pico/* aotts/src/main/cpp/pico/ | ||
| fi | ||
| rm -rf aotts/src/main/cpp/pico_src | ||
| - name: Download SVOX Pico language data files | ||
| run: | | ||
| mkdir -p aotts/src/main/assets/lang | ||
| SVOX_RAW="https://android.googlesource.com/platform/external/svox/+archive/refs/heads/master/pico/lang.tar.gz" | ||
| echo "Fetching SVOX Pico language data from AOSP..." | ||
| curl -L "$SVOX_RAW" -o /tmp/pico_lang.tar.gz 2>/dev/null || true | ||
| if [ -f /tmp/pico_lang.tar.gz ] && [ -s /tmp/pico_lang.tar.gz ]; then | ||
| tar -xzf /tmp/pico_lang.tar.gz -C aotts/src/main/assets/lang/ 2>/dev/null || true | ||
| echo "Extracted language data from AOSP tarball" | ||
| fi | ||
| # Fallback: sparse clone just the lang folder | ||
| if [ -z "$(ls -A aotts/src/main/assets/lang/*.bin 2>/dev/null)" ]; then | ||
| echo "Tarball failed, trying sparse clone..." | ||
| git clone --depth=1 --filter=blob:none --sparse \ | ||
| https://android.googlesource.com/platform/external/svox \ | ||
| /tmp/svox_clone || true | ||
| if [ -d /tmp/svox_clone ]; then | ||
| git -C /tmp/svox_clone sparse-checkout set pico/lang | ||
| git -C /tmp/svox_clone checkout || true | ||
| find /tmp/svox_clone/pico/lang -name "*.bin" \ | ||
| -exec cp {} aotts/src/main/assets/lang/ \; 2>/dev/null || true | ||
| fi | ||
| fi | ||
| echo "Language files:" | ||
| ls -lh aotts/src/main/assets/lang/ || echo "(none — build continues without them)" | ||
| - name: Grant Gradle execute permission | ||
| run: chmod +x aotts/gradlew | ||
| - name: Build AOTTS APK | ||
| working-directory: aotts | ||
| run: | | ||
| ./gradlew clean assembleDebug assembleRelease \ | ||
| -Pandroid.ndkVersion=23.1.7779620 \ | ||
| --no-daemon \ | ||
| --stacktrace || \ | ||
| ./gradlew clean assembleDebug \ | ||
| -Pandroid.ndkVersion=23.1.7779620 \ | ||
| --no-daemon \ | ||
| --stacktrace | ||
| - name: Collect AOTTS APK | ||
| run: | | ||
| mkdir -p /tmp/aotts-output | ||
| RELEASE=$(find aotts/build/outputs/apk/release -name "*.apk" 2>/dev/null | head -1) | ||
| DEBUG=$(find aotts/build/outputs/apk/debug -name "*.apk" 2>/dev/null | head -1) | ||
| if [ -n "$RELEASE" ]; then | ||
| cp "$RELEASE" /tmp/aotts-output/aotts.apk | ||
| echo "Using release APK" | ||
| elif [ -n "$DEBUG" ]; then | ||
| cp "$DEBUG" /tmp/aotts-output/aotts.apk | ||
| echo "Using debug APK" | ||
| else | ||
| echo "ERROR: No APK found" | ||
| exit 1 | ||
| fi | ||
| ls -lh /tmp/aotts-output/ | ||
| - name: Upload AOTTS APK | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: aotts-apk | ||
| path: /tmp/aotts-output/aotts.apk | ||
| retention-days: 30 | ||
| if-no-files-found: error | ||
| # ────────────────────────────────────────────── | ||
| # Job 3: Build full AOSP system image | ||
| # Waits for both APKs, re-signs them with the | ||
| # AOSP platform key, drops them in as privileged | ||
| # system apps, and builds the system image. | ||
| # ────────────────────────────────────────────── | ||
| build-aosp: | ||
| name: Build AOSP System Image | ||
| runs-on: ubuntu-22.04 | ||
| needs: [build-screenreader, build-aotts] | ||
| steps: | ||
| - name: Checkout BAOSP repo | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| path: baosp | ||
| - name: Free up disk space | ||
| run: | | ||
| sudo apt-get remove -y '^dotnet-.*' '^llvm-.*' 'php.*' \ | ||
| azure-cli google-cloud-sdk google-chrome-stable firefox \ | ||
| powershell mono-devel snapd || true | ||
| sudo apt-get autoremove -y | ||
| sudo apt-get clean | ||
| sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc \ | ||
| /usr/local/share/boost /opt/hostedtoolcache/CodeQL | ||
| df -h | ||
| - name: Install AOSP build dependencies | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y \ | ||
| openjdk-11-jdk \ | ||
| git-core gnupg flex bison \ | ||
| build-essential zip curl zlib1g-dev \ | ||
| gcc-multilib g++-multilib libc6-dev-i386 \ | ||
| lib32ncurses5-dev x11proto-core-dev libx11-dev \ | ||
| lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip \ | ||
| libncurses5 libncurses5-dev bc rsync wget python3 \ | ||
| python3-pip repo | ||
| - name: Install repo tool (if not from apt) | ||
| run: | | ||
| if ! command -v repo &>/dev/null; then | ||
| mkdir -p ~/.bin | ||
| curl https://storage.googleapis.com/git-repo-downloads/repo > ~/.bin/repo | ||
| chmod a+x ~/.bin/repo | ||
| echo "$HOME/.bin" >> $GITHUB_PATH | ||
| fi | ||
| repo version | ||
| - name: Configure Git for repo | ||
| run: | | ||
| git config --global user.email "baosp-builder@github-actions.local" | ||
| git config --global user.name "BAOSP Builder" | ||
| git config --global color.ui false | ||
| - name: Cache AOSP source | ||
| id: aosp-cache | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: aosp-source | ||
| key: aosp-source-${{ github.event.inputs.aosp_branch || 'android-14.0.0_r1' }} | ||
| restore-keys: | | ||
| aosp-source- | ||
| - name: Initialize AOSP repo | ||
| if: steps.aosp-cache.outputs.cache-hit != 'true' | ||
| run: | | ||
| mkdir -p aosp-source | ||
| cd aosp-source | ||
| BRANCH="${{ github.event.inputs.aosp_branch || 'android-14.0.0_r1' }}" | ||
| repo init \ | ||
| -u https://android.googlesource.com/platform/manifest \ | ||
| -b "$BRANCH" \ | ||
| --depth=1 \ | ||
| --no-clone-bundle | ||
| echo "Initialized AOSP repo for branch: $BRANCH" | ||
| - name: Sync AOSP source | ||
| run: | | ||
| cd aosp-source | ||
| repo sync \ | ||
| -c \ | ||
| -j$(nproc) \ | ||
| --no-tags \ | ||
| --no-clone-bundle \ | ||
| --fail-fast \ | ||
| --force-sync | ||
| echo "AOSP sync complete" | ||
| df -h | ||
| - name: Download screen reader APK | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: andrdscren-apk | ||
| path: /tmp/screenreader-output | ||
| - name: Download AOTTS APK | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: aotts-apk | ||
| path: /tmp/aotts-output | ||
| - name: Re-sign APKs with AOSP platform key | ||
| run: | | ||
| # The AOSP platform key lives in build/target/product/security/ | ||
| # We use signapk.jar (built as part of AOSP host tools) to strip | ||
| # the existing debug/dev signature and replace it with the platform | ||
| # key so both apps run as system-signed privileged applications. | ||
| PLATFORM_PK8="aosp-source/build/target/product/security/platform.pk8" | ||
| PLATFORM_CERT="aosp-source/build/target/product/security/platform.x509.pem" | ||
| if [ ! -f "$PLATFORM_PK8" ] || [ ! -f "$PLATFORM_CERT" ]; then | ||
| echo "Platform keys not found — building signapk host tool first..." | ||
| cd aosp-source | ||
| source build/envsetup.sh | ||
| TARGET="${{ github.event.inputs.build_target || 'aosp_x86_64-userdebug' }}" | ||
| lunch "$TARGET" | ||
| make -j$(nproc) signapk 2>&1 | tail -20 | ||
| cd .. | ||
| fi | ||
| SIGNAPK_JAR=$(find aosp-source/out/host -name "signapk.jar" 2>/dev/null | head -1) | ||
| if [ -z "$SIGNAPK_JAR" ]; then | ||
| echo "WARNING: signapk.jar not found — falling back to zipalign+apksigner" | ||
| # Use Android build tools apksigner as fallback | ||
| APKSIGNER=$(find $ANDROID_SDK_ROOT/build-tools -name "apksigner" 2>/dev/null | sort -V | tail -1) | ||
| if [ -n "$APKSIGNER" ]; then | ||
| echo "Using apksigner at: $APKSIGNER" | ||
| # Convert PK8 to PKCS12 for apksigner | ||
| openssl pkcs8 -in "$PLATFORM_PK8" -inform DER -nocrypt \ | ||
| -out /tmp/platform.pem 2>/dev/null || true | ||
| if [ -f /tmp/platform.pem ]; then | ||
| for APK in /tmp/screenreader-output/andrdscren.apk /tmp/aotts-output/aotts.apk; do | ||
| [ -f "$APK" ] || continue | ||
| echo "Signing $APK with platform key..." | ||
| "$APKSIGNER" sign \ | ||
| --key /tmp/platform.pem \ | ||
| --cert "$PLATFORM_CERT" \ | ||
| "$APK" && echo "Signed: $APK" || echo "WARNING: Could not sign $APK" | ||
| done | ||
| fi | ||
| else | ||
| echo "WARNING: No signing tool available — APKs will be re-signed by Soong via Android.bp certificate field" | ||
| fi | ||
| else | ||
| echo "Using signapk at: $SIGNAPK_JAR" | ||
| for APK in /tmp/screenreader-output/andrdscren.apk /tmp/aotts-output/aotts.apk; do | ||
| [ -f "$APK" ] || continue | ||
| SIGNED="${APK%.apk}-platform-signed.apk" | ||
| echo "Signing $APK with AOSP platform key..." | ||
| java -jar "$SIGNAPK_JAR" \ | ||
| "$PLATFORM_CERT" "$PLATFORM_PK8" \ | ||
| "$APK" "$SIGNED" && \ | ||
| mv "$SIGNED" "$APK" && \ | ||
| echo "Platform-signed: $APK" || \ | ||
| echo "WARNING: signapk failed for $APK — Soong will re-sign via Android.bp" | ||
| done | ||
| fi | ||
| - name: Place screen reader into AOSP tree (privileged) | ||
| run: | | ||
| APK="/tmp/screenreader-output/andrdscren.apk" | ||
| if [ ! -f "$APK" ]; then | ||
| echo "WARNING: Screen reader APK not found — skipping" | ||
| else | ||
| DEST="aosp-source/packages/apps/Andrdscren" | ||
| mkdir -p "$DEST" | ||
| cp "$APK" "$DEST/Andrdscren.apk" | ||
| # certificate: "platform" → Soong re-signs with platform key if not already done | ||
| # privileged: true → installs to /system/priv-app for elevated permissions | ||
| # (required for AccessibilityService to work reliably) | ||
| cat > "$DEST/Android.bp" << 'EOF' | ||
| android_app_import { | ||
| name: "Andrdscren", | ||
| apk: "Andrdscren.apk", | ||
| certificate: "platform", | ||
| privileged: true, | ||
| dex_preopt: { | ||
| enabled: false, | ||
| }, | ||
| } | ||
| EOF | ||
| echo "Screen reader placed at $DEST (privileged, platform-signed)" | ||
| ls -lh "$DEST" | ||
| fi | ||
| - name: Place AOTTS into AOSP tree (privileged) | ||
| run: | | ||
| APK="/tmp/aotts-output/aotts.apk" | ||
| if [ ! -f "$APK" ]; then | ||
| echo "WARNING: AOTTS APK not found — skipping" | ||
| else | ||
| DEST="aosp-source/packages/apps/AoTTS" | ||
| mkdir -p "$DEST" | ||
| cp "$APK" "$DEST/AoTTS.apk" | ||
| # TTS engines also benefit from platform signing to register | ||
| # as a trusted system TTS provider without user confirmation. | ||
| cat > "$DEST/Android.bp" << 'EOF' | ||
| android_app_import { | ||
| name: "AoTTS", | ||
| apk: "AoTTS.apk", | ||
| certificate: "platform", | ||
| privileged: true, | ||
| dex_preopt: { | ||
| enabled: false, | ||
| }, | ||
| } | ||
| EOF | ||
| echo "AOTTS placed at $DEST (privileged, platform-signed)" | ||
| ls -lh "$DEST" | ||
| fi | ||
| - name: Apply BAOSP accessibility patches | ||
| run: | | ||
| cd aosp-source | ||
| PATCH_DIR="../baosp/patches" | ||
| if [ -d "$PATCH_DIR" ] && ls "$PATCH_DIR"/*.patch &>/dev/null; then | ||
| for patch in "$PATCH_DIR"/*.patch; do | ||
| echo "Applying patch: $patch" | ||
| patch -p1 < "$patch" || echo "WARNING: $patch failed to apply — skipping" | ||
| done | ||
| else | ||
| echo "No patches found — skipping" | ||
| fi | ||
| - name: Copy BAOSP device overlays | ||
| run: | | ||
| if [ -d baosp/device ]; then | ||
| cp -r baosp/device/* aosp-source/device/ | ||
| echo "Copied BAOSP device overlays" | ||
| else | ||
| echo "No device overlays — skipping" | ||
| fi | ||
| - name: Enable screen reader and TTS as system defaults | ||
| run: | | ||
| # Patches SettingsProvider's default values so that on first boot | ||
| # the screen reader is already on and AOTTS is the default TTS | ||
| # engine — no manual setup needed after flashing. | ||
| OVERLAY_DIR="aosp-source/frameworks/base/packages/SettingsProvider/res/values" | ||
| mkdir -p "$OVERLAY_DIR" | ||
| cat > "$OVERLAY_DIR/baosp_accessibility_defaults.xml" << 'EOF' | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <resources> | ||
| <!-- Enable Android accessibility framework on first boot --> | ||
| <integer name="def_accessibility_enabled">1</integer> | ||
| <!-- Enable the BAOSP screen reader service by default --> | ||
| <string name="def_enabled_accessibility_services" translatable="false" | ||
| >com.example.andrd/com.example.andrd.ScreenReaderService</string> | ||
| <!-- Set AOTTS (SVOX Pico) as the default TTS engine --> | ||
| <string name="def_tts_default_synth" translatable="false" | ||
| >com.example.picottsengine</string> | ||
| <!-- Default TTS speech rate and pitch (100 = normal) --> | ||
| <integer name="def_tts_default_rate">100</integer> | ||
| <integer name="def_tts_default_pitch">100</integer> | ||
| <!-- Enable touch exploration (required for screen reader gestures) --> | ||
| <integer name="def_touch_exploration_enabled">1</integer> | ||
| </resources> | ||
| EOF | ||
| echo "Accessibility defaults written" | ||
| # Belt-and-suspenders: also set via build.prop fragment | ||
| cat > "aosp-source/device/baosp_accessibility.prop" << 'EOF' | ||
| # BAOSP: accessibility defaults | ||
| ro.product.accessibility.screenreader=com.example.andrd/com.example.andrd.ScreenReaderService | ||
| ro.product.tts.default_engine=com.example.picottsengine | ||
| EOF | ||
| echo "Build prop fragment written" | ||
| - name: Build AOSP system image | ||
| run: | | ||
| cd aosp-source | ||
| TARGET="${{ github.event.inputs.build_target || 'aosp_x86_64-userdebug' }}" | ||
| source build/envsetup.sh | ||
| lunch "$TARGET" | ||
| echo "Building target: $TARGET with $(($(nproc) - 1)) jobs" | ||
| df -h | ||
| m -j$(($(nproc) - 1)) systemimage 2>&1 | tee build.log | ||
| echo "Build complete" | ||
| df -h | ||
| - name: Collect build artifacts | ||
| if: always() | ||
| run: | | ||
| DEVICE=$(cd aosp-source && source build/envsetup.sh 2>/dev/null && \ | ||
| get_build_var TARGET_DEVICE 2>/dev/null || echo "generic_x86_64") | ||
| OUT="aosp-source/out/target/product/$DEVICE" | ||
| mkdir -p /tmp/baosp-images | ||
| for img in system.img boot.img recovery.img userdata.img ramdisk.img; do | ||
| if [ -f "$OUT/$img" ]; then | ||
| cp "$OUT/$img" /tmp/baosp-images/ | ||
| echo "Collected $img ($(du -sh "$OUT/$img" | cut -f1))" | ||
| fi | ||
| done | ||
| [ -f aosp-source/build.log ] && cp aosp-source/build.log /tmp/baosp-images/ | ||
| ls -lh /tmp/baosp-images/ | ||
| - name: Upload BAOSP system images | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: baosp-system-images | ||
| path: /tmp/baosp-images/ | ||
| retention-days: 30 | ||
| if-no-files-found: warn | ||
| - name: Upload build log on failure | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: build-log | ||
| path: aosp-source/build.log | ||
| retention-days: 7 | ||
| if-no-files-found: ignore | ||