Skip to content

feat: migrate legacy native modules to Nitro Modules#22

Open
huhuanming wants to merge 86 commits intomainfrom
feat/native-logger
Open

feat: migrate legacy native modules to Nitro Modules#22
huhuanming wants to merge 86 commits intomainfrom
feat/native-logger

Conversation

@huhuanming
Copy link
Contributor

@huhuanming huhuanming commented Feb 26, 2026

Summary

Migrate all legacy React Native Bridge native modules to Nitro Modules for New Architecture compatibility.

New Modules

  • react-native-bundle-update — JS bundle OTA update with SHA256 verification, ZIP extraction, PGP signature validation, download progress events, and fallback management
  • react-native-app-update — Android APK download/install with notification progress (iOS no-op stub)
  • react-native-perf-memory — Memory usage reporting (RSS) for iOS and Android
  • react-native-splash-screen — Legacy splash screen management, only effective on Android < 12 (iOS no-op stub)
  • react-native-webview-checker — Android WebView package info and Google Play Services availability check (iOS no-op stub)

Extended Modules

  • react-native-device-utils — Added getLaunchOptions(), exitApp(), and startup time tracking (merged from LaunchOptionsManager + ExitModule)

Key Changes

  • All modules use OneKeyLog (native-logger) instead of android.util.Log / NSLog
  • Events use Nitro callback listener pattern (addXxxListener → listener ID) replacing NativeEventEmitter
  • BundleUpdateStore (iOS) / BundleUpdateStoreAndroid (Android) provide static access for getJSBundleFile() before JS engine starts
  • SharedPreferences / UserDefaults keys maintained for seamless migration
  • HTTPS-only enforcement on OkHttpClient for bundle/APK downloads
  • Zip-slip protection on all ZIP extraction operations

Test Plan

  • Verify nitrogen codegen runs successfully for all 6 modules
  • iOS build succeeds with all new pods
  • Android build succeeds with all new Gradle modules
  • Bundle download + SHA256 verification + install flow works
  • APK download with progress notification works (Android)
  • Memory usage reporting returns valid RSS values
  • Splash screen hides correctly on Android < 12
  • WebView checker returns correct package info (Android)
  • Launch options and exit app work via device-utils
  • Fallback bundle data persistence works across app restarts

Open with Devin

@huhuanming huhuanming force-pushed the feat/native-logger branch 3 times, most recently from b985df0 to 13c4dfd Compare February 26, 2026 18:27
huhuanming and others added 14 commits February 28, 2026 23:30
Unified native logging module with static OneKeyLog API that self-initializes
on first call, replacing react-native-file-logger's JS-configured approach.

- iOS: CocoaLumberjack backend with custom log file naming (app- prefix) and message-only formatter
- Android: SLF4J + logback-android backend
- Nitro HybridObject bridge for JS layer (write, getLogFilePaths, deleteLogFiles)
- Consistent log file naming across platforms (app- prefix + date)
- Same log config as existing: 20MB max, 3 files, daily rolling, {CachesDir}/logs

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Remove unnecessary CocoaLumberjack import from iOS NativeLogger.swift
- Add else branch in Android NativeLogger.kt write() for unknown levels
- Use detachAndStopAllAppenders() in Android OneKeyLog to prevent duplicate logcat
- Unify iOS logsDirectory path to use computed property (remove duplication)
- Add sortedBy to Android getLogFilePaths() for consistent ordering with iOS

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Add file-based logging via OneKeyLog to 5 native modules:
- device-utils: UI style changes, foldable detection, spanning state
- keychain-module: keychain operations success/failure with OSStatus
- cloud-kit-module: CloudKit CRUD operations and query results
- check-biometric-auth-changed: biometric state change detection
- get-random-values: error-only logging for SecRandomCopyBytes/SecureRandom

