From 2c594cae5d7c6108c7004d149921de6ee65bfeed Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 27 May 2014 11:46:31 -0400 Subject: [PATCH 01/10] Eclipse: hide warnings from submodules There are about 4000 warnings from all the included submodules included as symlinks. This hides them all so Eclipse only shows the warnings for FDroid itself. --- .classpath | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.classpath b/.classpath index fadbb9f4b..b6c82120d 100644 --- a/.classpath +++ b/.classpath @@ -4,7 +4,11 @@ - + + + + + From c4b059502c87e87def5d81ddacc3649018ce639d Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 27 May 2014 18:37:06 -0400 Subject: [PATCH 02/10] better LocalRepoService singleton enforcement We only ever want a single LocalRepoService. Use the values returned by the standard methods for start/stop and bind/unbind as the test for whether the Service is indeed running. --- src/org/fdroid/fdroid/FDroidApp.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index e5d8c13fe..1190f02be 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -282,17 +282,21 @@ public class FDroidApp extends Application { public static void startLocalRepoService(Context context) { if (!localRepoServiceIsBound) { Context app = context.getApplicationContext(); - app.bindService(new Intent(app, LocalRepoService.class), - serviceConnection, Context.BIND_AUTO_CREATE); - localRepoServiceIsBound = true; + Intent service = new Intent(app, LocalRepoService.class); + localRepoServiceIsBound = app.bindService(service, serviceConnection, + Context.BIND_AUTO_CREATE); + if (localRepoServiceIsBound) + app.startService(service); } } public static void stopLocalRepoService(Context context) { + Context app = context.getApplicationContext(); if (localRepoServiceIsBound) { - context.getApplicationContext().unbindService(serviceConnection); + app.unbindService(serviceConnection); localRepoServiceIsBound = false; } + app.stopService(new Intent(app, LocalRepoService.class)); } public static void restartLocalRepoService() { From fdaa2ad1065c2ffd696f6b09b3b5da1356ce9853 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 27 May 2014 18:39:44 -0400 Subject: [PATCH 03/10] update IP-related settings whenever the HTTPS pref changes WifiStateChangeService handles updating lots of IP-related things, then things that depend on it listen to the broadcast from that Service. The most straightforward way to update HTTPS or HTTP throughout the app is to trigger this Service. It runs its stuff in an AsyncTask so it is all low priority. --- src/org/fdroid/fdroid/FDroidApp.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index 1190f02be..2fc8b9506 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -41,6 +41,7 @@ import com.nostra13.universalimageloader.utils.StorageUtils; import de.duenndns.ssl.MemorizingTrustManager; +import org.fdroid.fdroid.Preferences.ChangeListener; import org.fdroid.fdroid.compat.PRNGFixes; import org.fdroid.fdroid.data.AppProvider; import org.fdroid.fdroid.data.InstalledAppCacheUpdater; @@ -213,6 +214,13 @@ public class FDroidApp extends Application { if (wifiState == WifiManager.WIFI_STATE_ENABLING || wifiState == WifiManager.WIFI_STATE_ENABLED) startService(new Intent(this, WifiStateChangeService.class)); + // if the HTTPS pref changes, then update all affected things + Preferences.get().registerLocalRepoHttpsListeners(new ChangeListener() { + @Override + public void onPreferenceChange() { + startService(new Intent(FDroidApp.this, WifiStateChangeService.class)); + } + }); } @TargetApi(18) From ba028408a124bf3af2ccdb7680cb7fe5c63020bb Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 27 May 2014 18:50:34 -0400 Subject: [PATCH 04/10] registerMDNSService() overwrites any existing registration There is only ever a single service to advertise via mDNS, so when a new registration is requested, remove any existing ones. This should eliminate these stacktraces: java.lang.IllegalStateException: A service information can only be registered with a single instamce of JmDNS. at javax.jmdns.impl.JmDNSImpl.registerService(JmDNSImpl.java:1005) at org.fdroid.fdroid.localrepo.LocalRepoService$5.run(LocalRepoService.java:239) at java.lang.Thread.run(Thread.java:856) --- src/org/fdroid/fdroid/localrepo/LocalRepoService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java index fc182352d..590a3471d 100644 --- a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java +++ b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java @@ -216,6 +216,12 @@ public class LocalRepoService extends Service { } private void registerMDNSService() { + /* + * a ServiceInfo can only be registered with a single instance of JmDNS, + * and there is only ever a single LocalHTTPD port to advertise anyway. + */ + if (pairService != null || jmdns != null) + clearCurrentMDNSService(); String repoName = Preferences.get().getLocalRepoName(); final HashMap values = new HashMap(); values.put("path", "/fdroid/repo"); @@ -249,6 +255,10 @@ public class LocalRepoService extends Service { Preferences.get().unregisterLocalRepoBonjourListeners(localRepoBonjourChangeListener); localRepoBonjourChangeListener = null; } + clearCurrentMDNSService(); + } + + private void clearCurrentMDNSService() { if (jmdns != null) { if (pairService != null) { jmdns.unregisterService(pairService); From f51d192e13e7b9637a1863c6029309b6fb6d5028 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 27 May 2014 21:42:30 -0400 Subject: [PATCH 05/10] move all mDNS service creation into the Thread Upon looking at this, ServiceInfo.create() also might take a while to run so might as well run it in the Thread with the other JmDNS stuff. --- .../fdroid/localrepo/LocalRepoService.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java index 590a3471d..d05826b83 100644 --- a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java +++ b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java @@ -216,31 +216,31 @@ public class LocalRepoService extends Service { } private void registerMDNSService() { - /* - * a ServiceInfo can only be registered with a single instance of JmDNS, - * and there is only ever a single LocalHTTPD port to advertise anyway. - */ - if (pairService != null || jmdns != null) - clearCurrentMDNSService(); - String repoName = Preferences.get().getLocalRepoName(); - final HashMap values = new HashMap(); - values.put("path", "/fdroid/repo"); - values.put("name", repoName); - values.put("fingerprint", FDroidApp.repo.fingerprint); - String type; - if (Preferences.get().isLocalRepoHttpsEnabled()) { - values.put("type", "fdroidrepos"); - type = "_https._tcp.local."; - } else { - values.put("type", "fdroidrepo"); - type = "_http._tcp.local."; - } - pairService = ServiceInfo.create(type, repoName, FDroidApp.port, 0, 0, values); new Thread(new Runnable() { - @Override public void run() { + /* + * a ServiceInfo can only be registered with a single instance + * of JmDNS, and there is only ever a single LocalHTTPD port to + * advertise anyway. + */ + if (pairService != null || jmdns != null) + clearCurrentMDNSService(); + String repoName = Preferences.get().getLocalRepoName(); + HashMap values = new HashMap(); + values.put("path", "/fdroid/repo"); + values.put("name", repoName); + values.put("fingerprint", FDroidApp.repo.fingerprint); + String type; + if (Preferences.get().isLocalRepoHttpsEnabled()) { + values.put("type", "fdroidrepos"); + type = "_https._tcp.local."; + } else { + values.put("type", "fdroidrepo"); + type = "_http._tcp.local."; + } try { + pairService = ServiceInfo.create(type, repoName, FDroidApp.port, 0, 0, values); jmdns = JmDNS.create(); jmdns.registerService(pairService); } catch (IOException e) { From 91e06b8496f78df05ebde39e9c06ef7dda11f25e Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 28 May 2014 15:29:04 -0400 Subject: [PATCH 06/10] new Wifi event cancels active processing of previous event If a new "wifi connected" event comes in while a previous one is still being processed, then cancel the current one as soon as possible. This prevents the events from being processed in an interleaved manner, causing chaos and crashes. Hopefully this will fix the jmdns crashes, since that is triggered by onPostExecute() via FDroidApp.restartLocalRepoService(). java.lang.IllegalStateException: A service information can only be registered with a single instamce of JmDNS. at javax.jmdns.impl.JmDNSImpl.registerService(JmDNSImpl.java:1005) at org.fdroid.fdroid.localrepo.LocalRepoService$5.run(LocalRepoService.java:239) at java.lang.Thread.run(Thread.java:856) --- .../fdroid/net/WifiStateChangeService.java | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/org/fdroid/fdroid/net/WifiStateChangeService.java b/src/org/fdroid/fdroid/net/WifiStateChangeService.java index c24271ff9..6b4fda481 100644 --- a/src/org/fdroid/fdroid/net/WifiStateChangeService.java +++ b/src/org/fdroid/fdroid/net/WifiStateChangeService.java @@ -23,9 +23,14 @@ import java.util.Locale; public class WifiStateChangeService extends Service { public static final String BROADCAST = "org.fdroid.fdroid.action.WIFI_CHANGE"; + private static WaitForWifiAsyncTask asyncTask; + @Override public int onStartCommand(Intent intent, int flags, int startId) { - new WaitForWifiAsyncTask().execute(); + if (asyncTask != null) + asyncTask.cancel(true); + asyncTask = new WaitForWifiAsyncTask(); + asyncTask.execute(); return START_NOT_STICKY; } @@ -38,15 +43,21 @@ public class WifiStateChangeService extends Service { wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); try { while (!wifiManager.isWifiEnabled()) { + if (isCancelled()) + return null; Log.i(TAG, "waiting for the wifi to be enabled..."); - Thread.sleep(3000); + Thread.sleep(1000); } int ipAddress = wifiManager.getConnectionInfo().getIpAddress(); while (ipAddress == 0) { + if (isCancelled()) + return null; Log.i(TAG, "waiting for an IP address..."); - Thread.sleep(3000); + Thread.sleep(1000); ipAddress = wifiManager.getConnectionInfo().getIpAddress(); } + if (isCancelled()) + return null; WifiInfo wifiInfo = wifiManager.getConnectionInfo(); ipAddress = wifiInfo.getIpAddress(); FDroidApp.ipAddressString = String.format(Locale.ENGLISH, "%d.%d.%d.%d", @@ -67,6 +78,9 @@ public class WifiStateChangeService extends Service { FDroidApp.repo.address = String.format(Locale.ENGLISH, "%s://%s:%d/fdroid/repo", scheme, FDroidApp.ipAddressString, FDroidApp.port); + if (isCancelled()) + return null; + Context context = WifiStateChangeService.this.getApplicationContext(); LocalRepoKeyStore localRepoKeyStore = LocalRepoKeyStore.get(context); Certificate localCert = localRepoKeyStore.getCertificate(); @@ -75,6 +89,9 @@ public class WifiStateChangeService extends Service { lrm.setUriString(FDroidApp.repo.address); lrm.writeIndexPage(Utils.getSharingUri(context, FDroidApp.repo).toString()); + if (isCancelled()) + return null; + /* * Once the IP address is known we need to generate a self * signed certificate to use for HTTPS that has a CN field set From 910f9a68a6e0167867c94ca6cc3a8d7484d2e2c2 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 27 May 2014 21:45:09 -0400 Subject: [PATCH 07/10] make DownloaderFactory for creating any kind of Downloader This will ultimately be used to create the right Downloader subclass instance based on the URL of the file to download (i.e. rfcomm://, .onion address, ssh://, new socket protocols, etc). Also delete unused constructors, they can trivially be readded if they are ever used, and they are currently just clutter. --- src/org/fdroid/fdroid/net/ApkDownloader.java | 4 ++-- src/org/fdroid/fdroid/net/Downloader.java | 9 ++++---- .../fdroid/fdroid/net/DownloaderFactory.java | 20 ++++++++++++++++ src/org/fdroid/fdroid/net/HttpDownloader.java | 23 ++++--------------- .../fdroid/fdroid/updater/RepoUpdater.java | 4 ++-- 5 files changed, 33 insertions(+), 27 deletions(-) create mode 100644 src/org/fdroid/fdroid/net/DownloaderFactory.java diff --git a/src/org/fdroid/fdroid/net/ApkDownloader.java b/src/org/fdroid/fdroid/net/ApkDownloader.java index baddfd5ef..c8c6bcc15 100644 --- a/src/org/fdroid/fdroid/net/ApkDownloader.java +++ b/src/org/fdroid/fdroid/net/ApkDownloader.java @@ -22,6 +22,7 @@ package org.fdroid.fdroid.net; import android.os.Bundle; import android.util.Log; + import org.fdroid.fdroid.Hasher; import org.fdroid.fdroid.ProgressListener; import org.fdroid.fdroid.data.Apk; @@ -172,8 +173,7 @@ public class ApkDownloader implements AsyncDownloadWrapper.Listener { Log.d(TAG, "Downloading apk from " + remoteAddress); try { - - Downloader downloader = new HttpDownloader(remoteAddress, localFile); + Downloader downloader = DownloaderFactory.create(remoteAddress, localFile); dlWrapper = new AsyncDownloadWrapper(downloader, this); dlWrapper.download(); return true; diff --git a/src/org/fdroid/fdroid/net/Downloader.java b/src/org/fdroid/fdroid/net/Downloader.java index 8c9f7cf77..4f6ee766d 100644 --- a/src/org/fdroid/fdroid/net/Downloader.java +++ b/src/org/fdroid/fdroid/net/Downloader.java @@ -30,24 +30,23 @@ public abstract class Downloader { public abstract InputStream inputStream() throws IOException; // The context is required for opening the file to write to. - public Downloader(String destFile, Context ctx) + Downloader(String destFile, Context ctx) throws FileNotFoundException, MalformedURLException { this(new File(ctx.getFilesDir() + File.separator + destFile)); } // The context is required for opening the file to write to. - public Downloader(Context ctx) throws IOException { + Downloader(Context ctx) throws IOException { this(File.createTempFile("dl-", "", ctx.getCacheDir())); } - public Downloader(File destFile) + Downloader(File destFile) throws FileNotFoundException, MalformedURLException { - // http://developer.android.com/guide/topics/data/data-storage.html#InternalCache outputFile = destFile; outputStream = new FileOutputStream(outputFile); } - public Downloader(OutputStream output) + Downloader(OutputStream output) throws MalformedURLException { outputStream = output; outputFile = null; diff --git a/src/org/fdroid/fdroid/net/DownloaderFactory.java b/src/org/fdroid/fdroid/net/DownloaderFactory.java new file mode 100644 index 000000000..8206db94b --- /dev/null +++ b/src/org/fdroid/fdroid/net/DownloaderFactory.java @@ -0,0 +1,20 @@ + +package org.fdroid.fdroid.net; + +import android.content.Context; + +import java.io.File; +import java.io.IOException; + +public class DownloaderFactory { + + public static Downloader create(String url, Context context) + throws IOException { + return new HttpDownloader(url, context); + } + + public static Downloader create(String url, File destFile) + throws IOException { + return new HttpDownloader(url, destFile); + } +} diff --git a/src/org/fdroid/fdroid/net/HttpDownloader.java b/src/org/fdroid/fdroid/net/HttpDownloader.java index 177fa6de1..7f5cc8e96 100644 --- a/src/org/fdroid/fdroid/net/HttpDownloader.java +++ b/src/org/fdroid/fdroid/net/HttpDownloader.java @@ -7,11 +7,9 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; -import java.net.UnknownHostException; import javax.net.ssl.SSLHandshakeException; @@ -26,14 +24,7 @@ public class HttpDownloader extends Downloader { private int statusCode = -1; // The context is required for opening the file to write to. - public HttpDownloader(String source, String destFile, Context ctx) - throws FileNotFoundException, MalformedURLException { - super(destFile, ctx); - sourceUrl = new URL(source); - } - - // The context is required for opening the file to write to. - public HttpDownloader(String source, File destFile) + HttpDownloader(String source, File destFile) throws FileNotFoundException, MalformedURLException { super(destFile); sourceUrl = new URL(source); @@ -42,19 +33,14 @@ public class HttpDownloader extends Downloader { /** * Downloads to a temporary file, which *you must delete yourself when * you are done*. - * @see org.fdroid.fdroid.net.HttpDownloader#getFile() + * @see org.fdroid.fdroid.net.Downloader#getFile() */ - public HttpDownloader(String source, Context ctx) throws IOException { + HttpDownloader(String source, Context ctx) throws IOException { super(ctx); sourceUrl = new URL(source); } - public HttpDownloader(String source, OutputStream output) - throws MalformedURLException { - super(output); - sourceUrl = new URL(source); - } - + @Override public InputStream inputStream() throws IOException { return connection.getInputStream(); } @@ -93,6 +79,7 @@ public class HttpDownloader extends Downloader { } } + @Override public boolean isCached() { return wantToCheckCache() && statusCode == 304; } diff --git a/src/org/fdroid/fdroid/updater/RepoUpdater.java b/src/org/fdroid/fdroid/updater/RepoUpdater.java index eab4291d7..7cb788ed3 100644 --- a/src/org/fdroid/fdroid/updater/RepoUpdater.java +++ b/src/org/fdroid/fdroid/updater/RepoUpdater.java @@ -13,7 +13,7 @@ import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.net.Downloader; -import org.fdroid.fdroid.net.HttpDownloader; +import org.fdroid.fdroid.net.DownloaderFactory; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; @@ -89,7 +89,7 @@ abstract public class RepoUpdater { protected Downloader downloadIndex() throws UpdateException { Downloader downloader = null; try { - downloader = new HttpDownloader(getIndexAddress(), context); + downloader = DownloaderFactory.create(getIndexAddress(), context); downloader.setCacheTag(repo.lastetag); if (progressListener != null) { // interactive session, show progress From b6197166694b303372a8c74a7a940e00fafd3ae2 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 28 May 2014 00:11:30 -0400 Subject: [PATCH 08/10] add TorHttpDownloader for handling repos on Tor Hidden Services Tor Hidden Services are on domain names that always end in .onion, so there is a URL pattern matcher that chooses which Downloader subclass to use based on testing for .onion. This is a quick, dumb implementation. It does make any attempt to see if Tor is running or even installed. That can come once NetCipher is easy to handle in the context of FDroid. refs #2367 https://dev.guardianproject.info/issues/2367 --- CHANGELOG.md | 2 + src/org/fdroid/fdroid/net/Downloader.java | 3 ++ .../fdroid/fdroid/net/DownloaderFactory.java | 16 ++++++- src/org/fdroid/fdroid/net/HttpDownloader.java | 44 +++++++++--------- .../fdroid/fdroid/net/TorHttpDownloader.java | 45 +++++++++++++++++++ 5 files changed, 87 insertions(+), 23 deletions(-) create mode 100644 src/org/fdroid/fdroid/net/TorHttpDownloader.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ea7c7e0d..3943d2260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ * find local repos on the same network using Bonjour/mDNS +* use FDroid repos on Tor Hidden Services (.onion addresses) + * directly send installed apps to other devices via Bluetooth and Android Beam (NFC+Bluetooth), also compatible with Samsung/HTC S-Beam diff --git a/src/org/fdroid/fdroid/net/Downloader.java b/src/org/fdroid/fdroid/net/Downloader.java index 4f6ee766d..4a9b96899 100644 --- a/src/org/fdroid/fdroid/net/Downloader.java +++ b/src/org/fdroid/fdroid/net/Downloader.java @@ -14,6 +14,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; +import java.net.URL; public abstract class Downloader { @@ -23,6 +24,8 @@ public abstract class Downloader { private ProgressListener progressListener = null; private Bundle eventData = null; private File outputFile; + + protected URL sourceUrl; protected String cacheTag = null; public static final String EVENT_PROGRESS = "downloadProgress"; diff --git a/src/org/fdroid/fdroid/net/DownloaderFactory.java b/src/org/fdroid/fdroid/net/DownloaderFactory.java index 8206db94b..df0ab415b 100644 --- a/src/org/fdroid/fdroid/net/DownloaderFactory.java +++ b/src/org/fdroid/fdroid/net/DownloaderFactory.java @@ -10,11 +10,23 @@ public class DownloaderFactory { public static Downloader create(String url, Context context) throws IOException { - return new HttpDownloader(url, context); + if (isOnionAddress(url)) { + return new TorHttpDownloader(url, context); + } else { + return new HttpDownloader(url, context); + } } public static Downloader create(String url, File destFile) throws IOException { - return new HttpDownloader(url, destFile); + if (isOnionAddress(url)) { + return new TorHttpDownloader(url, destFile); + } else { + return new HttpDownloader(url, destFile); + } + } + + private static boolean isOnionAddress(String url) { + return url.matches("^[a-zA-Z0-9]+://[^/]+\\.onion/.*"); } } diff --git a/src/org/fdroid/fdroid/net/HttpDownloader.java b/src/org/fdroid/fdroid/net/HttpDownloader.java index 7f5cc8e96..50fc41a65 100644 --- a/src/org/fdroid/fdroid/net/HttpDownloader.java +++ b/src/org/fdroid/fdroid/net/HttpDownloader.java @@ -16,11 +16,10 @@ import javax.net.ssl.SSLHandshakeException; public class HttpDownloader extends Downloader { private static final String TAG = "org.fdroid.fdroid.net.HttpDownloader"; - private static final String HEADER_IF_NONE_MATCH = "If-None-Match"; - private static final String HEADER_FIELD_ETAG = "ETag"; + protected static final String HEADER_IF_NONE_MATCH = "If-None-Match"; + protected static final String HEADER_FIELD_ETAG = "ETag"; - private URL sourceUrl; - private HttpURLConnection connection; + protected HttpURLConnection connection; private int statusCode = -1; // The context is required for opening the file to write to. @@ -54,28 +53,31 @@ public class HttpDownloader extends Downloader { @Override public void download() throws IOException, InterruptedException { try { - connection = (HttpURLConnection)sourceUrl.openConnection(); - - if (wantToCheckCache()) { - setupCacheCheck(); - Log.i(TAG, "Checking cached status of " + sourceUrl); - statusCode = connection.getResponseCode(); - } - - if (isCached()) { - Log.i(TAG, sourceUrl + " is cached, so not downloading (HTTP " + statusCode + ")"); - } else { - Log.i(TAG, "Downloading from " + sourceUrl); - downloadFromStream(); - updateCacheCheck(); - } + connection = (HttpURLConnection) sourceUrl.openConnection(); + doDownload(); } catch (SSLHandshakeException e) { - // TODO this should be handled better, it is not internationalised here. + // TODO this should be handled better, it is not internationalised here throw new IOException( "A problem occurred while establishing an SSL " + "connection. If this problem persists, AND you have a " + "very old device, you could try using http instead of " + - "https for the repo URL." + Log.getStackTraceString(e) ); + "https for the repo URL." + Log.getStackTraceString(e)); + } + } + + protected void doDownload() throws IOException, InterruptedException { + if (wantToCheckCache()) { + setupCacheCheck(); + Log.i(TAG, "Checking cached status of " + sourceUrl); + statusCode = connection.getResponseCode(); + } + + if (isCached()) { + Log.i(TAG, sourceUrl + " is cached, so not downloading (HTTP " + statusCode + ")"); + } else { + Log.i(TAG, "Downloading from " + sourceUrl); + downloadFromStream(); + updateCacheCheck(); } } diff --git a/src/org/fdroid/fdroid/net/TorHttpDownloader.java b/src/org/fdroid/fdroid/net/TorHttpDownloader.java new file mode 100644 index 000000000..13e6c09bb --- /dev/null +++ b/src/org/fdroid/fdroid/net/TorHttpDownloader.java @@ -0,0 +1,45 @@ + +package org.fdroid.fdroid.net; + +import android.content.Context; +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.SocketAddress; + +import javax.net.ssl.SSLHandshakeException; + +public class TorHttpDownloader extends HttpDownloader { + + TorHttpDownloader(String url, Context ctx) throws IOException { + super(url, ctx); + } + + TorHttpDownloader(String url, File destFile) + throws FileNotFoundException, MalformedURLException { + super(url, destFile); + } + + @Override + public void download() throws IOException, InterruptedException { + try { + SocketAddress sa = new InetSocketAddress("127.0.0.1", 8118); + Proxy tor = new Proxy(Proxy.Type.HTTP, sa); + connection = (HttpURLConnection) sourceUrl.openConnection(tor); + doDownload(); + } catch (SSLHandshakeException e) { + throw new IOException( + "A problem occurred while establishing an SSL " + + "connection. If this problem persists, AND you have a " + + "very old device, you could try using http instead of " + + "https for the repo URL." + Log.getStackTraceString(e)); + } + } + +} From d19e77049acbf05f467423761bad653d224b8b76 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 28 May 2014 18:27:23 -0400 Subject: [PATCH 09/10] rename Downloader.inputStream() to getInputStream() This follows: URLConnection.getInputStream() BaseImageDownloader.getStream() --- src/org/fdroid/fdroid/net/Downloader.java | 6 +++--- src/org/fdroid/fdroid/net/HttpDownloader.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/org/fdroid/fdroid/net/Downloader.java b/src/org/fdroid/fdroid/net/Downloader.java index 4a9b96899..593777d52 100644 --- a/src/org/fdroid/fdroid/net/Downloader.java +++ b/src/org/fdroid/fdroid/net/Downloader.java @@ -30,7 +30,7 @@ public abstract class Downloader { public static final String EVENT_PROGRESS = "downloadProgress"; - public abstract InputStream inputStream() throws IOException; + public abstract InputStream getInputStream() throws IOException; // The context is required for opening the file to write to. Downloader(String destFile, Context ctx) @@ -120,13 +120,13 @@ public abstract class Downloader { Log.d(TAG, "Downloading from stream"); InputStream input = null; try { - input = inputStream(); + input = getInputStream(); // Getting the input stream is slow(ish) for HTTP downloads, so we'll check if // we were interrupted before proceeding to the download. throwExceptionIfInterrupted(); - copyInputToOutputStream(inputStream()); + copyInputToOutputStream(getInputStream()); } finally { Utils.closeQuietly(outputStream); Utils.closeQuietly(input); diff --git a/src/org/fdroid/fdroid/net/HttpDownloader.java b/src/org/fdroid/fdroid/net/HttpDownloader.java index 50fc41a65..b9284d7c3 100644 --- a/src/org/fdroid/fdroid/net/HttpDownloader.java +++ b/src/org/fdroid/fdroid/net/HttpDownloader.java @@ -40,7 +40,7 @@ public class HttpDownloader extends Downloader { } @Override - public InputStream inputStream() throws IOException { + public InputStream getInputStream() throws IOException { return connection.getInputStream(); } From c1b5bf52791345f942de0327fe9307a3bccf1b8b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 28 May 2014 19:45:18 -0400 Subject: [PATCH 10/10] implemented IconDownloader for UIL downloads with FDroid classes This lets UniversalImageLoader (UIL) use FDroid's generic Downloader infrastucture so that connection configuration all happens based on the URL in DownloaderFactory. refs #2598 https://dev.guardianproject.info/issues/2598 refs #2367 https://dev.guardianproject.info/issues/2367 --- src/org/fdroid/fdroid/FDroidApp.java | 4 ++- src/org/fdroid/fdroid/net/HttpDownloader.java | 10 +++++- src/org/fdroid/fdroid/net/IconDownloader.java | 32 +++++++++++++++++++ .../fdroid/fdroid/net/TorHttpDownloader.java | 15 +-------- 4 files changed, 45 insertions(+), 16 deletions(-) create mode 100644 src/org/fdroid/fdroid/net/IconDownloader.java diff --git a/src/org/fdroid/fdroid/FDroidApp.java b/src/org/fdroid/fdroid/FDroidApp.java index 2fc8b9506..16d936fce 100644 --- a/src/org/fdroid/fdroid/FDroidApp.java +++ b/src/org/fdroid/fdroid/FDroidApp.java @@ -47,6 +47,7 @@ import org.fdroid.fdroid.data.AppProvider; import org.fdroid.fdroid.data.InstalledAppCacheUpdater; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.localrepo.LocalRepoService; +import org.fdroid.fdroid.net.IconDownloader; import org.fdroid.fdroid.net.WifiStateChangeService; import org.thoughtcrime.ssl.pinning.PinningTrustManager; import org.thoughtcrime.ssl.pinning.SystemKeyStore; @@ -152,7 +153,8 @@ public class FDroidApp extends Application { bluetoothAdapter = getBluetoothAdapter(); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) - .discCache(new LimitedAgeDiscCache( + .imageDownloader(new IconDownloader(getApplicationContext())) + .diskCache(new LimitedAgeDiscCache( new File(StorageUtils.getCacheDirectory(getApplicationContext(), true), "icons"), null, diff --git a/src/org/fdroid/fdroid/net/HttpDownloader.java b/src/org/fdroid/fdroid/net/HttpDownloader.java index b9284d7c3..f1dab1ffc 100644 --- a/src/org/fdroid/fdroid/net/HttpDownloader.java +++ b/src/org/fdroid/fdroid/net/HttpDownloader.java @@ -41,6 +41,8 @@ public class HttpDownloader extends Downloader { @Override public InputStream getInputStream() throws IOException { + setupConnection(); + // TODO check out BaseImageDownloader.getStreamFromNetwork() for optims return connection.getInputStream(); } @@ -53,7 +55,7 @@ public class HttpDownloader extends Downloader { @Override public void download() throws IOException, InterruptedException { try { - connection = (HttpURLConnection) sourceUrl.openConnection(); + setupConnection(); doDownload(); } catch (SSLHandshakeException e) { // TODO this should be handled better, it is not internationalised here @@ -65,6 +67,12 @@ public class HttpDownloader extends Downloader { } } + protected void setupConnection() throws IOException { + if (connection != null) + return; + connection = (HttpURLConnection) sourceUrl.openConnection(); + } + protected void doDownload() throws IOException, InterruptedException { if (wantToCheckCache()) { setupCacheCheck(); diff --git a/src/org/fdroid/fdroid/net/IconDownloader.java b/src/org/fdroid/fdroid/net/IconDownloader.java new file mode 100644 index 000000000..a8dedf17a --- /dev/null +++ b/src/org/fdroid/fdroid/net/IconDownloader.java @@ -0,0 +1,32 @@ + +package org.fdroid.fdroid.net; + +import android.content.Context; + +import com.nostra13.universalimageloader.core.download.BaseImageDownloader; + +import java.io.IOException; +import java.io.InputStream; + +public class IconDownloader extends BaseImageDownloader { + + public IconDownloader(Context context) { + super(context); + } + + public IconDownloader(Context context, int connectTimeout, int readTimeout) { + super(context, connectTimeout, readTimeout); + } + + @Override + public InputStream getStream(String imageUri, Object extra) throws IOException { + switch (Scheme.ofUri(imageUri)) { + case HTTP: + case HTTPS: + Downloader downloader = DownloaderFactory.create(imageUri, context); + return downloader.getInputStream(); + default: + return super.getStream(imageUri, extra); + } + } +} diff --git a/src/org/fdroid/fdroid/net/TorHttpDownloader.java b/src/org/fdroid/fdroid/net/TorHttpDownloader.java index 13e6c09bb..824ff6433 100644 --- a/src/org/fdroid/fdroid/net/TorHttpDownloader.java +++ b/src/org/fdroid/fdroid/net/TorHttpDownloader.java @@ -2,7 +2,6 @@ package org.fdroid.fdroid.net; import android.content.Context; -import android.util.Log; import java.io.File; import java.io.FileNotFoundException; @@ -13,8 +12,6 @@ import java.net.MalformedURLException; import java.net.Proxy; import java.net.SocketAddress; -import javax.net.ssl.SSLHandshakeException; - public class TorHttpDownloader extends HttpDownloader { TorHttpDownloader(String url, Context ctx) throws IOException { @@ -27,19 +24,9 @@ public class TorHttpDownloader extends HttpDownloader { } @Override - public void download() throws IOException, InterruptedException { - try { + protected void setupConnection() throws IOException { SocketAddress sa = new InetSocketAddress("127.0.0.1", 8118); Proxy tor = new Proxy(Proxy.Type.HTTP, sa); connection = (HttpURLConnection) sourceUrl.openConnection(tor); - doDownload(); - } catch (SSLHandshakeException e) { - throw new IOException( - "A problem occurred while establishing an SSL " + - "connection. If this problem persists, AND you have a " + - "very old device, you could try using http instead of " + - "https for the repo URL." + Log.getStackTraceString(e)); - } } - }