Files
rr3-ota/README.md
ssfdre38 0daa89e935 Fix critical version code formula bug
Changed formula from (major*100000 + minor*1000 + patch) to the correct
(major*10000 + minor*100 + patch) to match UpdateManager.smali implementation.

Updated all examples:
- 14.5.0 = 140500 (0x224D4) not 145000
- Corrected hex values throughout documentation

This bug would have prevented OTA updates from working correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-24 22:32:41 -08:00

11 KiB

🔄 RR3 OTA Update System

Standalone OTA (Over-The-Air) update system for Real Racing 3 Community Edition

This package provides a complete, drop-in OTA update system that can be integrated into any RR3 APK version (EA's official or community editions).


📦 What's Included

rr3-ota/
├── UpdateManager.java              (Main update logic - manifest-based)
├── UpdateManager.smali             (Compiled smali version)
├── community_update_checker.html   (WebView UI for updates)
├── README.md                       (This file)
└── INTEGRATION-GUIDE.md            (Step-by-step integration)

Features

  • 🔄 Manifest-based updates - Full control over upgrade paths
  • 📡 GitHub integration - Uses GitHub Releases for hosting
  • 🌐 Network flexibility - WiFi or mobile data support
  • 📊 Progress tracking - Real-time download progress
  • 🎨 Material Design UI - Beautiful WebView interface
  • ⚙️ User preferences - WiFi-only mode, auto-check settings
  • 🔒 Version control - Prevents unwanted major version jumps
  • 📱 Android 6.0+ - Compatible with API 23+

🎯 How It Works

Manifest System

Instead of relying on GitHub's /releases/latest API, this system uses a version manifest (versions.json) that you control:

{
  "versions": [
    {
      "version": "15.1.0",
      "version_code": 151000,
      "download_url": "https://github.com/.../RR3-v15.1.0.apk",
      "upgrade_from": ["15.0.0", "15.0.1"]
    }
  ]
}

Benefits:

  • You control which versions get updates
  • Users on v14.x won't be forced to v15.x
  • Can create multiple update channels (stable, beta, legacy)
  • Easy to manage (edit single JSON file)

Update Flow

1. User opens app
   ↓
2. UpdateManager fetches versions.json
   ↓
3. Finds applicable update (checks upgrade_from)
   ↓
4. Shows update dialog with changelog
   ↓
5. User clicks Download
   ↓
6. DownloadManager downloads APK
   ↓
7. Installation prompt (preserves user data)
   ↓
8. Updated!

🚀 Quick Integration

For RR3 Community Edition (Already Integrated)

If you're building RR3 Community Edition from our repository:

  • UpdateManager is already patched in
  • HTML UI already in assets
  • Just build the APK

For Other RR3 Versions (Integration Required)

Follow the detailed guide: INTEGRATION-GUIDE.md

Quick steps:

  1. Decompile your RR3 APK with apktool
  2. Add UpdateManager.smali to smali/com/community/
  3. Add community_update_checker.html to assets/
  4. Add "Check for Updates" button to your UI
  5. Recompile and sign APK

📝 Configuration

Update Version Constants

Edit UpdateManager.java (or .smali) before building:

// IMPORTANT: Update these for each build
private static final String CURRENT_VERSION = "15.0.0-community-alpha";
private static final int CURRENT_VERSION_CODE = 150000;

Version Code Formula:

versionCode = (major * 10000) + (minor * 100) + patch

Examples:
15.0.0 → 150000
15.1.0 → 151000
14.5.2 → 145200

Manifest URL

The manifest URL is hardcoded in UpdateManager:

private static final String UPDATE_API_URL = 
    "https://raw.githubusercontent.com/YOUR-ORG/YOUR-REPO/main/versions.json";

Change this to your own manifest location!


📊 Version Manifest Setup

1. Create versions.json

Create a versions.json file in your releases repository:

{
  "schema_version": 1,
  "last_updated": "2026-02-24T04:00:00Z",
  "channels": {
    "stable": {
      "description": "Stable releases",
      "latest": "15.0.0"
    }
  },
  "versions": [
    {
      "version": "15.0.0",
      "version_code": 150000,
      "channel": "stable",
      "release_date": "2026-02-24",
      "min_android": 23,
      "target_android": 34,
      "download_url": "https://github.com/YOUR-ORG/releases/download/v15.0.0/RR3-v15.0.0.apk",
      "file_size": 240000000,
      "sha256": "abc123...",
      "changelog": "## Initial Release\n- Feature 1\n- Feature 2",
      "upgrade_from": []
    }
  ]
}

2. Host on GitHub

Upload versions.json to your GitHub repository (main branch, root or any folder).

Access URL:

https://raw.githubusercontent.com/YOUR-ORG/YOUR-REPO/main/versions.json

3. Add New Versions

When releasing a new version:

  1. Create GitHub Release (hosts APK file)
  2. Update versions.json:
    {
      "version": "15.1.0",
      "version_code": 151000,
      "download_url": "https://github.com/.../v15.1.0/RR3-v15.1.0.apk",
      "upgrade_from": ["15.0.0"],
      ...
    }
    
  3. Update channel latest:
    "channels": {
      "stable": { "latest": "15.1.0" }
    }
    
  4. Commit and push versions.json

Users will see the update within seconds!


🎨 UI Integration

Add Update Button

The easiest way to integrate the UI is to add a button that opens the update checker:

Example (in your HTML UI):

<button onclick="showUpdateChecker()">🔄 Check for Updates</button>

<script>
function showUpdateChecker() {
    window.location.href = 'file:///android_asset/community_update_checker.html';
}
</script>

WebView Bridge Setup

The UpdateManager must be added as a JavaScript interface to your WebView:

webView.addJavascriptInterface(new UpdateManager(context), "UpdateManager");

In smali:

new-instance v0, Lcom/community/UpdateManager;
move-object v1, p0  # context
invoke-direct {v0, v1}, Lcom/community/UpdateManager;-><init>(Landroid/content/Context;)V

const-string v1, "UpdateManager"
invoke-virtual {webView, v0, v1}, Landroid/webkit/WebView;->addJavascriptInterface(Ljava/lang/Object;Ljava/lang/String;)V

🧪 Testing

Test Manifest Parsing

# Fetch your manifest
curl https://raw.githubusercontent.com/YOUR-ORG/YOUR-REPO/main/versions.json

# Check structure
jq . versions.json

# Verify accessible
curl -I https://raw.githubusercontent.com/YOUR-ORG/YOUR-REPO/main/versions.json

Test Update Flow

  1. Set current version lower (e.g., 15.0.0)
  2. Add test version to manifest (e.g., 15.0.1)
  3. Set upgrade_from to current version
  4. Open app and check for updates
  5. Verify update is offered

Test Download

  1. Create actual GitHub release with APK
  2. Update manifest download_url
  3. Test download (WiFi only, then mobile)
  4. Verify progress tracking works
  5. Test installation

🔧 Customization

Change Manifest URL

Edit UpdateManager.java line ~23:

private static final String UPDATE_API_URL = "YOUR_MANIFEST_URL";

Modify UI

Edit community_update_checker.html:

  • Change colors (CSS variables at top)
  • Modify layout
  • Add custom branding
  • Change button text

Add Update Channels

In your manifest:

"channels": {
  "stable": { "latest": "15.0.0" },
  "beta": { "latest": "15.1.0-beta" },
  "legacy-v14": { "latest": "14.5.2" }
}

Then add channel selection in your UI.

Network Preferences

Users can toggle WiFi-only mode:

UpdateManager.setWifiOnlyPreference(true);  // WiFi only
UpdateManager.setWifiOnlyPreference(false); // WiFi + Mobile

📱 Android Permissions

Add to AndroidManifest.xml:

<!-- Network access for checking updates -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<!-- Download updates -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" 
    android:maxSdkVersion="32" />

<!-- Install updates -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

🔒 Security

APK Verification

Recommended: Verify SHA-256 checksum after download

// Add to UpdateManager
public boolean verifyDownload(String expectedSha256) {
    // Calculate SHA-256 of downloaded APK
    // Compare with manifest value
    // Return true if match
}

HTTPS Only

  • Manifest URL uses HTTPS (raw.githubusercontent.com)
  • Download URLs use HTTPS (github.com)
  • No insecure connections

Code Signing

Always sign your APK with the same keystore for update continuity.


📂 File Structure

After Integration

your-rr3-apk/
├── smali/
│   └── com/
│       └── community/
│           └── UpdateManager.smali        ← Add this
├── assets/
│   └── community_update_checker.html      ← Add this
└── AndroidManifest.xml                    ← Add permissions

Your GitHub Release Repo

your-releases-repo/
├── versions.json                          ← Version manifest
└── README.md                              ← Usage instructions

FAQ

Q: Does this work with EA's official RR3?

A: Technically yes, but EA may ban your account for modified APKs. This is designed for community servers where EA's servers are shut down.

Q: Can users downgrade versions?

A: No. The system only offers updates to versions with higher version codes.

Q: What if GitHub goes down?

A: Update checks will fail gracefully. Users can still play, just won't get updates until GitHub is back.

Q: Can I host the manifest elsewhere?

A: Yes! Just change the UPDATE_API_URL. Any HTTPS endpoint that returns the JSON works.

Q: How do I prevent v14 users from updating to v15?

A: In your v15 manifest entry, don't include v14.x in upgrade_from:

"upgrade_from": ["15.0.0", "15.0.1"]  // No 14.x!

Q: Can I have multiple APK variants (arm64, x86)?

A: Not currently, but you can extend the manifest:

"variants": [
  {"arch": "arm64-v8a", "url": "...-arm64.apk"},
  {"arch": "armeabi-v7a", "url": "...-arm.apk"}
]

Then update UpdateManager to select based on device architecture.


🤝 Contributing

Found a bug? Have an improvement?

  1. Test your changes thoroughly
  2. Update documentation
  3. Submit with clear description

📜 License

This OTA system is part of the RR3 Community Edition project.

License: MIT License

Credits:

  • Project Lead: @ssfdre38 (Daniel Elliott)
  • AI Assistant: GitHub Copilot CLI


🆘 Support

Need help?

  • 🐛 Report issues on GitHub
  • 💬 Join community Discord (link TBA)
  • 📧 Contact: @ssfdre38

🔄 Keep your RR3 Community Edition up to date! 🏁

Made with ❤️ by the RR3 Community