From df08e84e7829652d7999eee5451080a012b00a1e Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 29 Mar 2018 22:26:41 +0200 Subject: [PATCH] switch all Downloader subclasses to use Uri instead of URL java.net.URL barfs on custom URL schemes, and making it handle them is really hard. Basically, there needs to be a Handler stub class, then URL.setURLStreamHandlerFactory() must run when F-Droid starts, since it has to be set before any URL instance is used. This all leaves some weird logic that gives the false impression that URLConnection will handle these custom schemes. Switching to Uri/urlString throughout the code matches the other classes that use urlString as the unique ID, and this doesn't add more lines of code. --- .../java/org/fdroid/fdroid/FDroidApp.java | 14 ------------- .../org/fdroid/fdroid/IndexV1Updater.java | 5 +---- .../fdroid/ProgressBufferedInputStream.java | 9 ++++---- .../org/fdroid/fdroid/ProgressListener.java | 2 +- .../java/org/fdroid/fdroid/RepoUpdater.java | 7 +++---- .../fdroid/net/BluetoothDownloader.java | 12 +++++------ .../org/fdroid/fdroid/net/Downloader.java | 10 ++++----- .../fdroid/fdroid/net/DownloaderFactory.java | 21 +++++++------------ .../fdroid/fdroid/net/DownloaderService.java | 6 ++---- .../org/fdroid/fdroid/net/HttpDownloader.java | 20 ++++++++++-------- .../net/www/protocol/bluetooth/Handler.java | 18 ---------------- 11 files changed, 40 insertions(+), 84 deletions(-) delete mode 100644 app/src/main/java/sun/net/www/protocol/bluetooth/Handler.java diff --git a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java index 58b16de3f..0d3a05068 100644 --- a/app/src/main/java/org/fdroid/fdroid/FDroidApp.java +++ b/app/src/main/java/org/fdroid/fdroid/FDroidApp.java @@ -60,12 +60,8 @@ import org.fdroid.fdroid.installer.InstallHistoryService; import org.fdroid.fdroid.net.ImageLoaderForUIL; import org.fdroid.fdroid.net.WifiStateChangeService; import org.fdroid.fdroid.views.hiding.HidingManager; -import sun.net.www.protocol.bluetooth.Handler; import java.io.IOException; -import java.net.URL; -import java.net.URLStreamHandler; -import java.net.URLStreamHandlerFactory; import java.security.Security; import java.util.List; @@ -370,16 +366,6 @@ public class FDroidApp extends Application { } }); - // This is added so that the bluetooth:// scheme we use for URLs the BluetoothDownloader - // understands is not treated as invalid by the java.net.URL class. The actual Handler does - // nothing, but its presence is enough. - URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() { - @Override - public URLStreamHandler createURLStreamHandler(String protocol) { - return TextUtils.equals(protocol, "bluetooth") ? new Handler() : null; - } - }); - final Context context = this; Preferences.get().registerUnstableUpdatesChangeListener(new Preferences.ChangeListener() { @Override diff --git a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java index b53a6a17e..b961f3abb 100644 --- a/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java +++ b/app/src/main/java/org/fdroid/fdroid/IndexV1Updater.java @@ -6,7 +6,6 @@ import android.net.Uri; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.core.JsonFactory; @@ -15,7 +14,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.InjectableValues; import com.fasterxml.jackson.databind.ObjectMapper; - import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.App; @@ -31,7 +29,6 @@ import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; import java.net.SocketTimeoutException; -import java.net.URL; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Date; @@ -157,7 +154,7 @@ public class IndexV1Updater extends RepoUpdater { JarFile jarFile = new JarFile(outputFile, true); JarEntry indexEntry = (JarEntry) jarFile.getEntry(DATA_FILE_NAME); InputStream indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry), - processIndexListener, new URL(repo.address), (int) indexEntry.getSize()); + processIndexListener, repo.address, (int) indexEntry.getSize()); processIndexV1(indexInputStream, indexEntry, cacheTag); } diff --git a/app/src/main/java/org/fdroid/fdroid/ProgressBufferedInputStream.java b/app/src/main/java/org/fdroid/fdroid/ProgressBufferedInputStream.java index d8ca7ca9f..e849fe3cb 100644 --- a/app/src/main/java/org/fdroid/fdroid/ProgressBufferedInputStream.java +++ b/app/src/main/java/org/fdroid/fdroid/ProgressBufferedInputStream.java @@ -3,12 +3,11 @@ package org.fdroid.fdroid; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URL; class ProgressBufferedInputStream extends BufferedInputStream { private final ProgressListener progressListener; - private final URL sourceUrl; + private final String urlString; private final int totalBytes; private int currentBytes; @@ -17,10 +16,10 @@ class ProgressBufferedInputStream extends BufferedInputStream { * Reports progress to the specified {@link ProgressListener}, with the * progress based on the {@code totalBytes}. */ - ProgressBufferedInputStream(InputStream in, ProgressListener progressListener, URL sourceUrl, int totalBytes) { + ProgressBufferedInputStream(InputStream in, ProgressListener progressListener, String urlString, int totalBytes) { super(in); this.progressListener = progressListener; - this.sourceUrl = sourceUrl; + this.urlString = urlString; this.totalBytes = totalBytes; } @@ -32,7 +31,7 @@ class ProgressBufferedInputStream extends BufferedInputStream { * the digits changing because it looks pretty, < 9000 since the reads won't * line up exactly */ if (currentBytes % 333333 < 9000) { - progressListener.onProgress(sourceUrl, currentBytes, totalBytes); + progressListener.onProgress(urlString, currentBytes, totalBytes); } } return super.read(buffer, byteOffset, byteCount); diff --git a/app/src/main/java/org/fdroid/fdroid/ProgressListener.java b/app/src/main/java/org/fdroid/fdroid/ProgressListener.java index beb370fd2..dc9fbcbdb 100644 --- a/app/src/main/java/org/fdroid/fdroid/ProgressListener.java +++ b/app/src/main/java/org/fdroid/fdroid/ProgressListener.java @@ -19,6 +19,6 @@ import java.net.URL; */ public interface ProgressListener { - void onProgress(URL sourceUrl, long bytesRead, long totalBytes); + void onProgress(String urlString, long bytesRead, long totalBytes); } diff --git a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java index afb3659ab..6e8654d89 100644 --- a/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java +++ b/app/src/main/java/org/fdroid/fdroid/RepoUpdater.java @@ -53,7 +53,6 @@ import javax.xml.parsers.SAXParserFactory; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.URL; import java.security.CodeSigner; import java.security.cert.Certificate; import java.security.cert.X509Certificate; @@ -213,7 +212,7 @@ public class RepoUpdater { JarFile jarFile = new JarFile(downloadedFile, true); JarEntry indexEntry = (JarEntry) jarFile.getEntry("index.xml"); indexInputStream = new ProgressBufferedInputStream(jarFile.getInputStream(indexEntry), - processIndexListener, new URL(repo.address), (int) indexEntry.getSize()); + processIndexListener, repo.address, (int) indexEntry.getSize()); // Process the index... SAXParserFactory factory = SAXParserFactory.newInstance(); @@ -251,14 +250,14 @@ public class RepoUpdater { protected final ProgressListener downloadListener = new ProgressListener() { @Override - public void onProgress(URL sourceUrl, long bytesRead, long totalBytes) { + public void onProgress(String urlString, long bytesRead, long totalBytes) { UpdateService.reportDownloadProgress(context, RepoUpdater.this, bytesRead, totalBytes); } }; protected final ProgressListener processIndexListener = new ProgressListener() { @Override - public void onProgress(URL sourceUrl, long bytesRead, long totalBytes) { + public void onProgress(String urlString, long bytesRead, long totalBytes) { UpdateService.reportProcessIndexProgress(context, RepoUpdater.this, bytesRead, totalBytes); } }; diff --git a/app/src/main/java/org/fdroid/fdroid/net/BluetoothDownloader.java b/app/src/main/java/org/fdroid/fdroid/net/BluetoothDownloader.java index fd9a06379..85d4f4d84 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/BluetoothDownloader.java +++ b/app/src/main/java/org/fdroid/fdroid/net/BluetoothDownloader.java @@ -1,8 +1,8 @@ package org.fdroid.fdroid.net; +import android.net.Uri; import android.support.annotation.Nullable; import android.util.Log; - import org.apache.commons.io.input.BoundedInputStream; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.net.bluetooth.BluetoothClient; @@ -14,7 +14,6 @@ import org.fdroid.fdroid.net.bluetooth.httpish.Response; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.net.URL; public class BluetoothDownloader extends Downloader { @@ -24,10 +23,11 @@ public class BluetoothDownloader extends Downloader { private FileDetails fileDetails; private final String sourcePath; - public BluetoothDownloader(String macAddress, URL sourceUrl, File destFile) throws IOException { - super(sourceUrl, destFile); + public BluetoothDownloader(Uri uri, File destFile) throws IOException { + super(uri, destFile); + String macAddress = uri.getHost().replace("-", ":"); this.connection = new BluetoothClient(macAddress).openConnection(); - this.sourcePath = sourceUrl.getPath(); + this.sourcePath = uri.getPath(); } @Override @@ -58,7 +58,7 @@ public class BluetoothDownloader extends Downloader { if (fileDetails == null) { Utils.debugLog(TAG, "Going to Bluetooth \"server\" to get file details."); try { - fileDetails = Request.createHEAD(sourceUrl.getPath(), connection).send().toFileDetails(); + fileDetails = Request.createHEAD(sourcePath, connection).send().toFileDetails(); } catch (IOException e) { Log.e(TAG, "Error getting file details from Bluetooth \"server\"", e); } diff --git a/app/src/main/java/org/fdroid/fdroid/net/Downloader.java b/app/src/main/java/org/fdroid/fdroid/net/Downloader.java index d391ef2b6..3dcb80479 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/Downloader.java +++ b/app/src/main/java/org/fdroid/fdroid/net/Downloader.java @@ -1,5 +1,6 @@ package org.fdroid.fdroid.net; +import android.net.Uri; import org.fdroid.fdroid.ProgressListener; import org.fdroid.fdroid.Utils; @@ -9,7 +10,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; -import java.net.URL; import java.util.Timer; import java.util.TimerTask; @@ -37,7 +37,7 @@ public abstract class Downloader { public final File outputFile; - final URL sourceUrl; + final String urlString; String cacheTag; boolean notFound; @@ -52,8 +52,8 @@ public abstract class Downloader { protected abstract void close(); - Downloader(URL url, File destFile) { - this.sourceUrl = url; + Downloader(Uri uri, File destFile) { + this.urlString = uri.toString(); outputFile = destFile; } @@ -201,7 +201,7 @@ public abstract class Downloader { @Override public void run() { if (downloaderProgressListener != null) { - downloaderProgressListener.onProgress(sourceUrl, bytesRead, totalBytes); + downloaderProgressListener.onProgress(urlString, bytesRead, totalBytes); } } }; diff --git a/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java b/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java index 8e87b4347..3d124eed9 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java +++ b/app/src/main/java/org/fdroid/fdroid/net/DownloaderFactory.java @@ -2,19 +2,15 @@ package org.fdroid.fdroid.net; import android.content.Context; import android.net.Uri; -import android.support.v4.content.LocalBroadcastManager; import org.fdroid.fdroid.data.Repo; import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.Schema; import java.io.File; import java.io.IOException; -import java.net.URL; public class DownloaderFactory { - private static LocalBroadcastManager localBroadcastManager; - /** * Downloads to a temporary file, which *you must delete yourself when * you are done. It is stored in {@link Context#getCacheDir()} and starts @@ -34,22 +30,19 @@ public class DownloaderFactory { public static Downloader create(Context context, String urlString, File destFile) throws IOException { - URL url = new URL(urlString); Downloader downloader; - if (localBroadcastManager == null) { - localBroadcastManager = LocalBroadcastManager.getInstance(context); - } - if ("bluetooth".equalsIgnoreCase(url.getProtocol())) { - String macAddress = url.getHost().replace("-", ":"); - downloader = new BluetoothDownloader(macAddress, url, destFile); + Uri uri = Uri.parse(urlString); + String scheme = uri.getScheme(); + if ("bluetooth".equals(scheme)) { + downloader = new BluetoothDownloader(uri, destFile); } else { final String[] projection = {Schema.RepoTable.Cols.USERNAME, Schema.RepoTable.Cols.PASSWORD}; - Repo repo = RepoProvider.Helper.findByUrl(context, Uri.parse(url.toString()), projection); + Repo repo = RepoProvider.Helper.findByUrl(context, uri, projection); if (repo == null) { - downloader = new HttpDownloader(url, destFile); + downloader = new HttpDownloader(uri, destFile); } else { - downloader = new HttpDownloader(url, destFile, repo.username, repo.password); + downloader = new HttpDownloader(uri, destFile, repo.username, repo.password); } } return downloader; diff --git a/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java b/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java index 52d474e45..cf564bab9 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java +++ b/app/src/main/java/org/fdroid/fdroid/net/DownloaderService.java @@ -31,7 +31,6 @@ import android.os.PatternMatcher; import android.os.Process; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; - import org.fdroid.fdroid.ProgressListener; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; @@ -42,7 +41,6 @@ import java.io.File; import java.io.IOException; import java.net.ConnectException; import java.net.SocketTimeoutException; -import java.net.URL; /** * DownloaderService is a service that handles asynchronous download requests @@ -199,7 +197,7 @@ public class DownloaderService extends Service { downloader = DownloaderFactory.create(this, uri, localFile); downloader.setListener(new ProgressListener() { @Override - public void onProgress(URL sourceUrl, long bytesRead, long totalBytes) { + public void onProgress(String urlString, long bytesRead, long totalBytes) { Intent intent = new Intent(Downloader.ACTION_PROGRESS); intent.setData(uri); intent.putExtra(Downloader.EXTRA_BYTES_READ, bytesRead); @@ -321,7 +319,7 @@ public class DownloaderService extends Service { * Check if a URL is actively being downloaded. */ private static boolean isActive(String urlString) { - return downloader != null && TextUtils.equals(urlString, downloader.sourceUrl.toString()); + return downloader != null && TextUtils.equals(urlString, downloader.urlString); } public static void setTimeout(int ms) { diff --git a/app/src/main/java/org/fdroid/fdroid/net/HttpDownloader.java b/app/src/main/java/org/fdroid/fdroid/net/HttpDownloader.java index 05b460473..8dba014de 100644 --- a/app/src/main/java/org/fdroid/fdroid/net/HttpDownloader.java +++ b/app/src/main/java/org/fdroid/fdroid/net/HttpDownloader.java @@ -1,6 +1,7 @@ package org.fdroid.fdroid.net; import android.annotation.TargetApi; +import android.net.Uri; import android.os.Build; import android.text.TextUtils; import com.nostra13.universalimageloader.core.download.BaseImageDownloader; @@ -29,29 +30,30 @@ public class HttpDownloader extends Downloader { private final String username; private final String password; + private URL sourceUrl; private HttpURLConnection connection; private boolean newFileAvailableOnServer; - HttpDownloader(URL url, File destFile) + HttpDownloader(Uri uri, File destFile) throws FileNotFoundException, MalformedURLException { - this(url, destFile, null, null); + this(uri, destFile, null, null); } /** * Create a downloader that can authenticate via HTTP Basic Auth using the supplied * {@code username} and {@code password}. * - * @param url The file to download + * @param uri The file to download * @param destFile Where the download is saved * @param username Username for HTTP Basic Auth, use {@code null} to ignore * @param password Password for HTTP Basic Auth, use {@code null} to ignore * @throws FileNotFoundException * @throws MalformedURLException */ - HttpDownloader(URL url, File destFile, String username, String password) + HttpDownloader(Uri uri, File destFile, String username, String password) throws FileNotFoundException, MalformedURLException { - super(url, destFile); - + super(uri, destFile); + this.sourceUrl = new URL(urlString); this.username = username; this.password = password; } @@ -95,7 +97,7 @@ public class HttpDownloader extends Downloader { case 200: contentLength = tmpConn.getContentLength(); if (!TextUtils.isEmpty(etag) && etag.equals(cacheTag)) { - Utils.debugLog(TAG, sourceUrl + " is cached, not downloading"); + Utils.debugLog(TAG, urlString + " is cached, not downloading"); return; } newFileAvailableOnServer = true; @@ -104,7 +106,7 @@ public class HttpDownloader extends Downloader { notFound = true; return; default: - Utils.debugLog(TAG, "HEAD check of " + sourceUrl + " returned " + statusCode + ": " + Utils.debugLog(TAG, "HEAD check of " + urlString + " returned " + statusCode + ": " + tmpConn.getResponseMessage()); } @@ -118,7 +120,7 @@ public class HttpDownloader extends Downloader { resumable = true; } setupConnection(resumable); - Utils.debugLog(TAG, "downloading " + sourceUrl + " (is resumable: " + resumable + ")"); + Utils.debugLog(TAG, "downloading " + urlString + " (is resumable: " + resumable + ")"); downloadFromStream(8192, resumable); cacheTag = connection.getHeaderField(HEADER_FIELD_ETAG); } diff --git a/app/src/main/java/sun/net/www/protocol/bluetooth/Handler.java b/app/src/main/java/sun/net/www/protocol/bluetooth/Handler.java deleted file mode 100644 index 34234ee89..000000000 --- a/app/src/main/java/sun/net/www/protocol/bluetooth/Handler.java +++ /dev/null @@ -1,18 +0,0 @@ -package sun.net.www.protocol.bluetooth; - -import java.io.IOException; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLStreamHandler; - -/** - * This class is added so that the bluetooth:// scheme we use for the {@link - * org.fdroid.fdroid.net.BluetoothDownloader} is not treated as invalid by - * the {@link URL} class. - */ -public class Handler extends URLStreamHandler { - @Override - protected URLConnection openConnection(URL u) throws IOException { - throw new UnsupportedOperationException("openConnection() not supported on bluetooth:// URLs"); - } -}