Merge branch '1.2-polish' into 'master'

1.2 polish

See merge request fdroid/fdroidclient!670
This commit is contained in:
Hans-Christoph Steiner 2018-04-11 15:05:57 +00:00
commit f181e41338
11 changed files with 96 additions and 77 deletions

View File

@ -471,6 +471,8 @@
<data android:pathPattern="/.*/.*/fdroid/repo/*"/>
<data android:pathPattern="/.*/.*/.*/fdroid/repo"/>
<data android:pathPattern="/.*/.*/.*/fdroid/repo/*"/>
<data android:pathPattern="/.*/.*/.*/.*/fdroid/repo"/>
<data android:pathPattern="/.*/.*/.*/.*/fdroid/repo/*"/>
<data android:path="/fdroid/archive"/>
<data android:pathPattern="/fdroid/archive/*"/>
<data android:pathPattern="/.*/fdroid/archive"/>
@ -479,6 +481,8 @@
<data android:pathPattern="/.*/.*/fdroid/archive/*"/>
<data android:pathPattern="/.*/.*/.*/fdroid/archive"/>
<data android:pathPattern="/.*/.*/.*/fdroid/archive/*"/>
<data android:pathPattern="/.*/.*/.*/.*/fdroid/archive"/>
<data android:pathPattern="/.*/.*/.*/.*/fdroid/archive/*"/>
<!--
Some QR Code scanners don't respect custom schemes like fdroidrepo://,
so this is a workaround, since the local repo URL is all uppercase in

View File

