Android Services: Recommendations when using AIDL for continuous service and low current drain impact

Last month a lot things happened.. I lost my dad, moved to another location .. thus, this blog was not the priority but was not forgotten.

This post is related to some issues I have observed in some Dalvik implementation and some recommendations is you want to create a service that must remains running all the time even after a device power on/off or reset.

Suppose you need to create a “light” service that must be:

1) starts when every time your devices boots.

2) contains very nice parcelable objects using aidl syntax sharing them thru IPC

3) is used to monitor something every X minutes but cannot impact the current drain.

4) the service must use the “uptime”, I mean, must reports “how long” is running

5) must be able to receive interruptions and reports them

A good application of this service could be an alarm central unit for your car. You could create this project using your old Android phone and transforming your old phone and hidden it in your car. You can also connect this old phone disassembling a 12V charging and connecting in some spot using the terminals of your battery.

So, let’s go to some mistakes.. see the list below:

Mistake 1) SystemClock.elapsedRealtime() versus System.currentTimeMillis()

If you are an old J2ME programmer probably the first option you can have in your mind to track your “uptime” is the System.currentTimeMillis(). However, if the user change the time/date the System.currentTimeMillis() is impacted!

The correct option on this case, is the SystemClock.elapsedRealtime() that will bring to you the total milliseconds after boot and it is not impacted with date/time.

Mistake 2) Wrong UPTIME

In the Mistake 1, you realized the importance of SystemClock.elapsedRealtime() but if your service need to auto-start when the devices boot, you should register the intent BOOT_COMPLETE.

So, it is very common to see developers using the UPTIME using directly the SystemClock.elapsedRealtime(), but remember! It is not only your service that starts after boot and of course, if UPTIME is something that must be precise your need to reduce the time waste to start your service.

So you need to do something like:

</pre>
long TIME_IN_MS_WHEN_STARTED = SystemClock.elapsedRealtime()
<pre>

then when you need to evaluate your uptime:

</pre>
long UPTIME = SystemClock.elapsedRealtime() - TIME_IN_MS_WHEN_STARTED
<pre>

I now, sounds ridiculous but it is very common mistake in real time systems.

Mistake 3) Using wakelocks instead RTC Alarms

If your system cannot be invasive, avoid the consume of the battery, but your need to monitor some inputs periodically, wakelocks is really the worst because will prevent your phone to sleep consuming a high current all the time. The best option on this case is to use the RTC of your device to wake up your service.

AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);

 Intent intent = new Intent(receiver.ACTION_REFRESH_SCHEDULE_ALARM);

 PendingIntent pi = PendingIntent.getBroadcast(getBaseContext(), 0, intent, 0);

// After after time_in_sec seconds
 am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000 * time_in_sec, pi);

Then to cancel the timer you need:

public void cancelTimer()
 {
 if (timerRunning) {
 timerRunning = false;
 Intent intent = new Intent(getBaseContext(), MyReceiver.class);
 PendingIntent sender = PendingIntent.getBroadcast(getBaseContext(), 0, intent, 0);
 AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
 alarmManager.cancel(sender);
 }

Mistake 4) No memory, crazy Dalvik killing everything and your service was not restarted !!! DAMNIT!! WHY ?

This is very tricky… there are situation that your system may be affect by “low memory” event, and the Dalvik decides to kill everything not in use including the background processes. Sometimes you will be able to the in logcat messages like “Too many background processes”, “Low memory”, and believe it is something you cannot control due to many variables in your device (sometimes many horrible software implementations installed in your system too).

When the Dalvik kills the services, as son we have decent memory available the Dalvik tried to restart everything again. Then you have a great surprise! Even you using a AIDL service, your client was not able to receive the UNBIND event, even a “service disconnect” error, and you are completely lost.

Why ? Well.. I tell a specific situation that I could realize some Dalvik implementation failing.

Suppose your service was using a periodically RTC event (like described on Mistake 3 previously) and then your service contains a receiver like the line:

Intent intent = new Intent(receiver.ACTION_REFRESH_SCHEDULE_ALARM);

Then, just after Dalvik killed you service, the RTC generates an event. Even your service already killed, the receiver will receive the intent. On this case, if you are accessing something already destroyed with your service, for example, an object that makes reference some memory in native side (NDK implementation) or your parcelable object are null at this time, your service will not be RESTARTED !!!

This is scenario that I could realize some implementation are failing. How to fix this issue ?

Make sure when your service is destroyed to cancelled the alarm:

</pre>
@Override
 public void onDestroy() {
 // TODO Auto-generated method stub

 cancelTimer();
 super.onDestroy();
 }
<pre>

and be precocious that in your receiver make sure the objects you are accessing are not null.

If you do this, the Dalvik will be able to re-start the service without problem.

Mistake 5) Saving all the time your data

If you need to save data, be reasonable.. Sometimes it is better to save in cache (RAM) and in specific amount of time save it. If you have summarized info to be save and the data is very few, try to use a light mechanism.. For example, SharedPreferences is lighter than SQL Lite on this situation.

Mistake 6) Problem with exceptions and tombstones

If your system access external devices and you have special NDK implementation on your system, make sure you are not masking any problem on your native side. For example:

</pre>
try {

 int result = my_obj.my_native_call();
 } catch (MySpecialException e) {
 Log.e(TAG, "Ops... a problem !!");
 } catch (Exception e1) {
 Log.e(TAG, "other problem caught!" + e1);

}
<pre>

If you have a problem in your native call, maybe none of the exceptions above will be caught but the failure is not on java context but in your native context.

On this case, if you did not insert decent logs messages on your native side, you will need to check the tomstones files.

You are able to see the tomstones dumps in your logcat also.

Thanks!

Leave a Reply