Battery-saver + phone-call-intent => no Internet?
Asked Answered
T

2

28

Note: as it turns out, the original question was incorrect in its assumptions. See more details on its edits at the bottom.

It's now about the battery-saver, and not battery-saver&doze-mode. It's also not about Service&BroadcastReceiver, but just BroadcastReceiver alone.

Background

Starting from Android Lollipop, Google introduced new, manual and automatic ways to help with battery saving:

"Doze" mode, and "Battery-saver".

On some cases, apps might not be able to access the Internet due to those techniques.

The problem

I work on an app that needs to access the Internet using a background service that triggers on specific cases, and if something important is being received, it shows some UI.

I've noticed, as a user, that on some cases, it fails to access the Internet.

The check of whether the app can access the Internet is as such:

public static boolean isInternetOn(Context context) {
    final NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
    return !(info == null || !info.isConnectedOrConnecting());
}

Problem is that I'm required to check why this sometimes returns false, so that if it fails, we should tell the user (via notification, probably) that the data cannot be accessed because the device restricted the app, and offer the user to white-list the app from battery optimization.

I'm not sure which ones affect this: Doze, battery saver, or both, and if it's always this way, for all devices, in all cases.

What I've tried

What I did find is how to query of Doze mode and Battery-saver (power saver) mode:

public class PowerSaverHelper {
    public enum PowerSaveState {
        ON, OFF, ERROR_GETTING_STATE, IRRELEVANT_OLD_ANDROID_API
    }

    public enum WhiteListedInBatteryOptimizations {
        WHITE_LISTED, NOT_WHITE_LISTED, ERROR_GETTING_STATE, IRRELEVANT_OLD_ANDROID_API
    }

    public enum DozeState {
        NORMAL_INTERACTIVE, DOZE_TURNED_ON_IDLE, NORMAL_NON_INTERACTIVE, ERROR_GETTING_STATE, IRRELEVANT_OLD_ANDROID_API
    }

    @NonNull
    public static DozeState getDozeState(@NonNull Context context) {
        if (VERSION.SDK_INT < VERSION_CODES.M)
            return DozeState.IRRELEVANT_OLD_ANDROID_API;
        final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (pm == null)
            return DozeState.ERROR_GETTING_STATE;
        return pm.isDeviceIdleMode() ? DozeState.DOZE_TURNED_ON_IDLE : pm.isInteractive() ? DozeState.NORMAL_INTERACTIVE : DozeState.NORMAL_NON_INTERACTIVE;
    }

    @NonNull
    public static PowerSaveState getPowerSaveState(@NonNull Context context) {
        if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
            return PowerSaveState.IRRELEVANT_OLD_ANDROID_API;
        final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (pm == null)
            return PowerSaveState.ERROR_GETTING_STATE;
        return pm.isPowerSaveMode() ? PowerSaveState.ON : PowerSaveState.OFF;
    }


    @NonNull
    public static WhiteListedInBatteryOptimizations getIfAppIsWhiteListedFromBatteryOptimizations(@NonNull Context context, @NonNull String packageName) {
        if (VERSION.SDK_INT < VERSION_CODES.M)
            return WhiteListedInBatteryOptimizations.IRRELEVANT_OLD_ANDROID_API;
        final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        if (pm == null)
            return WhiteListedInBatteryOptimizations.ERROR_GETTING_STATE;
        return pm.isIgnoringBatteryOptimizations(packageName) ? WhiteListedInBatteryOptimizations.WHITE_LISTED : WhiteListedInBatteryOptimizations.NOT_WHITE_LISTED;
    }

    //@TargetApi(VERSION_CODES.M)
    @SuppressLint("BatteryLife")
    @RequiresPermission(permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
    @Nullable
    public static Intent prepareIntentForWhiteListingOfBatteryOptimization(@NonNull Context context, @NonNull String packageName, boolean alsoWhenWhiteListed) {
        if (VERSION.SDK_INT < VERSION_CODES.M)
            return null;
        if (ContextCompat.checkSelfPermission(context, permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) == PackageManager.PERMISSION_DENIED)
            return null;
        final WhiteListedInBatteryOptimizations appIsWhiteListedFromPowerSave = getIfAppIsWhiteListedFromBatteryOptimizations(context, packageName);
        Intent intent = null;
        switch (appIsWhiteListedFromPowerSave) {
            case WHITE_LISTED:
                if (alsoWhenWhiteListed)
                    intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
                break;
            case NOT_WHITE_LISTED:
                intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).setData(Uri.parse("package:" + packageName));
                break;
            case ERROR_GETTING_STATE:
            case IRRELEVANT_OLD_ANDROID_API:
            default:
                break;
        }
        return intent;
    }

