31

I'm developing an app which is basically a location tracking software. When you start it, it saves locations and sending them to a server.

The code is working for like 5 years now without any modification, without any errors.

It is implemented with a simple foreground service.

In the recent months I was getting user reported errors about the service stops randomly on Huawei devices. First I thought it is some kind of rare/new crash on newer androids but there was no error logs at all in Fabric.

I tried it in a new Huawei device and for my greatest surprise, this phenomenon does really exists. Huawei devices (with EMUI) does really kills the foreground services after a couple of minutes.

This is really really bad for my app, first of all, the users want to run this tracking app for long hours, and secondly, the recent months made Huawei be a popular choice amongs Android users. Like 10% of my user base has a Huawei device.

I'm aware of https://dontkillmyapp.com/ It is a great website for getting information about this issue.

I have tried their solution - which is basically adding a wakelock with a specific tag to my service, so Huawei's EMUI won't kill it.

I've tried this in the following way, but my Huawei test device still kills my foreground service after some minutes.

Code inside my Service:

I basically aquires a wakelock in the service's onCreate callback.

 private void acquireLock() {

    if (wakeLock == null) {
        PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
        if (mgr != null) {

            if (Build.MANUFACTURER.toLowerCase().equals("huawei")) {
                lockTag = "LocationManagerService";
            }

            wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockTag);

            Log.i("MY_TAG", "tag:" + lockTag);
        }
    }
    if (wakeLock != null && !wakeLock.isHeld()) {
        wakeLock.acquire();
        //also tried with: wakeLock.acquire(1000*60*60*72); 3 days wakelock just in case.
        Log.i("MY_TAG", "wakeLock acquired!");
    }
}

@Override
public void onCreate() {
    acquireLock();
}

E D I T:

Clraification: My service is a foreground service, with a presistent notification. It can run well for DAYS on other devices.

Please help if you can,

Adam

IgorGanapolsky
  • 26,189
  • 23
  • 116
  • 147
Adam Varhegyi
  • 11,307
  • 33
  • 124
  • 222
  • Huawei is customizing AOSP and putting more restrictions in battery optimization and other places so that the user's don't encounter such a problem that – M D Jul 09 '19 at 12:40
  • 1
    did you resolved problem ? i am working on that if you need i can give you some good suggestions . – A Farmanbar Nov 28 '19 at 16:32
  • 1
    @Mr.AF would love to hear them – Adam Varhegyi Nov 29 '19 at 16:28
  • 1
    I'm working with foregroundService too, with AOSP android OS the required action are: showing the foreground notification and disabling doze by the user. With devices with OEM rom and OEM battery optimization you must follow dontkillmyapp. Dontkillmyapp ranks Huawei the worst manufacturer and say there is not a solution, both user side and developer side. – fireb86 Feb 27 '20 at 13:36
  • Did you found solution ? I have already tried awakelock it helps little bit but doesn't guarantee to keep alive the service. The only think that really helps is in battery settings for my app switch from automatic optimization to manual .However this isn't good solution for me because it's depends on user that can be lazy. – DespeiL May 13 '20 at 16:48

9 Answers9

5