Each module declares a dependency on ReactNativeNativeLogger (iOS podspec)
and compileOnly project dependency (Android build.gradle where applicable).

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Add test page with log directory viewer, write operations (custom message,
all levels, batch), and file management (refresh, delete). Registers
the native-logger module in the example app route and package.json.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
New modules created via create-nitro-module.js will now include
OneKeyLog dependency out of the box (podspec, build.gradle, imports).

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Replace force unwrap with safe unwrap in iOS logsDirectory
- Use detachAppender instead of detachAndStopAllAppenders on Android
- Handle applicationContext null gracefully in Android OneKeyLog
- Log failures in getLogFilePaths and deleteLogFiles (iOS + Android)
- Redact keychain key names in log output for security
- Replace setTimeout with await in test page

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Remove all key name references from Keychain log messages for security
- Add detailed comment explaining maximumNumberOfLogFiles off-by-one

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
HIGH:
- H-2: Replace unsafe /tmp/logs fallback with no-op when context is null
- H-3: Apply iOS file protection (completeUntilFirstUserAuthentication)
- H-1: Add 4KB message truncation to prevent disk abuse

MEDIUM:
- M-1: Attach file appender to named "OneKey" logger instead of ROOT
  to prevent capturing third-party SLF4J output
- M-2: Change compileOnly to implementation to prevent NoClassDefFoundError
- M-3: Truncation also mitigates log injection via oversized messages
- M-5: Fix TOCTOU race in iOS log archive with retry loop

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- P1: deleteLogFiles skips active app-latest.log to avoid breaking
  logger's open file handle (iOS + Android)
- P2: Android logger uses retry-able cached getter instead of by-lazy,
  so if applicationContext is null at first access, logging can
  recover once context becomes available
- P3: Log warning if iOS archive TOCTOU retry loop exhausts 1000 attempts
- P4: Remove unused OneKeyLog imports from module template
- P5: Use NSString.appendingPathComponent for iOS path construction

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- P1: Replace force unwrap with guard let in ReactNativeGetRandomValues (iOS)
- P2: Remove duplicate error logging in KeychainModule wrapper (Core already logs)
- P2: Change biometric auth change log level from warn to info
- P2: Add @volatile to windowLayoutInfo for thread-safe reads from Promise.async
- P2: Use error.localizedDescription in CloudKit logs for consistency
- P3: Remove unnecessary UserDefaults.synchronize() call

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…log dir

- Native-side log writes now use `HH:mm:ss | LEVEL : [tag] message` format
- JS-side log writes remain unformatted (raw message preserved)
- Add copy-to-clipboard button for log directory path in test page
- Integrate native-logger into example app and update pod dependencies

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- iOS: call OneKeyLog via ObjC bridge (OneKeyLogBridge) to avoid
  Clang module build failure with NitroModules C++ headers
- Android: call OneKeyLog.info directly in MainApplication.onCreate
- Both log "Application started" with native timestamp format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
React Native autolinking generates project names from scoped npm
package names by replacing `@`/`/` with `_`, so
`@onekeyfe/react-native-native-logger` becomes
`:onekeyfe_react-native-native-logger`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@socket-security
Copy link

socket-security bot commented Feb 28, 2026

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

huhuanming and others added 2 commits March 1, 2026 00:30
ContentProvider.onCreate() runs between attachBaseContext() and
Application.onCreate(), so the logger is ready for the earliest
app-level startup logs. Consuming apps no longer need to call
OneKeyLog.init() manually.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 5 new Nitro modules and extend device-utils for migrating all legacy
RCTBridgeModule/ReactContextBaseJavaModule code to New Architecture:

- react-native-bundle-update: JS bundle OTA update with SHA256 verification,
  ZIP extraction, PGP signature validation, and download progress events
- react-native-app-update: Android APK download/install with notification
  progress (iOS stub)
- react-native-perf-memory: Memory usage reporting (RSS) for iOS and Android
- react-native-splash-screen: Legacy splash screen for Android < 12 (iOS stub)
- react-native-webview-checker: Android WebView package info and Google Play
  Services availability check (iOS stub)
