Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to access Internet via WiFi from a Background service

I'll get straight onto some facts/figures I discovered pls help me if you've faced/solved a similar problem.

I send back data every 5 minutes to a server unless the user manually toggles it Off with the help of a wakeful broadcast receiver through a intent service. Plus I get wifi locks before doing (I've tried this already without locks; doesnt work either)

Now on devices sending back data from regular data networks (2G/3G/4G) this works fine but on those connected to a wifi network somehow gets me a getActiveNetworkInfo() as null (Even directly hitting the web URL gives a hostname not found error) this surely happens on HTC One (with v.4.4.2 installed) others devices do work fine.

  • we all know about the previous official connectivity manager have had issues in returning too much true / false condition even if network was not/available. I highly doubt if they've cropped up again or is just some custom OEM jing-bang

  • the alarm gets fired >> fires up the wakeful broadcast receiver >> get wifi locks >> sleep the thread for 3 secs inside theonReceive` after getting locks in order to wait for wifi to resume connectivity >> do my service work afterwards.

  • am weighing the possibility of forcing use of mobile data connection instead of wifi. (by using startUsingNetworkFeature()) Does anyone have a solution for the same?

like image 406
beerBear Avatar asked Jul 15 '14 06:07

beerBear


People also ask

Why does my Wi-Fi say no internet but it works?

If this ONLY happens when you are on WiFi, then the likely cause is that the WiFi router or WiFi zone has some sort of site blocking enabled OR you have a misconfigured DNS setting on the WiFi adapter.

Why won't my computer connect to Wi-Fi but others will?

The most common one relates to correctly authenticating with the target network. Other possible causes include faulty Wi-Fi network configuration, a malfunctioning modem or router, corrupt or failing network hardware or drivers, or antivirus software or firewall blocking your connection.

Why can't I connect to my Wi-Fi?

If your Android phone won't connect to Wi-Fi, you should first make sure that your phone isn't on Airplane Mode, and that Wi-Fi is enabled on your phone. If your Android phone claims it's connected to Wi-Fi but nothing will load, you can try forgetting the Wi-Fi network and then connecting to it again.


1 Answers

Hope I'm not too late, I had a problem just like yours. My app needed to send data (from internal storage file) to server at certain intervals (30m/1h/2h/4h). For getActiveNetworkInfo() to give proper result, you need to wake the wifi (because after 5-15 min of phone sleep wifi turns off). That gave me a lot of trouble, but here's how my solution works:

First, I have a WakefulBroadcastReceiver that gets called when I need to wake the phone:

/** Receiver for keeping the device awake */
public class WakeUpReceiver extends WakefulBroadcastReceiver {

    // Constant to distinguish request
    public static final int WAKE_TYPE_UPLOAD = 2;

    // AlarmManager to provide access to the system alarm services.
    private static AlarmManager alarm;
    // Pending intent that is triggered when the alarm fires.
    private static PendingIntent pIntent;

    /** BroadcastReceiver onReceive() method */
    @Override
    public void onReceive(Context context, Intent intent) {
        // Start appropriate service type
        int wakeType = intent.getExtras().getInt("wakeType");
        switch (wakeType) {
        case WAKE_TYPE_UPLOAD:
            Intent newUpload = new Intent(context, UploadService.class);
            startWakefulService(context, newUpload);
            break;
        default:
            break;
        }
    }

    /** Sets alarms */
    @SuppressLint("NewApi")
    public static void setAlarm(Context context, int wakeType, Calendar startTime) {
        // Set alarm to start at given time
        alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, WakeUpReceiver.class);
        intent.putExtra("wakeType", wakeType);
        pIntent = PendingIntent.getBroadcast(context, wakeType, intent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        // For android 4.4+ the method is different
        if (android.os.Build.VERSION.SDK_INT >= 
                android.os.Build.VERSION_CODES.KITKAT) {
            alarm.setExact(AlarmManager.RTC_WAKEUP, 
                    startTime.getTimeInMillis() + 5000, pIntent);
        } else {
            alarm.set(AlarmManager.RTC_WAKEUP, 
                    startTime.getTimeInMillis() + 5000, pIntent);
        }
        // The + 5000 is for adding symbolic 5 seconds to alarm start
    }
}

Note the public static void setAlarm(Context, int, Calendar) function, I use that function to set the alarms (see Main application).

Next, the service itself isn't an IntentService, just Service:

/** Service for uploading data to server */
public class UploadService extends Service {

    private static final String serverURI = "http://your.server.com/file_on_server.php";

    private Looper mServiceLooper;
    private ServiceHandler mServiceHandler;

    WifiLock wfl;

    private Intent currentIntent;

    private SharedPreferences sharedPrefs;
    private int updateInterval;
    private boolean continueService;

    /** Service onCreate() method */
    @Override
    public void onCreate() {
        super.onCreate();

        // Initialize wifi lock
        wfl = null;

        // Initialize current Intent
        currentIntent = null;

        // Create separate HandlerThread
        HandlerThread thread = new HandlerThread("SystemService",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        // Get the HandlerThread's Looper and use it for our Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);

        // Get shared variables values if set
        sharedPrefs = getSharedPreferences("serviceVars", MODE_PRIVATE);
        updateInterval = sharedPrefs.getInt("sendInterval", 60); // in your case 5
        continueService = sharedPrefs.getBoolean("bgServiceState", false);
    }

    /** Service onStartCommand() method */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // If continuing, set new alarm
        if (continueService) {
            Calendar nextStart = Calendar.getInstance();
            nextStart.set(Calendar.SECOND, 0);
            // Set alarm to fire after the interval time
            nextStart.add(Calendar.MINUTE, updateInterval);
            WakeUpReceiver.setAlarm(this, WakeUpReceiver.WAKE_TYPE_UPLOAD,
                    nextStart);
        }

        // Get current Intent and save it
        currentIntent = intent;

        // Acquire a wifi lock to ensure the upload process works
        WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
        wfl = wm.createWifiLock(WifiManager.WIFI_MODE_FULL, "WifiLock");
        if (!wfl.isHeld()) {
            wfl.acquire();
        }

        // For each start request, send a message to start a job and give
        // start ID so we know which request we're stopping when we finish
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        mServiceHandler.sendMessage(msg);
        // If service gets killed, it will restart
        return START_STICKY;
    }

    /** Handler that receives messages from the thread */
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        /** Executes all service operations */
        @Override
        public void handleMessage(Message msg) {
            // First wait for 5 seconds
            synchronized (this) {
                try {
                    wait(5 * 1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // Start checking if internet is enabled
            boolean hasInternet = false;
            long endTime = System.currentTimeMillis() + 55 * 1000;
            // Check every second (max 55) if connected
            while (System.currentTimeMillis() < endTime) {
                if (hasInternet(UploadService.this)) {
                    hasInternet = true;
                    break;
                }
                synchronized (this) {
                    try {
                        wait(1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            // Connected to internet or 60 (5 + 55) seconds passed
            if (hasInternet) {
                // Connect to server
                connectToServer(serverURI, fileName);
            } else {
                // Can't connect, send message or something
            }

            // Stop service
            stopSelf(msg.arg1);
        }
    }

    /** Checks if phone is connected to Internet */
    private boolean hasInternet(Context context) {
        ConnectivityManager cm = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo ni = null;
        if (cm != null) {
            ni = cm.getActiveNetworkInfo();
        }
        return ni != null && ni.getState() == NetworkInfo.State.CONNECTED;
    }

    /** Service onDestroy() method */
    @Override
    public void onDestroy() {
        // Release wifi lock
        if (wfl != null) {
            if (wfl.isHeld()) {
                wfl.release();
            }
        }

        // Release wake lock provided by BroadcastReceiver.
        WakeUpReceiver.completeWakefulIntent(currentIntent);

        super.onDestroy();
    }

    /** Performs server communication */
    private void connectToServer(String serverUri, String dataFileName) {
        // this function does my network stuff
    }

    /** Service onBind() method - Not used */
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

I also have a BOOT BroadcastReceiver, so if alarm is set and then phone is rebooted it will run service again:

/** Receiver for (re)starting alarms on device reboot operations */
public class BootReceiver extends BroadcastReceiver {
    WakeUpReceiver alarm = new WakeUpReceiver();

    /** BroadcastReceiver onReceive() method */
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
            WakeUpReceiver.setAlarm(context, WakeUpReceiver.WAKE_TYPE_UPLOAD,
                    Calendar.getInstance());
        }
    }
}

Last, but not least in the Main Activity, I start the service by starting receivers:

// Start upload service now
Calendar timeNow = Calendar.getInstance();
WakeUpReceiver.setAlarm(this, WakeUpReceiver.WAKE_TYPE_UPLOAD, timeNow);

// Enable BootReceiver to (re)start alarm on device restart
getPackageManager().setComponentEnabledSetting(
        new ComponentName(this, BootReceiver.class),
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP);

And when I stop the service, I stop the BOOT receiver too:

// Disable BootReceiver to (re)start alarm on device restart
getPackageManager().setComponentEnabledSetting(
        new ComponentName(this, BootReceiver.class),
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
        PackageManager.DONT_KILL_APP);

Additionally, don't forget to add proper permissions and components to manifest:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<receiver
    android:name=".BootReceiver"
    android:enabled="false" >
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
<receiver android:name=".WakeUpReceiver" />

<service
    android:name=".UploadService"
    android:enabled="true"
    android:label="ServerUpload"
    android:launchMode="singleInstance" />

I think I covered it all, I'll be glad if this helps anyone solve their problems.

like image 53
kiko283 Avatar answered Nov 04 '22 20:11

kiko283