I had a similar problem a few months ago, I spent a lot of time to search a solution and finally i have found this (i don't know if works for you, but it was help me).

I implemented in an application that I developed, a service that recover the user position every minutes, this position is saved in the sqlite database of the device. I need that the application work alway without interrupt.

In the test phase I found that some devices interrupted the execution of my code (like in your case).

After several fists on my desk, I realized that the problem was related to energy saving options which are different for some manufacturers but also for Android versions.

This solution help me:

@SuppressLint({"NewApi", "BatteryLife"})
private void checkOptimization() {
    String packageName = getApplicationContext().getPackageName();
    PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
    if (pm != null) {
        if (!pm.isIgnoringBatteryOptimizations(packageName)) {
                Intent intent = new Intent();
                intent.setAction(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                intent.setData(Uri.parse("package:" + ctx.getPackageName()));
                ctx.startActivity(intent);
        } else {
            new initialize().execute();
        }
    }
}

Basically I ask the user (I can't do it any other way) to allow my application to avoid being optimized (this code work for Build.VERSION.SDK_INT >= 23)

The manifest file needs this permission:

android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS  
bytesculptor
  • 411
  • 1
  • 4
  • 21
Mattia
  • 1,057
  • 2
  • 17
  • 33
3

This is not a Huawei related solution but there are useful actions to mitigate the problem.

Calling startForeground and START_STICKY is required in this case

/** YourService **/

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    startForeground(
        App.NOTIFICATION_ID_YOUR_SERVICE,
        buildNotification("Foo bar")
    )
    return START_STICKY
}

These two methods allow the user to disable doze (Oreo>) and enable autostart permission (some OEM) to preserve the STICKY service lifecycle.

/** YourActivity **/

fun openBatteryOptimization(context: Context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val intent = Intent()
        intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
        context.startActivity(intent)
    } else {
        //Timber.d("Battery optimization not necessary")
    }
}

fun openAutostartSettings(context: Context) {
    try {
        val intent = Intent()
        val manufacturer = Build.MANUFACTURER
        if ("xiaomi".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.miui.securitycenter",
                "com.miui.permcenter.autostart.AutoStartManagementActivity"
            )
        } else if ("oppo".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.coloros.safecenter",
                "com.coloros.safecenter.permission.startup.StartupAppListActivity"
            ) //need "oppo.permission.OPPO_COMPONENT_SAFE" in the manifest
        } else if ("vivo".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.vivo.permissionmanager",
                "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"
            )
        } else if ("Letv".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.letv.android.letvsafe",
                "com.letv.android.letvsafe.AutobootManageActivity"
            )
        } else if ("Honor".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.huawei.systemmanager",
                "com.huawei.systemmanager.optimize.process.ProtectActivity"
            )
        } else {
            //Timber.d("Auto-start permission not necessary")
        }
        val list = context.packageManager
            .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
        if (list.size > 0) {
            context.startActivity(intent)
        }
    } catch (e: Exception) {
    }
}

Also using partial wake lock help to mitigate but it does not guarantee to keep alive the service.

/** YourService **/

private val wakeLock: PowerManager.WakeLock by lazy {
    (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
        newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ServiceWakelock")
    }
}

private fun acquireWakelock() {
    try {
        wakeLock.let {
            wakeLock.setReferenceCounted(false)
            if (!wakeLock.isHeld) {
                wakeLock.acquire()
            }
        }
    } catch (e: RuntimeException) {
    }
}

private fun releaseWakelock() {
    try {
        wakeLock.let {
            if (it.isHeld) {
                it.release()
            }
        }
    } catch (e: RuntimeException) {
    }
}

override fun onCreate() {
    super.onCreate()
    acquireWakelock()
}

override fun onDestroy() {
    releaseWakelock()
    super.onDestroy()
}

With Binder you can check if the service is running or not (and starts it up again) and you can get the instance of the service.

/** YourActivity **/

private val mConnection = object : ServiceConnection {

    override fun onServiceConnected(className: ComponentName, iBinder: IBinder) {
        // The system calls this to deliver the IBinder returned by the service's onBind() method.
        val binder = iBinder as YourService.YourBinder
        service = binder.getService()
        bound = true
    }

    override fun onServiceDisconnected(arg0: ComponentName) {
        // The Android system calls this when the connection to the service is unexpectedly lost, such as when the service has crashed or has been killed. This is not called when the client unbinds
        bound = false
    }
}

private fun bindYourService() {
    Intent(this, YourService::class.java).also { intent ->
        applicationContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
    }
}

private fun unbindYourService() {
    try {
        applicationContext.unbindService(mConnection)
    } catch (e: Exception) {
        Timber.e(e)
    }
    bound = false
}

/** YourService **/

private val binder = YourBinder()

inner class YourBinder: Binder() {
    fun getService(): YourService = this@YourService
}

override fun onBind(intent: Intent): IBinder {
    return binder
}

override fun onRebind(intent: Intent?) {
    super.onRebind(intent)
}

override fun onUnbind(intent: Intent?): Boolean {
    return super.onUnbind(intent)
}
fireb86
  • 1,723
  • 21
  • 35
  • 1
    i read your answer. good materials although may not be complete answer but helps for complete answer. – A Farmanbar Feb 28 '20 at 13:15
  • What is `AutoStartManagementActivity`? – IgorGanapolsky Apr 12 '20 at 15:33
  • @IgorGanapolsky Xiaomi autostart settings – fireb86 Apr 12 '20 at 16:07
  • Good answer, but it IS Huawei issue. Because, for example, my app works fine with Samsung, and Huawei kills it after lock screen. When I removed the app from battery optimization in Settings, the app works ok even after lock screen, but after some period of time still is killed by the OS. I use ForegroundService, sticky, with WakeLock in it. – alla Jul 16 '20 at 08:13
  • anyone can provide the complete answer in java? – FGH Sep 01 '20 at 03:59
  • @fireb86 could you please take a look at the question of mine? https://stackoverflow.com/questions/74284432/how-to-create-location-foreground-service-without-google-play-service – Kumza Ion Nov 02 '22 at 07:01
