-
Notifications
You must be signed in to change notification settings - Fork 0
Initial device library for Android #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
3a228c0 to
06e14b8
Compare
|
This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation. |
06e14b8 to
db80ba7
Compare
db80ba7 to
e7fe983
Compare
e7fe983 to
36340bc
Compare
b500191 to
1f14579
Compare
Add CI/CD infrastructure:
- ci.yml: Build SDK, run tests, lint (detekt/ktlint), build sample app
- codeql.yml: Security analysis for Java/Kotlin (weekly schedule)
- zizmor.yml: Workflow security scanning
- dependabot.yml: Daily dependency updates with 4-day cooldown
All workflows pass zizmor validation with:
- Actions pinned to SHA hashes
- Minimal permissions (permissions: {} at top level)
- persist-credentials: false on all checkouts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
b8c6bed to
47ca150
Compare
Foundation commit establishing the Android device fingerprinting SDK:
- SDK architecture with singleton DeviceTracker, SdkConfig builder pattern
- Restructured DeviceData model with nested types for organized signal collection:
- StoredIDs (mediaDrmID, androidID placeholders)
- BuildInfo (comprehensive Build.* fields)
- DisplayInfo (screen metrics with refresh rate)
- HardwareInfo (CPU cores, memory, storage)
- InstallationInfo (install timestamps, source)
- LocaleInfo (language, country, timezone)
- Placeholder types for future: GpuInfo, AudioInfo, SensorInfo, CameraInfo,
CodecInfo, NetworkInfo, SystemSettings, BehaviorInfo
- DeviceDataCollector populating build, display, hardware, installation, locale
- Network layer with Ktor HTTP client
- Sample app demonstrating SDK usage
- Unit tests for SdkConfig builder validation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement persistent device identifier collection: - MediaDRM ID: Hardware-backed ID using Widevine, persists through factory reset - Android ID (SSAID): App-scoped ID that persists across reinstalls Both IDs gracefully handle unavailable scenarios (emulators, custom ROMs). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Comprehensive tests for DeviceDataCollector covering: - Build info (fingerprint, manufacturer, model, ABIs) - Display info (dimensions, density, refresh rate) - Hardware info (CPU cores, memory) - Installation info (install times, version) - Locale and timezone - StoredIDs integration Also fixes ktlint formatting in StoredIDsCollector. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Collect GPU fingerprint using EGL/OpenGL ES: - Renderer name (e.g., "Adreno 640") - Vendor (e.g., "Qualcomm") - OpenGL ES version - Supported extensions Creates a temporary PBuffer surface to query GPU info without requiring a visible window. Gracefully returns null when OpenGL ES is unavailable (emulators, some devices). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Collect audio configuration via AudioManager: - Native output sample rate (e.g., "48000") - Output frames per buffer (e.g., "256") These values indicate the device's native audio path configuration and can be useful for device fingerprinting. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Enumerate all device sensors via SensorManager: - Sensor name and vendor - Type, version, and specifications - Power consumption and resolution The complete sensor list provides a unique fingerprint of the device's hardware capabilities. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Enumerate cameras via CameraManager (no permission required): - Camera ID and facing direction (front/back/external) - Sensor physical size - Supported JPEG resolutions - Available focal lengths Provides device fingerprinting without requesting camera permission. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Collect media codec support via MediaCodecList: - Audio codecs (name, supported types, encoder/decoder) - Video codecs (name, supported types, encoder/decoder) Collect system features via PackageManager: - Hardware features (camera, bluetooth, wifi, etc.) - Software features (live wallpaper, etc.) Both provide device fingerprinting signals. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Collect network information via ConnectivityManager and WifiManager: - Connection type (wifi, cellular, ethernet, bluetooth, vpn) - Metered status - Downstream bandwidth estimate - WiFi-specific: frequency, link speed, signal strength (RSSI) Adds ACCESS_WIFI_STATE permission (ACCESS_NETWORK_STATE was already present). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Collect system settings via Settings.System/Global: - Screen timeout - Development settings enabled - ADB enabled - Animator duration scale - Boot count Collect behavioral signals via Settings.Secure: - Enabled input methods (keyboards) - Enabled accessibility services These signals help identify device configuration and usage patterns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Collect the default WebView user agent string via WebSettings, which provides browser version and system information useful for device fingerprinting. Gracefully handles devices where WebView is unavailable. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add @Suppress annotations for LongMethod and ReturnCount in GpuCollector - Add @Suppress annotation for ReturnCount in NetworkCollector - Use error() instead of throw IllegalStateException in DeviceTracker - Add @Suppress for TooGenericExceptionCaught in DeviceTracker and DeviceApiClient 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit implements server-generated stored IDs (similar to browser
cookies in the JS implementation) and renames the existing device-generated
identifiers for clarity.
Changes:
- Rename StoredIDs → DeviceIDs for device-generated hardware identifiers
(MediaDRM ID, Android ID)
- Add StoredID model for server-generated IDs (format: "{uuid}:{hmac}")
- Add StoredIDStorage using SharedPreferences for persistence
- Add StoredIDCollector to read stored IDs during collection
- Update DeviceApiClient to parse server response and return stored ID
- Update DeviceTracker to save stored ID from server response
- Add @SerialName annotations for snake_case JSON serialization
JSON field names use snake_case for consistency with other MaxMind services:
- stored_id, device_ids, media_drm_id, android_id
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add two new device fingerprinting signals: 1. Telephony Context (TelephonyCollector): - Network operator name - SIM state - Phone type (GSM/CDMA/etc) - ICC card presence 2. Font Profile (FontCollector): - Tests for presence of standard Android fonts - Tests for manufacturer-specific fonts (Samsung, HTC, Sony, LG, Xiaomi, OnePlus) - Helps identify device manufacturers and custom ROMs These signals were identified as missing from the original implementation plan and provide additional device fingerprinting capabilities. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive HTTP tests using Ktor MockEngine: - Test successful responses with stored_id - Test null stored_id handling - Test server error (500) handling - Test client error (400) handling - Test request body contains account_id and device data - Test correct endpoint URL construction - Test content type header - Test network exception handling Changes: - Add ktor-client-mock test dependency - Modify DeviceApiClient to accept optional HttpClient for testing - Fix NestedClassesVisibility warning in ApiException - Catch specific exceptions (SecurityException, IllegalArgumentException, IllegalStateException) instead of generic Exception in collectors - Update CodecCollectorTest and TelephonyCollectorTest for specific exceptions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
47ca150 to
85d2cbd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR introduces the initial MaxMind Device SDK for Android, a comprehensive library for collecting device fingerprinting data and sending it to MaxMind servers. The implementation follows Android best practices with Kotlin-first design, Java compatibility, and coroutine-based async operations.
Key changes:
- Complete SDK implementation with singleton pattern and initialization guard
- Comprehensive device data collection (GPU, sensors, cameras, audio, display, etc.)
- Network layer using Ktor with dual IPv6/IPv4 request flow
- Sample app demonstrating SDK usage with Material Design UI
- Extensive unit test coverage using JUnit 5, MockK, and Robolectric
Reviewed changes
Copilot reviewed 81 out of 81 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| sample/src/main/res/values/strings.xml | Added UI strings for sample app demonstrating SDK functionality |
| sample/src/main/res/layout/activity_main.xml | Material Design UI layout for SDK demo with collapsible device info sections |
| sample/src/main/java/.../MainActivity.kt | Sample app demonstrating SDK initialization, data collection, and transmission |
| device-sdk/src/test/.../StoredIDStorageTest.kt | Unit tests for SharedPreferences-based stored ID persistence |
| device-sdk/src/test/.../DeviceApiClientTest.kt | Comprehensive tests for HTTP client with mock engine |
| device-sdk/src/test/.../SdkConfigTest.kt | Builder pattern validation tests for SDK configuration |
| device-sdk/src/test/.../collector/helper/*.kt | Tests for device info collection helpers (locale, installation, hardware, display) |
| device-sdk/src/test/.../collector/*.kt | Tests for specialized collectors (WebView, telephony, sensors, etc.) |
| device-sdk/src/test/.../DeviceTrackerTest.kt | Singleton lifecycle tests with reflection-based reset |
| device-sdk/src/main/.../storage/StoredIDStorage.kt | SharedPreferences-based persistence for server-generated IDs |
| device-sdk/src/main/.../network/DeviceApiClient.kt | Ktor HTTP client with dual IPv6/IPv4 request support |
| device-sdk/src/main/.../model/*.kt | Serializable data models for device information |
| device-sdk/src/main/.../config/SdkConfig.kt | Immutable configuration with builder pattern |
| device-sdk/src/main/.../collector/helper/*.kt | Helper classes for collecting device information |
| device-sdk/src/main/.../collector/*.kt | Specialized collectors for various device subsystems |
| device-sdk/src/main/.../DeviceTracker.kt | Main SDK singleton entry point with lifecycle management |
| device-sdk/src/androidTest/.../collector/*.kt | Instrumented tests for GPU, fonts, and cameras |
| device-sdk/proguard-rules.pro | R8/ProGuard rules for SDK minification |
| device-sdk/consumer-rules.pro | Consumer ProGuard rules for apps using the SDK |
| SETUP.md | Comprehensive project setup guide |
| README.md | SDK documentation with usage examples and API reference |
| CLAUDE.md | Development guidelines for AI assistants |
| CHANGELOG.md | Version history starting with 0.1.0 |
The code is well-structured, thoroughly tested, and follows Android/Kotlin best practices. No critical issues were identified during the review.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Add dual-request logic to capture both IPv6 and IPv4 addresses for devices: - First sends to IPv6 endpoint (d-ipv6.mmapiws.com) - If response has ip_version=6, also sends to IPv4 endpoint (d-ipv4.mmapiws.com) - Custom server URL bypasses dual-request and sends to single endpoint Changes: - SdkConfig: Replace serverUrl with customServerUrl, add useDefaultServers, add DEFAULT_IPV6_HOST, DEFAULT_IPV4_HOST, ENDPOINT_PATH constants - ServerResponse: Add ipVersion field for IP version detection - DeviceApiClient: Refactor to accept SdkConfig, implement sendWithDualRequest() - DeviceTracker: Update to use SdkConfig-based DeviceApiClient - Update tests for new behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Display device data in expandable/collapsible sections instead of raw JSON. Uses reflection to dynamically iterate over DeviceData properties, so new fields are automatically included without code changes. Changes: - Add collapsible section UI with tap to expand/collapse - Show summary at top, detailed JSON in collapsible sections below - Add kotlin-reflect dependency for dynamic property iteration - All sections collapsed by default 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Display stored ID, MediaDRM ID, and Android ID in the summary section for quick reference when debugging. Shows "(none)" for null values. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add a collectSafe() helper function that wraps inline collection methods with try-catch error handling. This ensures partial device data is still collected even if individual subsystems fail (e.g., WindowManager or PackageManager throws). Changes: - Add collectSafe<T>(fallback, block) inline function - Define fallback constants for BuildInfo, DisplayInfo, HardwareInfo, InstallationInfo, and LocaleInfo - Wrap all inline collection calls in collect() with collectSafe - Add enableLogging constructor parameter for optional failure logging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Extract inline collection methods into injectable helper classes to enable unit testing without requiring Robolectric or instrumented tests. New helper classes: - BuildInfoHelper: Collects device build information from Build.* - DisplayInfoHelper: Collects display metrics and HDR capabilities - HardwareInfoHelper: Collects CPU, memory, and storage info - InstallationInfoHelper: Collects app installation metadata - LocaleInfoHelper: Collects locale and timezone info DeviceDataCollector now accepts these helpers via constructor with defaults, allowing tests to inject mocks for isolated testing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add instrumented tests that run on real Android devices/emulators to verify: - GpuCollector: EGL context creation, renderer/vendor info, resource cleanup - CameraCollector: Camera2 API access, facing values, repeated calls - FontCollector: Typeface detection, standard fonts, consistency These tests require actual Android APIs and cannot be mocked in unit tests. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add unit tests for DeviceTracker singleton pattern using MockK to mock the companion object state. Tests cover: - getInstance throws when not initialized - isInitialized returns expected values - initialize throws when already initialized - getInstance returns tracker when initialized - collectDeviceData and shutdown can be called - SdkConfig.Builder creates valid configuration Uses mockkObject for companion object mocking since reflection-based singleton reset is unreliable with Kotlin @volatile fields. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fix documentation issues: - Change SdkConfig.Builder to use Int accountID instead of String API key - Fix deviceData property access to use nested structure (deviceData.build.*) - Update configuration options table with correct parameter names/types Expand "Collected Data" section to document all signals: - Device identifiers (storedID, deviceIDs) - Device info (build, display, hardware) - Subsystems (GPU, audio, sensors, cameras, codecs) - System state (features, network, installation, settings, behavior) - Additional signals (telephony, fonts, locale/timezone, WebView UA) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
85d2cbd to
f224769
Compare
9ab8d81 to
eb121fd
Compare
Add ability to configure the sample app to connect to a local development server instead of production MaxMind servers. This is useful for testing the SDK against a local backend. Configuration via local.properties (gitignored): - debug.server.url: Custom server URL (e.g., https://localhost:8443) - debug.ca.cert: Path to CA certificate for self-signed HTTPS When debug.ca.cert is configured, the build system: - Copies the certificate to res/raw/debug_ca.crt (gitignored) - Generates a network_security_config.xml that trusts the bundled cert - Sets BuildConfig.DEBUG_SERVER_URL for the app to use Without configuration, the sample app connects to production servers. Also documents ADB reverse port forwarding for physical device testing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Document the pre-commit formatting workflow using precious tidy -g to fix formatting issues before committing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Move account ID from hardcoded value to local.properties configuration. The sample app now requires maxmind.account.id to be set, showing an error message if not configured. Configuration in local.properties: maxmind.account.id=123456 This ensures developers use their own account ID rather than a demo value. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Measure HTTP round-trip time of the first (IPv6) request and send it with the second (IPv4) request. This matches the browser device.js behavior and helps with proxy detection. Changes: - Add requestDuration field to DeviceData (Float?, milliseconds) - Measure time in sendWithDualRequest() around IPv6 request - Pass duration to IPv4 request via DeviceData.copy() - Add tests for dual-request behavior and request_duration field 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
I can't comment inline due to the size.
|
No description provided.