    /**
     * registers a receiver to listen to power-save events. returns true iff succeeded to register the broadcastReceiver.
     */
    @TargetApi(VERSION_CODES.M)
    public static boolean registerPowerSaveReceiver(@NonNull Context context, @NonNull BroadcastReceiver receiver) {
        if (VERSION.SDK_INT < VERSION_CODES.M)
            return false;
        IntentFilter filter = new IntentFilter();
        filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
        context.registerReceiver(receiver, filter);
        return true;
    }

}

I think I also found a way to check them out while being connected to the device:

battery saver:

./adb shell settings put global low_power [1|0]

Doze state:

./adb shell dumpsys deviceidle step [light|deep]

And :

./adb shell dumpsys deviceidle force-idle

The questions

In short I just want to know if the reason for not being able to access the Internet, is indeed because there is no Internet connection, or if the app just got currently restricted due to certain battery optimizations.

Only for the case of being restricted, I could warn the user that if it's ok with him, the app would be white listed so that it could still work the same.

Here are my questions regarding it:

  1. Which of the above prevent background services of apps to access the Internet? Do all of them cause it? Is it device-specific? Does "Interactive" affect it?

  2. What's "force-idle" for, if there is already a way to go to "light" and "deep" doze states? Is there also a way to reset doze mode back to normal? I tried multiple commands, but only restarting of the device really got it to reset back to normal...

  3. Does the BroadcastReceiver I created allow to check it correctly? Will it trigger in all cases that access to the Internet is denied due to all of the special cases? Is it true that I can't register to it in manifest?

  4. Is it possible to check if the reason for not being able to access the Internet, is indeed because there is no Internet connection, or if the app just got currently restricted due to certain battery optimizations?

  5. Have the restrictions of Internet connection for background services on special cases changed on Android O ? Maybe even more cases I should check?

  6. Suppose I change the service to run in foreground (with a notification), will this cover all cases, and always have access to the Internet, no matter what special state the device is in?


EDIT: it seems that it's not the service's fault at all, and that it occurs in battery saver mode too, without Doze mode.

The trigger to the service is a BroadcastReceiver that listens to phone calls events, and even if I check for Internet connection on its onReceive function, I see that it returns false. Same goes for the service that is started from it, even if it's a foreground service. Looking at the NetworkInfo result, it's "BLOCKED", and its state is indeed "DISCONNECTED".

Question now, is why this occurs.

Here's a new sample POC to check this out. To reproduce, you need to turn on battery saver mode (using ./adb shell settings put global low_power 1 command, or as a user), then launch it, accept the permissions, close activity, and call from another phone to this one. You will notice that on the activity, it shows there is Internet connection, and on the BroadcastReceiver, it says it doesn't.

Note that battery saver mode might be turned off automatically when connecting to USB cable, so you might need to try it when the device is not connected. Using the adb command prevents it, as opposed to the user-method of enabling it.

The sample project can also be found here, even though it was originally meant to be about Doze mode. Just use battery-saver mode instead, to see that the issue occurs.

PhoneBroadcastReceiver

public class PhoneBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, final Intent intent) {
        Log.d("AppLog", "PhoneBroadcastReceiver:isInternetOn:" + isInternetOn(context));
    }

    public static boolean isInternetOn(Context context) {
        final NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
        return !(info == null || !info.isConnectedOrConnecting());
    }
}

manifest

<manifest package="com.example.user.myapplication" xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>

    <application
        android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <receiver android:name=".PhoneBroadcastReceiver">
            <intent-filter >
                <action android:name="android.intent.action.PHONE_STATE"/>
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("AppLog", "MainActivity: isInternetOn:" + PhoneBroadcastReceiver.isInternetOn(this));
        if (VERSION.SDK_INT >= VERSION_CODES.M) {
            requestPermissions(new String[]{permission.READ_PHONE_STATE, permission.PROCESS_OUTGOING_CALLS}, 1);
        }
    }
}
Terenceterencio answered 31/8, 2017 at 8:12 Comment(14)
Please show the code that informs the user the background service is running.Perri
@Perri No need. The issue doesn't seem about the service anymore. It occurs straight in the PhoneBroadcastReceiver, which is the one that starts the service anyway (just use "startService" with any Service you create, if you insist). Right on PhoneBroadcastReceiver, if you check for Internet connection, you get that it's not available.Terenceterencio
FAIL RUNNING service without informing user.Perri
@Perri I don't understand what you mean.Terenceterencio
Just some quick questions, what is your targetsdk? Also, when you run your test, is the app in the foreground or background (by definition of this documentation developer.android.com/about/versions/oreo/…)? And finally, have you tried starting a foreground service from your broadcastreceiver and checking connection in the onStartCommand function?Andri
@PabloBaxter targetSdk is 25 but setting to 26 will produce same behavior, as it occurs on pre-Android-O as well. About the service, this was the original question. In the original code, the service (and we also tried foreground service) is started from the BroadcastReceiver , and fails there too to get Internet connection. BTW, in the onStartCommand, it doesn't matter if it's foreground or not, as this only helps later.Terenceterencio
Also noticed the reproducing steps are incorrect, so I've updated them.Terenceterencio
Please try the following first - when waiting for the call start foreground service just to check if it works then. When I was working on my app (music streaming player) it had the same issue, users reported that in energy saving mode they are unable to access internet. Once we ensured that all networks calls are done while foreground service is running, everything started working again. Additional question: does it matter if it is wifi connection or cellular data connection?Cetology
@Cetology I wrote that I tried on a Service too, including foreground service. You can try to modify the project that is attached here: issuetracker.google.com/issues/65395920 . It was originally about Doze mode, but it has the same issue with battery-saver mode. If you add there a service to be started from the broadcastreceiver, and do the check there (including if you have it a foreground service), you will notice the same results.Terenceterencio
@androiddeveloper If the battery saver you are using is an AppStore-downloaded app, delete it. I have had enough problems with these. They don't save your battery, it uses more of it. I had one on my Kindle Fire 7", and eventually it got to the point where my tablet locked up, and would NOT function without a factory reset. Just friendly adviceSapphira
@ChaseBarnes I'm talking about the built in feature of battery saver mode. Not a third party app.Terenceterencio
@androiddeveloper thank goodness. I would hate for you to encounter my problemSapphira
@ChaseBarnes Generally I prefer to avoid all tweakers/cleaners/boosters/anti-viruses on Android. They usually don't do much at all.Terenceterencio
@androiddeveloper Agreed. They are nothing but trouble, In my experience.Sapphira
A
4

So I downloaded your example app from the issue tracker, tested it the way you described, and found these results on a Nexus 5 running Android 6.0.1:


Conditions for test 1:

  • App not whitelisted
  • Battery saver mode set using adb shell settings put global low_power 1
  • Device connected via wireless using adb tcpip <port> and adb connect <ip>:<port>
  • BroadcastReceiver only, no Service

In this test, app functioned as you had mentioned:

App in background -

D/AppLog: PhoneBroadcastReceiver:isInternetOn:false
D/AppLog: PhoneBroadcastReceiver:isInternetOn:false

Conditions for test 2:

  • Same as test 1 with changes below

  • BroadcastReceiver starts Service (example below)

    public class PhoneService extends Service {
    
        public void onCreate() {
            super.onCreate();
            startForeground(1, new Notification.Builder(this)
                    .setSmallIcon(R.mipmap.ic_launcher_foreground)
                    .setContentTitle("Test title")
                    .setContentText("Test text")
                    .getNotification());
        }
    
        public int onStartCommand(Intent intent, int flags, int startId) {
            final String msg = "PhoneService:isInternetOn:" + isInternetOn(this);
            Log.d("AppLog", msg);
            Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
            return START_STICKY;
        }
    
        public static boolean isInternetOn(Context context) {
            final NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
            return !(info == null || !info.isConnectedOrConnecting());
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    

This test gave me the same results as above.


Conditions for test 3:

  • Same as test 2 with changes below
  • App is whitelisted from battery optimizations

App in background -

D/AppLog: PhoneService:isInternetOn:false
D/AppLog: PhoneService:isInternetOn:true

This test was interesting in that the first log didn't give me internet connection, but the second log did, which was about 4 seconds after the first, and well after it established the foreground service. When I ran the test the second time, both logs were true. This seems to indicate a delay between the startForeground function being called and the system putting the app into the foreground.


I even ran test 2 and 3 by using adb shell dumpsys deviceidle force-idle, and got similar results to test 3, where the first log wasn't connected, but all subsequent logs showed internet connection.

I believe this all functions as intended, since battery saver on the device states:

To help improve battery life, battery saver reduces your device's performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.

So unless you are currently using the app, or you whitelisted the app and have a foreground service running, you can expect no internet connection to be available for your app to use if it is in battery saver or Doze mode.


Edit #1

This may work as a different workaround vs using some timer to recheck for internet connection:

MyService.java

@Override
public void onCreate() {
    super.onCreate();
    Log.d("AppLog", "MyService:onCreate isInternetOn:" + PhoneBroadcastReceiver.isInternetOn(this));
    if (!PhoneBroadcastReceiver.isInternetOn(this)) {
        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
            final ConnectivityManager connectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(CONNECTIVITY_SERVICE);
            connectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                    .build(), new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    //Use this network object to perform network operations
                    connectivityManager.unregisterNetworkCallback(this);
                }
            });
        }
    }
}

This will either return immediately if there is a network connection you can use, or wait until there is a connection. From here you can probably just use a Handler to unregister if you don't receive any result after some time, although I'd probably just leave it active.


Edit #2

So this is what I was recommending. This is based off the answer you gave in the comments earlier (Android check internet connection):

@Override
public void onCreate() {
    super.onCreate();
    Log.d("AppLog", "MyService:onCreate isInternetOn:" + PhoneBroadcastReceiver.isInternetOn(this));
    if (!PhoneBroadcastReceiver.isInternetOn(this)) {
        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
            final ConnectivityManager connectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(CONNECTIVITY_SERVICE);
            connectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
                    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
                    .build(), new ConnectivityManager.NetworkCallback() {
                @Override
                public void onAvailable(Network network) {
                    isConnected(network); //Probably add this to a log output to verify this actually works for you
                    connectivityManager.unregisterNetworkCallback(this);
                }
            });
        }
    }
}

public static boolean isConnected(Network network) {
    
    if (network != null) {
        try {
            URL url = new URL("http://www.google.com/");
            HttpURLConnection urlc = (HttpURLConnection)network.openConnection(url);
            urlc.setRequestProperty("User-Agent", "test");
            urlc.setRequestProperty("Connection", "close");
            urlc.setConnectTimeout(1000); // mTimeout is in seconds
            urlc.connect();
            if (urlc.getResponseCode() == 200) {
                return true;
            } else {
                return false;
            }
        } catch (IOException e) {
            Log.i("warning", "Error checking internet connection", e);
            return false;
        }
    }

    return false;

}
Andri answered 27/9, 2017 at 20:24 Comment(27)
Can't reproduce test 3. When the test app is white listed from battery saver, it shows it has Internet connection, no matter where. Even on the PhoneBroadcastReceiver class. Maybe you should avoid using the adb-wireless option. Just disconnect, do the test, and connect to see the results on LogCat. Also, maybe try to enable battery-saver as a user (via the quick-settings)Terenceterencio
What I did notice, is that as you wrote, after a certain delay, the Internet is available. It also seems it does matter that it's a foreground service. Otherwise, it continues to tell there is no Internet connection.Terenceterencio
But isn't this a bug on the OS ? Is there a good workaround for this, that doesn't require white-listing?Terenceterencio
I've tested it further. I tried to use a BroadcastReceiver that listens to connectivity changes. It didn't work. However, it seems the delay of when the connection is available after you have the service created - is quite low. It's less than 100ms on HTC One M8 . I think that a nice workaround would be to try to have Internet connection, say, in the first second since the service runs, and if it's not available then, it means we probably really don't have Internet connection, or that we need to be white-listed. If you have a better workaround, please let me know.Terenceterencio
Newest project available here: issuetracker.google.com/issues/65395920#comment6Terenceterencio
Apparently, SO won't let me change the comment after 5 min. Basically look into the registerNetworkCallback in the ConnectivityManager class. Reason connectivity changes isn't working is probably due to this. I'll test it out later when I get some time, but just letting you know now in case you want to jump in on it.Andri
About registerNetworkCallback, I'm already aware of this. What I did was in runtime alone anyway, without manifest changes. You can see some remains of what I did in the project there, though it's not being used because it doesn't help.Terenceterencio
Downloaded the project and I can't reproduce the results of test 3. The device was restarted since yesterday, so I may have gotten bad results due to some weird device state. In regards to avoiding white-listing, I don't think there is any good option when it comes to battery-saver mode. You can probably put a listener for it, and pop-up the request to white-list, but that could be detrimental for a good user experience.Andri
You mean you couldn't reproduce it, just as I couldn't ?Terenceterencio
As for the workaround, I think I may have something that could work a little better. I've updated my answer to show the code and explanation.Andri
Yes, I couldn't reproduce the results from test 3. As for the workaround, I haven't fully tested it. My test device is on a different OS version now (for work), so I don't know if I'll get the chance to test it today.Andri
Doesn't seem to work for me. It does gets called to onAvailable, but when checking if Internet is available, I get 'false' as value. I tried to add all of the possible capabilities, but got "IllegalArgumentException: NetworkCapability out of range" . Maybe some of them are not intended to be used, but I don't see it on the docs.Terenceterencio
Have you tried using the network object passed to open a connection? NetworkInfo might still show no network connection, but the Network object might still work.Andri
I tried now, to use this code on the Network that I got : https://mcmap.net/q/11530/-android-check-internet-connection-duplicate , but it still says there is no Internet connection. BTW, I think the cause for the crash of multiple capabilities is because I used them as flags, instead of multiple calls (addCapability(...).addCapability() ) , as shown here: developer.android.com/reference/android/net/…Terenceterencio
I thought I've found a possible solution, in the form of WakeLock, but it was probably a temporary situation that gave me Internet connection for some reason. I'm really out of ideas of how to handle this correctly. Only solution for now is the workaround of polling after a very short time (a bit less than 100ms, usually) that there is Internet connection.Terenceterencio
For now I will accept this answer, as I'm out of ideas what can be a good workaround for this.Terenceterencio
I've added another edit to my answer. This was one option you can potentially use, rather than the NetworkInfo. Also, just use the capability NetworkCapabilities.NET_CAPABILITY_INTERNET.Andri
Not yet, installing Android 6 on my Nexus 5 device soon. Will let you know if this works for me in about an hour or so.Andri
Looks like it works for me, but I still need the white-listing and foreground service, although. Also looks like the fresh install made the timing for me less than 100ms too.Andri
What do you mean you need the "white-listing" ? The white listing of battery optimization is what I'm trying to avoid using (which is also why I wrote it even in the sample), because it alone makes everything work without any issue, even in background service or right on the broadcastReceiver... Also, the "isConnected" is supposed to be run on a background thread, because it makes a call to Internet...Anyway, I will check it out tomorrow (need 2 devices, and I don't have at home...).Terenceterencio
There might be something I'm missing, because without white-listing, I haven't been able to connect unless the app is visible. As for the updated answer, I probably should have handled the network call better, but that callback is on a background thread by default, unless you use the pendingintent or pass in your own handler.Andri
But not being white listed is the most important thing. If you white list your app, it always has Internet connection. Battery saver mode doesn't affect it. I didn't write that in order to reproduce the issue you need to white list the app. That won't help, because there wouldn't be anything to ask here.Terenceterencio
How did you know "onAvailable" isn't on the UI thread? Anyway, I just tested, and the "onAvailable" callback returns 'false' for both ways of checking if there is Internet connection (meaning there is no Internet connection).Terenceterencio
Looking at the 'activeNetwork', I see it says it is available, but not connected or connecting: "state: DISCONNECTED/BLOCKED, reason: (unspecified)". Here's the new project: issuetracker.google.com/issues/65395920#comment7Terenceterencio
Since I hate to see this bounty not given, while you've tried to help as much as you could, and found a workaround (the timed delay), I'm granting this bounty now. I will really be happy though, if you will find a better workaround and publish it here.Terenceterencio
It really doesn't feel right for me to accept this bounty without getting a good solution in. I'll keep working and updating the answer as I find something better. Is there any chance you can create a github repo with an updated solution, and post it on your question, so it can be quickly referenced?Andri
I prefer to put there something that all can use, and not a very "niche" question-project. For now, you can use the one I've attached on the issue-tracker...Terenceterencio
H
0

Google recommended way of handling this is using JobScheduler, or similar library (e.g. Firebase JobDispatcher), to schedule the job when there's network during one of the "maintenance windows". See Optimizing for Doze and App Standby for additional details. If you really need to execute your service out of this maintenance window, you should store the information on disk (DB, files...) and periodically synchronize it, or in a extreme case request to be whitelisted.

With that out of the way, let's go for your questions.

Which of the above prevent background services of apps to access the Internet? Do all of them cause it? Is it device-specific? Does "Interactive" affect it?

Doze mode for sure. Battery saving mode, it states that "Limits ... most background data", but I think is just a recommendation for applications. See here.

Specifically, notice:

RESTRICT_BACKGROUND_STATUS_ENABLED The user has enabled Data Saver for this app. Apps should make an effort to limit data usage in the foreground and gracefully handle restrictions to background data usage.

So, it looks like an advice, not something that it's enforced.

Also, notice that some phone manufacturers have battery saving applications that may impose additional restrictions and kill applications to save battery. See the following answer and discussion.

What's "force-idle" for, if there is already a way to go to "light" and "deep" doze states? Is there also a way to reset doze mode back to normal? I tried multiple commands, but only restarting of the device really got it to reset back to normal...

I don't have additional experience to share, aside from the documentation that you probably have already read in the "Testing" sections of this and this articles.