0

Huawei's EMUI monitors and manages the battery consumption of the apps strictly. Even app has the permissions of Secondary Launch, Run in Background and Auto-Launch, EMUI may kill it or it's background processes after a while.

EMUI manages this through a list and as i know if an application is having such issues, it is necessary to apply for a special permission to be on that list.

captaink
  • 476
  • 3
  • 8
  • "it is necessary to apply for a special permission to be on that list.". Tell me more about that special permissions. – deadfish Jun 12 '20 at 06:38
0

For me the following helped (changes in Settings of Huawei device):

Step 1. Set your Huawei smartphone to allow specific apps to run in the background

Step 2. Disable the Battery optimization for the same apps

Sources:

https://www.digitalcitizen.life/stop-huawei-from-closing-apps-when-you-lock-screen

https://dontkillmyapp.com/

alla
  • 529
  • 5
  • 20
0

This is like a game between App developers who want to keep the Apps alive and OS developers who want to detect and kill unnecessary processes to optimize OS performance.

For Huawei devices, there are 3 elegant ways to keep the App alive as I have tried, and they have pros&cons.

1. Guide the users to give the App permissions to run in the background.

You can implement the code to ask users to give needed permissions in Settings->Apps->App launch

    private void showActivity(@NonNull String packageName, @NonNull String activityDir) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(packageName, activityDir));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }

    private void goSettings() {
        try {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
        } catch (Exception e) {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.optimize.bootstart.BootStartActivity");
        }
    }

This can be done directly in the code, but users have to do the settings and in some certain scenarios(manually optimize the OS), the App might still be killed.

2. Integrate Huawei's HMS Push Kit.

It provides high-priority data messages, which can let you send data messages to the destination devices and when the messages arrive, the corresponding app is directly started. In this way, you can activate the App by sending messages periodically or triggered by certain activities, but sending high-priority data messages requires applying for permission and it takes several weeks.

3. Apply for EMUI background activity permission.

This is probably the once and for all solution, it gives the App permission to operate in the background and ignore power consumption and OS optimization. No need to change the code and very straightforward, but the application needs about a month to review and there is a chance it may be denied.

zhangxaochen
  • 32,744
  • 15
  • 77
  • 108
-1

It's not related to Huawei phones but Android OS Version.

Since releasing Android 26 (Oreo). Google decided to set some restrictions & Limitations and Also some permissions would not grant either. Basically, i would like to suggest you to read more about Google Oreo and later Permissions security and policy.

i have your problem and my app works perfect in < Android 26 but for later version, i encountered a big disaster and i am working on it to handle it for new android version users right now. Therefore, you have to adapt to Current Google Policy. Otherwise, you have to make decision of SHIFT + DELETE.


Clraification: My service is a foreground service, with a presistent notification. It can run well for DAYS on other devices.

As you clarified , Your application service life cycle continues when app is closed. However, According to Background Location Limits

In an effort to reduce power consumption, Android 8.0 (API level 26) limits how frequently background apps can retrieve the user's current location. Apps can receive location updates only a few times each hour.

As a result, Your foreground service may be closed because of extra location updates which is banned.

How To Fix

According to Background Location Limits

If an app is in the foreground on a device running Android 8.0 (API level 26), the location update behavior is the same as on Android 7.1.1 (API level 25) and lower.

Therefore, if the user closes the app even the foreground service was started either intentionally or not, Notify the user to open the app again.

Tuning your app's location behavior

  • Bring your app to the foreground.

  • Start a foreground service in your app by calling startForegroundService(). When such a foreground service is active,
    it appears as an ongoing notification in the notification area.

  • Use elements of the Geofencing API, such as the GeofencingClient, which are optimized for minimizing power use.

  • Use a passive location listener, which may receive faster location updates if there are foreground apps requesting location updates at a faster rate.