- react-native-device-utils: Extended with LaunchOptions, ExitApp, and
  startup time tracking

All modules use OneKeyLog for logging and follow the Nitro callback listener
pattern for events (replacing NativeEventEmitter).
@huhuanming huhuanming changed the title feat: add react-native-native-logger module feat: migrate legacy native modules to Nitro Modules Mar 1, 2026
huhuanming and others added 7 commits March 1, 2026 17:21
Add test pages for BundleUpdate, AppUpdate, PerfMemory, SplashScreen,
and WebViewChecker modules. Also add LaunchOptions and ExitApp tests
to the existing DeviceUtils test page.
…p-update

- Use custom urlSession instead of URLSession.shared to prevent HTTPS bypass via HTTP redirects (iOS bundle-update)
- Add HTTPS redirect detection after download completes (iOS bundle-update)
- Enforce TLS 1.2 minimum on URLSession configuration (iOS bundle-update)
- Use defer to guarantee isDownloading reset on all error paths (iOS bundle-update)
- Wrap downloadAPK in try/finally to guarantee isDownloading reset (Android app-update)
- Make verifyASC throw instead of silently succeeding without GPG verification (Android app-update)
- Fix response.body!! force-unwrap crash in downloadASC (Android app-update)
- Add readTimeout to OkHttpClient to prevent indefinite hangs (Android app-update)
- Make testVerification return false instead of true when GPG is unimplemented (both platforms)
- Remove dead downloadThread field and unused MessageDigest import (Android app-update)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…update

Port GPG verification from app-monorepo desktop implementation to native modules:
- iOS bundle-update: Gopenpgp framework integration via dynamic ObjC calls with graceful fallback
- Android bundle-update: BouncyCastle bcpg for PGP cleartext signature verification
- Android app-update: Full verifyASC implementation with SHA256 hash comparison
- Embed OneKey developer GPG public key for signature validation
- Add real test signatures from app-monorepo for testVerification()

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- native-logger: replace thread-unsafe SimpleDateFormat with DateTimeFormatter (Android),
  add NSLock around DateFormatter calls (iOS)
- native-logger: add android.util.Log fallback when file logger is unavailable (Android)
- bundle-update: add DispatchQueue for thread-safe access to listeners/isDownloading (iOS)
- app-update: validate file paths against app directories to prevent path traversal (Android)
- webview-checker: use WebView.getCurrentWebViewPackage() API 26+ with fallback candidates (Android)
- device-utils: replace exit(0) with UIApplication suspend to avoid App Store rejection (iOS)
- bundle-update: add symlink detection after zip extraction (Android)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Fix Base64 encoding inconsistency between iOS/Android in get-random-values
- Fix UTF-8 truncation at buffer boundaries in bundle-update readFileContent
- Fix dead downloadTask property in iOS bundle-update, use session invalidation
- Fix non-atomic write in bundle-update writeFallbackUpdateBundleDataFile
- Fix clearCache not actually clearing cached files in app-update
- Downgrade CloudKit recordID logging from info to debug level
- Add ProGuard consumer rules for logback-android in native-logger
- Flush log buffers before getLogFilePaths on both platforms

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Integrate Gopenpgp v3.3.0 xcframework directly into app-modules so
app-monorepo no longer needs to separately import the GPG framework.
Replace dynamic ObjC runtime calls (NSClassFromString/NSSelectorFromString)
with direct typed Gopenpgp API imports for type safety and clarity.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…ata from logs

Replace all android.util.Log and NSLog calls in react-native-lite-card
with OneKeyLog (NativeLogger) for unified file-based logging. Remove
plaintext PIN logging, cardInfo dumps, certificate serial numbers, and
APDU response data from log output. Add native-logger as dependency
on both Android (build.gradle) and iOS (podspec).

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
huhuanming and others added 19 commits March 3, 2026 11:07
- Change Promise<Void> to Promise<Unit> in AppUpdate and BundleUpdate
  to match updated Nitro codegen specs