@ -56,7 +56,6 @@ public class AppUpdateStatusService extends IntentService {
if (cacheDirList == null) {
return;
}
PackageManager packageManager = getPackageManager();
List<Apk> apksReadyToInstall = new ArrayList<>();
for (String repoDirName : cacheDirList) {
File repoDir = new File(cacheDir, repoDirName);
@ -67,12 +66,7 @@ public class AppUpdateStatusService extends IntentService {
for (String apkFileName : apks) {
Apk apk = processDownloadedApk(new File(repoDir, apkFileName));
if (apk != null) {
PackageInfo packageInfo = null;
try {
packageInfo = packageManager.getPackageInfo(apk.packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
// ignored
}
PackageInfo packageInfo = Utils.getPackageInfo(this, apk.packageName);
if (packageInfo == null || packageInfo.versionCode != apk.versionCode) {
Utils.debugLog(TAG, "Marking downloaded apk " + apk.apkName + " as ReadyToInstall");
apksReadyToInstall.add(apk);
@ -134,8 +128,8 @@ public class AppUpdateStatusService extends IntentService {
return null;
}
try {
PackageInfo info = getPackageManager().getPackageInfo(downloadedApk.packageName, 0);
PackageInfo info = Utils.getPackageInfo(this, downloadedApk.packageName);
if (info != null) {
File pathToInstalled = InstalledAppProviderService.getPathToInstalledApk(info);
if (pathToInstalled != null && pathToInstalled.canRead() &&
pathToInstalled.length() == downloadedApk.size && // Check size before hash for performance.
@ -145,7 +139,6 @@ public class AppUpdateStatusService extends IntentService {
AppUpdateStatusManager.getInstance(this).markAsNoLongerPendingInstall(downloadedApk.getUrl());
return null;
}
} catch (PackageManager.NameNotFoundException ignored) {
}
Utils.debugLog(TAG, downloadedApk.packageName + ':' + downloadedApk.versionCode

View File

@ -26,7 +26,6 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
@ -450,16 +449,9 @@ public class RepoUpdater {
* per repo basis.
*/
void processRepoPushRequests() {
PackageManager pm = context.getPackageManager();
for (RepoPushRequest repoPushRequest : repoPushRequestList) {
String packageName = repoPushRequest.packageName;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
// ignored
}
PackageInfo packageInfo = Utils.getPackageInfo(context, packageName);
if (RepoPushRequest.INSTALL.equals(repoPushRequest.request)) {
ContentResolver cr = context.getContentResolver();

View File

@ -19,6 +19,7 @@
package org.fdroid.fdroid;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
@ -668,7 +669,12 @@ public final class Utils {
}
}
// Try to get the version name of the client. Return null on failure.
/**
* Try to get the {@link PackageInfo#versionName} of the
* client.
*
* @return null on failure
*/
public static String getVersionName(Context context) {
String versionName = null;
try {
@ -680,6 +686,20 @@ public final class Utils {
return versionName;
}
/**
* Try to get the {@link PackageInfo} for the {@code packageName} provided.
*
* @return null on failure
*/
public static PackageInfo getPackageInfo(Context context, String packageName) {
try {
return context.getPackageManager().getPackageInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
debugLog(TAG, "Could not get PackageInfo: ", e);
}
return null;
}
/**
* Useful for debugging during development, so that arbitrary queries can be made, and their
* results inspected in the debugger.

View File

@ -4,10 +4,8 @@ import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import org.fdroid.fdroid.Utils;
/**
@ -38,12 +36,9 @@ public class ObbUrlActivity extends Activity {
String packageName = componentName.getPackageName();
Apk apk = null;
try {
PackageManager pm = getPackageManager();
PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
PackageInfo packageInfo = Utils.getPackageInfo(this, packageName);
if (packageInfo != null) {
apk = ApkProvider.Helper.findApkFromAnyRepo(this, packageName, packageInfo.versionCode);
} catch (PackageManager.NameNotFoundException e) {
Utils.debugLog(TAG, e.getLocalizedMessage());
}
if (apk == null) {

View File

@ -7,11 +7,12 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.net.Uri;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.fdroid.fdroid.AppUpdateStatusManager;
@ -127,6 +128,19 @@ public class InstallManagerService extends Service {
super.onDestroy();
}
/**
* This goes through a series of checks to make sure that the incoming
* {@link Intent} is still valid. The default {@link Intent#getAction() action}
* in the logic is {@link #ACTION_INSTALL} since it is the most complicate
* case. Since the {@code Intent} will be redelivered by Android if the
* app was killed, this needs to check that it still makes sense to handle.
* <p>
* For example, if F-Droid is killed while installing, it might not receive
* the message that the install completed successfully. The checks need to be
* as specific as possible so as not to block things like installing updates
* with the same {@link PackageInfo#versionCode}, which happens sometimes,
* and is allowed by Android.
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Utils.debugLog(TAG, "onStartCommand " + intent);
@ -155,7 +169,7 @@ public class InstallManagerService extends Service {
appUpdateStatusManager.removeApk(urlString);
return START_NOT_STICKY;
} else if (!ACTION_INSTALL.equals(action)) {
Utils.debugLog(TAG, "Ignoring " + intent + " as it is not an " + ACTION_INSTALL + " intent");
Log.i(TAG, "Ignoring unknown intent action: " + intent);
return START_NOT_STICKY;
}
@ -166,7 +180,6 @@ public class InstallManagerService extends Service {
if ((flags & START_FLAG_REDELIVERY) == START_FLAG_REDELIVERY
&& !DownloaderService.isQueuedOrActive(urlString)) {
// TODO is there a case where we should allow an active urlString to pass through?
Utils.debugLog(TAG, urlString + " finished downloading while InstallManagerService was killed.");
appUpdateStatusManager.removeApk(urlString);
return START_NOT_STICKY;
@ -179,6 +192,14 @@ public class InstallManagerService extends Service {
return START_NOT_STICKY;
}
PackageInfo packageInfo = Utils.getPackageInfo(this, apk.packageName);
if ((flags & START_FLAG_REDELIVERY) == START_FLAG_REDELIVERY
&& packageInfo != null && packageInfo.versionCode == apk.versionCode
&& TextUtils.equals(packageInfo.versionName, apk.versionName)) {
Log.i(TAG, "INSTALL Intent no longer valid since its installed, ignoring: " + intent);
return START_NOT_STICKY;
}
FDroidApp.resetMirrorVars();
DownloaderService.setTimeout(FDroidApp.getTimeout());

View File

@ -5,7 +5,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import org.apache.commons.io.FileUtils;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Utils;

View File

@ -2,7 +2,7 @@ package org.fdroid.fdroid.localrepo;
import android.content.Context;
import android.util.Log;
import kellinwood.security.zipsigner.ZipSigner;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Utils;
import org.spongycastle.asn1.ASN1Sequence;
@ -19,6 +19,9 @@ import org.spongycastle.operator.ContentSigner;
import org.spongycastle.operator.OperatorCreationException;
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.X509KeyManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@ -46,12 +49,6 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.X509KeyManager;
import kellinwood.security.zipsigner.ZipSigner;
// TODO Address exception handling in a uniform way throughout
@SuppressWarnings("LineLength")

View File

@ -14,7 +14,6 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.Hasher;
import org.fdroid.fdroid.Preferences;

View File

@ -20,7 +20,6 @@ import android.support.v4.app.NotificationCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
@ -42,6 +41,10 @@ import org.fdroid.fdroid.localrepo.type.SwapType;
import org.fdroid.fdroid.localrepo.type.WifiSwap;
import org.fdroid.fdroid.net.WifiStateChangeService;
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
@ -56,11 +59,6 @@ import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* Central service which manages all of the different moving parts of swap which are required
* to enable p2p swapping of apps.
@ -115,10 +113,10 @@ public class SwapService extends Service {
* Call {@link Observable#subscribe()} on this in order to be notified of peers
* which are found. Call {@link Subscription#unsubscribe()} on the resulting
* subscription when finished and you no longer want to scan for peers.
*
* <p>
* The returned object will scan for peers on a background thread, and emit
* found peers on the mian thread.
*
* <p>
* Invoking this in multiple places will return the same, cached, peer finder.
* That is, if in the past it already found some peers, then you subscribe
* to it in the future, the future subscriber will still receive the peers
@ -158,7 +156,8 @@ public class SwapService extends Service {
*/
public static final int STEP_INITIAL_LOADING = 9;
@SwapStep private int step = STEP_INTRO;
@SwapStep
private int step = STEP_INTRO;
/**
* Current screen that the swap process is up to.
@ -174,7 +173,8 @@ public class SwapService extends Service {
return this;
}
@NonNull public Set<String> getAppsToSwap() {
@NonNull
public Set<String> getAppsToSwap() {
return appsToSwap;
}
@ -301,7 +301,8 @@ public class SwapService extends Service {
@IntDef({STEP_INTRO, STEP_SELECT_APPS, STEP_JOIN_WIFI, STEP_SHOW_NFC, STEP_WIFI_QR,
STEP_CONNECTING, STEP_SUCCESS, STEP_CONFIRM_SWAP, STEP_INITIAL_LOADING})
@Retention(RetentionPolicy.SOURCE)
public @interface SwapStep { }
public @interface SwapStep {
}
// =================================================
// Have selected a specific peer to swap with
@ -340,6 +341,7 @@ public class SwapService extends Service {
* which is only available in API >= 11.
* Package names are reverse-DNS-style, so they should only have alpha numeric values. Thus,
* this uses a comma as the separator.
*
* @see SwapService#deserializePackages(String)
*/
private static String serializePackages(Set<String> packages) {

View File

@ -4,7 +4,6 @@ import android.annotation.TargetApi;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
import info.guardianproject.netcipher.NetCipher;
import org.apache.commons.io.FileUtils;
import org.fdroid.fdroid.BuildConfig;
@ -23,6 +22,13 @@ import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
/**
* Download files over HTTP, with support for proxies, {@code .onion} addresses,
* HTTP Basic Auth, etc. This is not a full HTTP client! This is only using
* the bits of HTTP that F-Droid needs to operate. It does not support things
* like redirects or other HTTP tricks. This keeps the security model and code
* a lot simpler.
*/
public class HttpDownloader extends Downloader {
private static final String TAG = "HttpDownloader";
@ -58,15 +64,6 @@ public class HttpDownloader extends Downloader {
this.password = password;
}
/**
* Note: Doesn't follow redirects (as far as I'm aware).
* {@link BaseImageDownloader#getStreamFromNetwork(String, Object)} has an implementation worth
* checking out that follows redirects up to a certain point. I guess though the correct way
* is probably to check for a loop (keep a list of all URLs redirected to and if you hit the
* same one twice, bail with an exception).
*
* @throws IOException
*/
@Override
protected InputStream getDownloadersInputStream() throws IOException {
setupConnection(false);