A Farmanbar
  • 4,381
  • 5
  • 24
  • 42
  • Can you cite such restrictions? Is there any which could make this happen? – Adam Varhegyi Jul 08 '19 at 14:37
  • @AdamVarhegyi i can't exactly say what's your problem because something that you show in question is not helpful but i can bring you example. – A Farmanbar Jul 08 '19 at 22:38
  • 3
    Its simple, my foreground service gets killed by OS in Huaweis. (EMUI) Thats it. – Adam Varhegyi Jul 09 '19 at 12:03
  • @AdamVarhegyi does your foreground service must keep continue while app is closed? or it works during app life cycle ? – A Farmanbar Jul 09 '19 at 13:40
  • @AdamVarhegyi i edited the post.hope it works for you.cuz it worked for me.good luck – A Farmanbar Jul 12 '19 at 03:40
  • 4
    I believe this answer is not true and shouldn't be accepted as a solution. The fact is that foreground services keep running on all model android implementation devices in compliance with doze mode. In essence, if you're moving (location changes), you're moving the device (sensors changes) or high priority notification comes in, the device still keeps the service running. In my case, my app works on all tested devices except huawei. – laszlo Jan 16 '20 at 11:48
  • OK then you might be right, I haven't realized you meant this. – laszlo Jan 16 '20 at 14:19
  • This answer is absolutely wrong ... this is caused by the problem of energy saving of mobile phones, most china mobile phones have this problem Huawei is one of them – lazy Mar 03 '20 at 02:46
  • @lazy i think you are absolutely wrong because this answer is almost official document and there is no difference between Huawei or sumsung or LG. unless, google put wrong document. – A Farmanbar Mar 03 '20 at 08:45
  • @Mr.AF The official document is of course no problem, but it has nothing to do with the problem of the op,The cause of this problem is that the OS in Huawei OEM battery optimization kills the foreground service – lazy Mar 03 '20 at 08:55
  • @lazy i do recommend you to read more about OS and System Design and limitation on battery usage and google privacy policy then you will understand what i have explained. – A Farmanbar Mar 03 '20 at 08:58
  • What do you mean by `Notify the user to open the app again.`? – IgorGanapolsky Apr 12 '20 at 16:01
  • @IgorGanapolsky when foreground service is online but the app is closed. – A Farmanbar Apr 13 '20 at 13:48
  • I think Doze mode in Android 11 is smarter than all of that. – IgorGanapolsky Apr 13 '20 at 15:44
  • haha, man wanted to look smart answering this way, it's not Huawei, it's not Huawei.. but Android OS.. so funny. Huawei defender:) – user924 Jan 24 '21 at 10:54
-1

Try going through here for once - explanation n code a never ending background service using broadcast receiver by setting its priority as HIGH

I'm using it & it works completely fine for me till now, we don't have any client using Huawei devices, so IDK how will it work on that devices, but as it works well on Samsung for us, I guess it will work as expected on Huawei as well. Give it a look.

Blu
  • 821
  • 4
  • 17
-2

I think the issue is in the way you handles the services in your app.
The major thing is, you need to keep a notification running on the status bar that indicates the service related to your app is running and end-user can terminate the service by clicking on the notification.
It is the combination of service + notification + required policies.
Hope it helps.

Viraj S
  • 390
  • 2
  • 12
-2

I have tested below code in a huawei and it worked. Maybe you can use it in your service with notification. It might not be perfect though i am still a novice on the subject.

public class SensorService extends Service {
public int counter=0;
Context context;



public SensorService(Context applicationContext) {
    super();


    Log.i("HERE", "here I am!");
}

static PhoneCallListener phoneListener = null;
TelephonyManager telephonyManager;
public SensorService() {


}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);

    String CHANNEL_ID = "yourid";
    String CHANNEL_NAME = "yourchannel";
    NotificationChannel channel = null;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        channel = new NotificationChannel(CHANNEL_ID,
                CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
    }

    NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        if (manager != null) {
            manager.createNotificationChannel(channel);
        }
    }



    Notification notification = null;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        notification = new Notification.Builder(this, CHANNEL_ID)
                .setContentTitle("title")
                .setContentText("text")

                .setAutoCancel(true)
                .build();
        this.startForeground(1,notification);
    }






    telephonyManager = (TelephonyManager) this
            .getSystemService(Context.TELEPHONY_SERVICE);



    if(phoneListener == null) {

        if (telephonyManager != null) {
            phoneListener = new PhoneCallListener();
            telephonyManager.listen(phoneListener,
                    PhoneStateListener.LISTEN_CALL_STATE);
        }
    }

    System.out.println("SERVICE");
    return START_STICKY;


}


@Override
public void onDestroy() {
    super.onDestroy();
    Log.i("EXIT", "ondestroy!");
    Intent broadcastIntent = new Intent(this, SensorRestarterBroadcastReceiver.class);

    sendBroadcast(broadcastIntent);




    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(new Intent(context, SensorService.class));

    } else {
        context.startService(new Intent(context, SensorService.class));
    }


    Intent brcast = new Intent(this, AlarmReceiver.class);

    sendBroadcast(brcast);


}



@Nullable
@Override
public IBinder onBind(Intent intent) {
    context = getApplicationContext();
    return null;
}


}
Fractal
  • 88
  • 1
  • 6