- Fix smart cast issue with signatures in verifyAPK by extracting to
  local vals
- Add missing deepLink parameter to LaunchOptions in DeviceUtils
- Replace NitroModules.applicationContext with BuildConfig.DEBUG in
  lite-card LogUtil (module doesn't depend on nitro-modules)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Library module's BuildConfig.DEBUG won't reflect the host app's build
type. Use Utils.getApp().applicationInfo.flags to check FLAG_DEBUGGABLE
at runtime, which correctly detects the host app's debug state.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…teTestPage

Dark theme, card layout, step-by-step pipeline (Download → Verify → Install)
with animated progress bar, collapsible utilities section, and platform info.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…g, and APK cache verification

- Fix relative path resolution: resolve to cacheDir/apks/ instead of root /
- Add detailed error logging in buildFile and downloadAPK catch block
- Verify existing APK via ASC SHA256 before re-downloading
- Extract shared computeSha256 helper to reduce duplication
- Update clearCache to clean cacheDir/apks/ directory (APK + ASC files)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
filePath is now inferred internally from the download URL's last path
segment instead of being passed as a parameter.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…native layer

- Fix event type mismatch: JS now listens for 'downloading' instead of 'update/downloading'
- Native downloadAPK throws error instead of silently returning when already downloading

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…og results

In verifyAPK and installAPK, debug builds (FLAG_DEBUGGABLE) now run all
comparisons and log match/mismatch results but skip throwing exceptions,
allowing testing with different-signed APKs.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Pipeline now has 5 steps: Download APK → Download ASC → Verify ASC →
Verify APK → Install APK. Removed redundant ASC buttons from utilities.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Align with BundleUpdate convention: update/start, update/downloading,
update/downloaded, update/error.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…rification

- Add bcprov-jdk15to18 dependency (was missing, only bcpg was included)
- Register BouncyCastleProvider at class init to fix "cannot create signature: no provider"
- Add try/catch logging in verifyASC to capture exceptions before they propagate
- Applied to both app-update and bundle-update modules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…id Android built-in BC conflict

Android ships a stripped-down "BC" provider that doesn't support SHA256withRSA.
Passing our own BouncyCastleProvider() instance directly bypasses the name lookup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Declare FileProvider in library manifest (auto-merged into host app)
- Add file_paths.xml exposing cacheDir/apks/ for FileProvider access
- Add REQUEST_INSTALL_PACKAGES permission

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bcProvider was in ReactNativeBundleUpdate companion but used in
BundleUpdateStoreAndroid.verifyGPGAndExtractSha256, causing unresolved
reference error.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
…downloadBundle

- Log progress percentage at each 1% increment during download
- Add catch block to log exceptions before propagating
- Deduplicate sendEvent calls by only emitting on progress change

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@socket-security
Copy link

socket-security bot commented Mar 3, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedreact-native-mmkv@​4.1.21001007390100

View full report

huhuanming and others added 10 commits March 3, 2026 14:44
- Log progress percentage at each 1% increment during URLSession download
- Deduplicate progress callbacks by tracking previous progress value
- Reset progress state on delegate reset

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Ensure parent directory exists before FileOutputStream
- Log totalBytesRead, fileExists, and fileSize after download completes
- Helps diagnose ENOENT issue after download

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d iOS

Each validation step that can silently return null/empty now logs the specific failure reason,
making it easy to diagnose why getJsBundlePath returns empty after bundle install.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…o file

Store signatures as files at <bundleDir>/asc/<version>-signature.asc instead
of SharedPreferences (Android) / UserDefaults (iOS) to avoid storage size
issues with multiple versions. Also adds listAscFiles API and fixes
installBundle passing empty signature in example.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…platforms

Log file existence, size, and path on write; log existence and content size
on read to help diagnose signature storage issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants