7

A am working on an Android app which connects to a local flask server using WiFi. Then, the app displays images which are stored on the server (RPi3). A click on an image triggers a download request and I want the Android DownloadManager to enqueue the request and download the image. The WiFi network does not provide internet access.

So far, I have been able to test it on Android 6 and Android 8.1 devices and everything works fine. Testing on several Android 9 devices, the download does not start, but after disconnecting from the local network the failed attempts are shown.

Reading other threads about this, I have tried the following:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

And added in the AndroidManifest.xml:

android:networkSecurityConfig="@xml/network_security_config"
  • Binding the network as described in the first answer here: Using a WiFi without Internet Connection

  • Playing with the options the DownloadManager.Request class provides, but they do not seem related to this problem:

.setRequiresCharging(false)
.setAllowedOverMetered(false)
.setAllowedOverRoaming(false)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, (String) url);
  • Checking again permissions. These are enabled:
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <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.CHANGE_NETWORK_STATE" />

This is the download request I create:

DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url))
        .setTitle("Some Title")
        .setDescription("Downloading a file")
        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
        .setDestinationInExternalPublicDir("some_path", (String) url)
        .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
DownloadManager downloadManager= (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
downloadID = downloadManager.enqueue(request);

The Flask server (Python3) uses this the return the image:

    @app.route('/getFullImage/<image_file>')
    def get_full_image(image_file):
        log.debug("Client requests full image.")
        folder = os.path.join(app.root_path, "media_images")

        return flask.send_from_directory(directory=folder, filename=image_file, as_attachment=True)

As said, on Android 6 and 8.1, it worked fine but none of the Android 9 devices start the download. If there is any setting or method I do not know of, I would be happy to learn about it.

Thank you.

jlee7
  • 126
  • 8
  • Have the same problem for android 9 pie. It cannot connect to my server because the wifi does not have internet connection. Any luck with this? – Lance Aug 06 '19 at 14:25
  • @Lance: So far, I haven't figured out a way to get this work. My work-around is to use another library (RetroFit) for the request and download it like this. Let me know, if you figure it out. :) – jlee7 Aug 06 '19 at 16:13
  • How did retrofit solve the problem? What is "this" when you say download it like this? – Lance Aug 06 '19 at 17:53
  • My problem is somehow similar but I'm not downlaoding a file, I'm doing a get request from a service that's connected to a wifi network that doesn't have internet. It actually work when I turn off the cellular but when the cellular is turned on, I can't do any request. So I guess that's somehow similar to your problem. I found a workaround but I'm still testing it. Will post the answer when all my test are complete. But I was able to connect to the wifi network even if it has no internet. – Lance Aug 06 '19 at 17:57
  • I used to have a connection to the wifi network, but was not able to perform the download. Android always tried to use the cellular network. When I used RetroFit for the request, I was able to use the wifi network. – jlee7 Aug 06 '19 at 20:02
  • 1
    HOWEVER, after a few weeks of not working ob that project, I actually have problems with just connecting to the wifi network. So, our problems are indeed similar! Keen to know your solution! :) – jlee7 Aug 06 '19 at 20:04
  • 1
    @Lance Would it be possible to sketch your workaround for connecting to the network? – jlee7 Aug 13 '19 at 07:11
  • see the answer. – Lance Aug 20 '19 at 21:29
  • Guys, I have the same problem but on android >= 7. Can you help? https://stackoverflow.com/questions/58287054/download-manager-not-working-without-internet-access – Adam Mrozek Oct 09 '19 at 08:07
  • Hey Adam, I haven't figured out how to use the native download manager in my case. Using RetroFit works however. Please let me know if anyone comes up with a good idea. – jlee7 Oct 11 '19 at 23:53

1 Answers1

2

The key to this problem is to bind the process to the network. This allows your app to use wifi even if the wifi has no internet connectivity.

Here's my code, it's in kotlin but you can also convert it to Java

Just call this anywhere before trying to do a request. For me I placed this in the initialization of a singleton class that I use as network utility.

fun bindProcessToNetwork() {
    // Unbind the process from the network
    getConnectivityManager().bindProcessToNetwork(null)

    // Bind the process to the network
    getConnectivityManager().bindProcessToNetwork(getWifiNetwork())
}

These are the other methods I used in bindProcessToNetwork. You'll notice that I used a for loop to iterate through all network and try to determine which one is wifi instead of using connectionManager.activeNetworkInfo because that will return the cellular network when wifi has no internet connectivity.

private fun getConnectivityManager() : ConnectivityManager {
    val context = ApplicationContextProvider.getContext()
    return context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
}

private fun getWifiNetwork(): Network? {
    val networks = getConnectivityManager().allNetworks
    for (network in networks) {
        if (isNetworkWifi(network)) {
            return network
        }
    }
    return null
}

private fun isNetworkWifi(network: Network): Boolean {
    return this.getConnectivityManager().getNetworkCapabilities(network)
        .hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
}

You can read the documentation for WifiManager and ConnectivityManager for more information. There's a lot that has been deprecated in Level 28 (Android 9). Make sure you only use this code in Android 9 or greater.

if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P){
    bindProcessToNetwork(); 
}
Lance
  • 2,774
  • 4
  • 37
  • 57
  • 1
    Hi Lance, apoligies for the late reply. Dur to other projects, I wasn't able to verify your answer, however if it turns out to be for what I was looking for, I will surely mark it as solved. :) Thank you very much! – jlee7 Aug 30 '19 at 23:14
  • This solution doesn't work for me. My own process still has connectivity via Wifi (even if I don't bind anything). Maybe the DownloadManager is running on another process where we can't affect the bound network by using bindProcessToNetwork(). – Alexey Ozerov Mar 16 '20 at 05:12