NordicSemiconductor / Android-DFU-Library

Device Firmware Update library and Android app
http://www.nordicsemi.com/dfu
BSD 3-Clause "New" or "Revised" License
753 stars 263 forks source link

Android 14 requires starting FGS with service type specified #423

Closed pashaoleynik97 closed 7 months ago

pashaoleynik97 commented 7 months ago

Where do you suspect the issue?

Issue related to Android version or specific device

Version

2.4.0 (Latest)

Describe the issue

In the DfuBaseService.java there is a line to start Foreground Service: startForeground(NOTIFICATION_ID, builder.build());

The problem is that apps targeting Android 14 requires service type to be specified. So, when DFU library tries to go foreground, it crashes with exception: android.app.MissingForegroundServiceTypeException: Starting FGS without a type.

Lib version: no.nordicsemi.android:dfu:2.4.0

Relevant log output

FATAL EXCEPTION: IntentService[DfuBaseService]
Process: com.companyname.app, PID: 22057
android.app.MissingForegroundServiceTypeException: Starting FGS without a type callerApp=ProcessRecord{4bd67b6 22057:com.companyname.app/u0a699} targetSDK=34
    at android.app.MissingForegroundServiceTypeException$1.createFromParcel(MissingForegroundServiceTypeException.java:53)
    at android.app.MissingForegroundServiceTypeException$1.createFromParcel(MissingForegroundServiceTypeException.java:49)
    at android.os.Parcel.readParcelableInternal(Parcel.java:4882)
    at android.os.Parcel.readParcelable(Parcel.java:4864)
    at android.os.Parcel.createExceptionOrNull(Parcel.java:3064)
    at android.os.Parcel.createException(Parcel.java:3053)
    at android.os.Parcel.readException(Parcel.java:3036)
    at android.os.Parcel.readException(Parcel.java:2978)
    at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:7214)
    at java.lang.reflect.Method.invoke(Native Method)
    at leakcanary.ServiceWatcher$install$4$2.invoke(ServiceWatcher.kt:93)
    at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
    at $Proxy3.setServiceForeground(Unknown Source)
    at android.app.Service.startForeground(Service.java:775)
    at no.nordicsemi.android.dfu.DfuBaseService.startForeground(DfuBaseService.java:1917)
    at no.nordicsemi.android.dfu.DfuBaseService.onHandleIntent(DfuBaseService.java:1153)
    at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:77)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:230)
    at android.os.Looper.loop(Looper.java:319)
philips77 commented 7 months ago

Hello, The library does not define the service in the manifest. It was delegated to the apps using it. You may:

  1. Check out your Android Manifest and the <service> tag, specify the service type to connectedDevice. Add FOREGROUND_SERVICE_CONNECTED_DEVICE permission, this is also needed. Mind, that you'll have to provide a video when publishing the app to Google Play showing how that permission is used. Just a video of DFU should work (worked for us in nRF DFU).
  2. Use: https://github.com/NordicSemiconductor/Android-DFU-Library/blob/d5708d2c36ba6410f1212648c10ff99f28aa61d7/lib/dfu/src/main/java/no/nordicsemi/android/dfu/DfuServiceInitiator.java#L152-L155 to disable running the service in foreground. If the DFU takes less then couple of minutes, or the app is open during the time, foreground service is not needed. You don't need to set the service type or add this permission in that case.
pashaoleynik97 commented 7 months ago

Thanks for the answer, @philips77

You are right, there were missing property in <service> tag: android:foregroundServiceType="connectedDevice".

With such Manifest it works fine:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!--  ...  -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />

    <application>

        <service
            android:name="com.companyname.app.service.MyDfuService"
            android:foregroundServiceType="connectedDevice"
            android:enabled="true"
            android:exported="false" />
    </application>
</manifest>