Does the BroadcastReceiver I created allow to check it correctly? Will it trigger in all cases that access to the Internet is denied due to all of the special cases? Is it true that I can't register to it in manifest?

Not completely sure if you will be able to check all the cases, but instead should try to follow the "synchronize when network is available" strategy if possible.

About registering in manifest, if your app is targeting Android Oreo, yes, you must register most of the receivers programmatically.

Is it possible to check if the reason for not being able to access the Internet, is indeed because there is no Internet connection, or if the app just got currently restricted due to certain battery optimizations?

The code you shared looks good, but I don't expect to be 100% sure, as sometimes multiple conditions can happen at the same time.

Have the restrictions of Internet connection for background services on special cases changed on Android O ? Maybe even more cases I should check?

Checks should be the same. Doze mode will be triggered in more cases than in Marshmallow, but the effects on the app should be exactly the same.

Suppose I change the service to run in foreground (with a notification), will this cover all cases, and always have access to the Internet, no matter what special state the device is in?

As I said before, in some devices (battery saver apps) the app will be killed, so it will probably not work. In stock Android, probably will rise some limitations, but I cannot confirm, because I haven't tested myself.

Heel answered 31/8, 2017 at 9:8 Comment(11)
JobScheduler doesn't fit the needs of the app on this case. The service must try to access the Internet "on the spot". About the questions, I don't understand then what you suggest to do in order to know the reason of no-internet connection.Terenceterencio
Then, I would take the whitelist route, and try to educate the users about the reasons to take that route. About my suggestion, basically I think that what you've already tried looks good, but I expect that maybe some really edge cases (like 3rd party battery savers) may not be catched. I don't have enough experience though to give you a more detailed explanation, sorry.Heel
I think I've found that battery saver mode also affects background services' access to the Internet. Is it possible? Can services access the internet even on these states somehow, without being whitelisted?Terenceterencio
I think is a recommendation, at least in 5.0, but maybe it is being enforced in later versions.Heel
Do foreground services also get affected by this Internet restriction?Terenceterencio
Checking the documentation again, foreground should not be affected, but it is advised to reduce network usage (e.g. requesting smaller resolution images to a server, doing fewer requests, etc.)Heel
Weird thing is that in POC, the background service can check for Internet connection fine, no matter what's its state, yet on the large app we work on at the office, it doesn't. We also tried to make it foreground (on the large app), but it didn't help. Do you know how to make a POC that mimics it well? What I did is just start a service that creates a thread that will check the Internet connection in 10 seconds. During this time, I close the activity to prevent it to be in foreground. Still, it says there is Internet connection...Terenceterencio
Without knowing it, I can just suggest a few things: Check that AndroidManifest.xml matches in relevant things like permissions, services, etc. Also check that you're targeting the same API levels (targetSdkVersion, also minSdkVersion, compileSdkVersion, buildToolsVersion). targetSdkVersion can change this kind of restrictions (e.g. targeting older versions allows some things to work in compatibility mode). Aside from this, it's difficult to say without looking at your apps code.Heel
Yes those don't seem the problem. I think the problem is from the trigger itself to start the service, which is phone calls events. These are caught from BroadcastReceiver, and even in this class itself (without the service), it returns false when checking if there is Internet connection. I've noticed that it has "android:priority="999"" in the manifest. Removing it seems to fix this issue, but why...Terenceterencio
Let us continue this discussion in chat.Heel
I've updated the question with a new sample code of BroadcastReceiver. Please let me know if you can find the reason for this. For some reason, in this POC, the priority doesn't make a difference.Terenceterencio

© 2022 - 2024 — McMap. All rights reserved.