Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Turn on the Internet on Android when in sleep

I have an Android app that needs to sync to the internet, but as soon as the phone goes to sleep I can't access the internet. It only happens when the user uses the "battery mode", when it turns off the data after 15 minutes. I wrote a test app and its turning the data on, but it still does connect to the server.

What I tried:

  • When I turn the data manually off, then the app is turning it on and it works
  • I also tried WakeLock, but it did not help.
  • The alarm works as expected, even when the phone goes to sleep for hours

Tested on Motorola Atrix Android 2.3.3. I can't rely on Wifi. In real life it will sync every week. How can we make it possible?

AlarmManager:

alarm_manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(this, 0, intent, 
                        PendingIntent.FLAG_UPDATE_CURRENT);
alarm_manager.setRepeating(AlarmManager.RTC_WAKEUP, 
                        System.currentTimeMillis(), 15000, pending);

AlarmReceiver:

public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("MYTAG", "RECEIVED getMobileDataEnabled: " + getMobileDataEnabled(context));  
        if (!isOnline(context)) {
            Log.d("MYTAG", "NO INET");
            if (turnOnInet(context)) {
                Log.d("MYTAG", "INET IS ON");
            }
        }

        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost("http://xxx.xxx.xxx.xxx/ping/pong/moto/");
            try {
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
                nameValuePairs.add(new BasicNameValuePair("short_code", "ROFL"));
                httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                httpclient.execute(httppost);
                Log.d("MYTAG", "POST FINISHED");
            }
            catch (Exception e) {
                Log.e("MYTAG", "MYTAG", e);
            }
    }

    public boolean isOnline(Context context) {
        ConnectivityManager cm = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo netInfo = cm.getActiveNetworkInfo();
        if (netInfo != null){
            Log.d("MYTAG", "isAvailable: "+netInfo.isAvailable());
        }
        if (netInfo != null && netInfo.isConnectedOrConnecting()) {
            return true;
        }
        return false;
    }

    public boolean turnOnInet(Context context) {
        ConnectivityManager mgr = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (mgr == null) {
            Log.d("MYTAG", "ConnectivityManager == NULL");
            return false;
        }
        try {
            Method setMobileDataEnabledMethod = mgr.getClass().getDeclaredMethod("setMobileDataEnabled", boolean.class);
            if (null == setMobileDataEnabledMethod) {
                Log.d("MYTAG", "setMobileDataEnabledMethod == null");
                return false;
            }    
            setMobileDataEnabledMethod.invoke(mgr, true);
        }
        catch(Exception e) {
            Log.e("MYTAG", "MYTAG", e);
            return false;
        }
        return true;
    }   


    private boolean getMobileDataEnabled(Context context) {
        ConnectivityManager mgr = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (mgr == null) {
            Log.d("MYTAG", "getMobileDataEnabled ConnectivityManager == null");
            return false;
        }
        try {
            Method method = mgr.getClass().getMethod("getMobileDataEnabled");
            return (Boolean) method.invoke(mgr);
        } catch (Exception e) {
            Log.e("MYTAG", "MYTAG", e);
            return false;
        }
    }
}

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
like image 349
programmersbook Avatar asked Aug 12 '12 23:08

programmersbook


3 Answers

First, you need to get that HttpPost code out of the BroadcastReceiver and into an IntentService. Never do network I/O on the main application thread, and onReceive() is called on the main application thread. For example, if you take too long, Android will terminate your code partway through your Internet operation.

Second, given the IntentService, you need to use a WakeLock. That may steer you to use my WakefulIntentService, which handles both problems. Or, use WakefulBroadcastReceiver, which has the same purpose.

Third, delete turnOnInet() and getMobileDataEnabled(). You do not need them, they are unreliable, and in particular turnOnInet() is user-hostile -- if the user wanted mobile data on, they would have turned it on.

Now, given all of that, in your onHandleIntent() of your IntentService() (or your doWakefulWork() of your WakefulIntentService), if you do not have an Internet connection right away, as a temporary workaround, SystemClock.sleep() for a second and try again, repeating a few times in a loop. If you find that you are getting Internet access after a bit, then you can consider getting more sophisticated (e.g., listening for connectivity change broadcasts rather than polling, though this would drive you away from WakefulIntentService and into a regular Service with your own background thread and a state machine for WakeLock management). Or, just stick with the sleep() -- it's unlikely to be the end of the world if you tie up this background thread for a few seconds. If you do not get connectivity after a modest amount of time, though, please do not keep trying indefinitely, as there are any number of reasons why you might not get a connection, including user-driven bandwidth management on Android 4.0+.

like image 103
CommonsWare Avatar answered Oct 20 '22 03:10

CommonsWare


I'd suggest to change a bit that approach, which is not bad at all, it's even nice to make sure that you are always synced, with the only problem that you are not giving the user the chance to decide, since if I, as an user, decide to turn off my data I just don't want anybody to turn it on. There could be several reasons for that, and any of them should be enough, but say that you get out of the country and you have no international data plan and by accident or default you have Data roaming activated. If I'd discover that some certain app turned my data on and spent a sensitive amount of money I'd be pretty pissed, and I'll be personal.

A more proper approach and straight solution as well would be to make a hard/full sync from time to time whenever the user opens your app or has access to some wifi connection (ConnectivityManager is your friend) based on some easy conditions (last time sync longer than a week, outdated saved data, inconsistencies, etc) and make a soft sync (update data in the background) in the rest of cases.

Moreover syncing periodically means wasting user data in case that user doesn't use the app. Ultimately, this turns your app into perfect candidate to be shut down by the system every once in a while.

Hope it helps. Keep us updated with your progress.

Related read: Optimizing downloads for efficient network access

like image 5
Jose L Ugia Avatar answered Oct 20 '22 03:10

Jose L Ugia


Not an exact answer - but this approach would absolutely ruin the battery life. The whole point of sleep is to save battery power and this app would do nothing more than be an annoyance for customers no matter how useful the features are.

What I would suggest is that if it is absolutely necessary to connect to the internet while the app is not in use - set a trigger for when the phone wakes up. If it is not completely necessary it probably would be best to simply reconnect to the internet every time the app is opened.

like image 1
SamuelDavis Avatar answered Oct 20 '22 01:10

SamuelDavis