Skip to main content

Common Issues

Symptoms: The permission dialog doesn’t appear when calling requestAuthorization().Solutions:
  1. Ensure Health Connect is installed: Health Connect is pre-installed on Android 14+. On older versions, install it from the Google Play Store.
  2. Set Activity before requesting:
    sdk.setActivity(this) // Must be called before requestAuthorization
    
  3. Verify provider is set:
    sdk.setProvider("google") // Must be set before requesting permissions
    sdk.requestAuthorization(types = listOf("steps", "heartRate"))
    
  4. Check min SDK: Your app’s minSdk must be 29 or higher.
  5. Check Health Connect package query: The SDK declares this in its manifest, but verify it merged correctly:
    <queries>
        <package android:name="com.google.android.apps.healthdata" />
    </queries>
    
Symptoms: Samsung Health provider not found or setProvider("samsung") returns false.Solutions:
  1. Samsung devices only: Samsung Health SDK typically only works on Samsung devices.
  2. Samsung Health app must be installed: The user needs the Samsung Health app installed and set up.
  3. Try Health Connect instead: Health Connect works on all Android 10+ devices:
    sdk.setProvider("google")
    
  4. Check available providers:
    val providers = sdk.getAvailableProviders()
    for (p in providers) {
        Log.d("SDK", "${p["displayName"]}: available=${p["isAvailable"]}")
    }
    
Symptoms: Data only syncs when the app is in the foreground.Solutions:
  1. Check battery optimization: Many Android manufacturers (Samsung, Xiaomi, Huawei, etc.) aggressively kill background services. Ask users to:
    • Go to Settings → Apps → Your App → Battery → Unrestricted
    • Or Settings → Battery → Battery Optimization → Your App → Don’t Optimize
  2. Verify notification permission (Android 13+): The SDK uses a foreground service with a notification. On Android 13+, POST_NOTIFICATIONS permission must be granted:
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1)
    }
    
  3. Check WorkManager status: WorkManager handles background scheduling. Verify it’s not constrained:
    val workInfos = WorkManager.getInstance(context)
        .getWorkInfosByTag("health_sync")
        .get()
    for (info in workInfos) {
        Log.d("SDK", "Work state: ${info.state}")
    }
    
  4. Lifecycle callbacks: Ensure you’re calling lifecycle methods:
    override fun onResume() {
        super.onResume()
        sdk.onForeground()
    }
    override fun onPause() {
        super.onPause()
        sdk.onBackground()
    }
    
Symptoms: Exception when calling signIn().Solutions:
  1. Check credentials: Ensure you provide either (accessToken + optional refreshToken) or apiKey:
    // Token-based (recommended)
    sdk.signIn(
        userId = userId,
        accessToken = accessToken,
        refreshToken = refreshToken,
        apiKey = null
    )
    
    // OR API key-based
    sdk.signIn(
        userId = userId,
        accessToken = null,
        refreshToken = null,
        apiKey = apiKey
    )
    
  2. Check network connectivity: Ensure the device can reach your Open Wearables host.
  3. Check token expiration: Access tokens expire. Ensure your backend generates fresh tokens.
  4. Listen for auth errors:
    sdk.authErrorListener = { error ->
        Log.e("SDK", "Auth error: $error")
    }
    
Symptoms: Sync seems to complete but data doesn’t show up in the API.Solutions:
  1. Check logs:
    sdk.logListener = { message ->
        Log.d("HealthSDK", message)
    }
    
  2. Verify health data exists: Open Health Connect (or Samsung Health) and confirm data exists for the requested types.
  3. Check sync status:
    val status = sdk.getSyncStatus()
    Log.d("SDK", "Status: $status")
    
  4. Reset anchors for full sync:
    sdk.resetAnchors()
    sdk.syncNow()
    
  5. Check permissions: Users may have denied specific data types.
Symptoms: getInstance() throws because SDK wasn’t initialized.Solution: Ensure initialize() is called before getInstance():
// ✅ Correct - in Application.onCreate()
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        OpenWearablesHealthSDK.initialize(this)
    }
}

// Then anywhere else:
val sdk = OpenWearablesHealthSDK.getInstance()
Symptoms: Sync session lost after app is killed.Solutions:
  1. Ensure configure() is called on launch:
    // In Application.onCreate()
    val sdk = OpenWearablesHealthSDK.initialize(this)
    sdk.configure(host = "https://api.openwearables.io")
    
  2. Check for resumable sessions:
    if (sdk.hasResumableSyncSession()) {
        sdk.resumeSync()
    }
    
  3. Verify EncryptedSharedPreferences: If the device was factory reset or the app’s data was cleared, stored credentials are lost and the user needs to sign in again.
Symptoms: A persistent notification appears during sync.Solution: This is expected behavior. The SDK uses a foreground service during active data sync for reliability. The notification disappears once the sync cycle completes. You cannot hide this notification — it’s an Android platform requirement for foreground services.

Battery Optimization

Android’s battery optimization can significantly impact background sync. Here’s how different manufacturers handle it:
ManufacturerSetting LocationRecommendation
SamsungSettings → Battery → Background Usage LimitsAllow background activity
XiaomiSettings → Battery → App Battery SaverNo restrictions
HuaweiSettings → Battery → App LaunchManage manually, enable all
OnePlusSettings → Battery → Battery OptimizationDon’t optimize
Pixel/StockSettings → Apps → Battery → UnrestrictedSet to Unrestricted
Consider linking users to dontkillmyapp.com which has device-specific instructions for keeping background services alive.

Testing

Manual Sync Test

// Trigger immediate sync and check results
sdk.syncNow()

// Check status after sync
val status = sdk.getSyncStatus()
Log.d("SDK", "Sent count: ${status["sentCount"]}")
Log.d("SDK", "Completed types: ${status["completedTypes"]}")

Check Stored State

val creds = sdk.getStoredCredentials()
Log.d("SDK", "Stored: $creds")
Log.d("SDK", "Session valid: ${sdk.isSessionValid()}")
Log.d("SDK", "Sync active: ${sdk.isSyncActive()}")

Force Fresh Start

If things are in a bad state:
// Clear everything
sdk.stopBackgroundSync()
sdk.signOut()
sdk.clearSyncSession()
sdk.resetAnchors()

// Re-initialize
sdk.configure(host = "https://api.openwearables.io")

Debugging Tips

Enable Verbose Logging

sdk.logListener = { message ->
    Log.d("HealthSDK", message)
}

sdk.authErrorListener = { error ->
    Log.e("HealthSDK", "Auth error: $error")
}

Check Provider State

val providers = sdk.getAvailableProviders()
for (p in providers) {
    Log.d("SDK", """
        Provider: ${p["displayName"]}
        ID: ${p["id"]}
        Available: ${p["isAvailable"]}
    """.trimIndent())
}

Getting Help

If you’re still experiencing issues:

GitHub Issues

Report bugs or request features on the SDK repository.

Discussions

Ask questions and get help from the community.
When reporting issues, please include:
  • Android version and device model
  • SDK version
  • Health provider used (Samsung Health or Health Connect)
  • Relevant logs from logListener
  • Steps to reproduce