From 36d699a6a65b58e19560ca963d5184ee8588c6cb Mon Sep 17 00:00:00 2001 From: Ajeje Brazorf Date: Thu, 5 Apr 2018 23:16:04 +0000 Subject: [PATCH 1/6] Translated using Weblate (Sardinian) Currently translated at 100.0% (416 of 416 strings) --- app/src/main/res/values-sc/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index 744bd3139..4362f1ef5 100644 --- a/app/src/main/res/values-sc/strings.xml +++ b/app/src/main/res/values-sc/strings.xml @@ -568,4 +568,12 @@ Inclue sas aplicatziones chi impreant s\'ischermu tàtile Ammustra sas aplicatziones chi tenent bisòngiu de s\'ischermu tàtile in cale si siat dispositivu + Annanghe un\'ispigru + %1$s est giai impostadu, custu at a annànghere informatziones de crae noas. + %1$s est giai impostadu, cunfirma chi lu cheres torrare a abilitare. + %1$s est giai impostadu e abilitadu. + In antis iscantzella %1$s pro annànghere custu cun una crae in cunflitu. + Custa est una còpia de %1$s, lu cheres annànghere comente isprigu? + Isprigos ufitziales + Isprigos impreadores From b5adf7d0eff09b771fd102576b20d8151f1bf69b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 6 Apr 2018 10:54:43 +0200 Subject: [PATCH 2/6] document ignoring EXIF --- app/src/main/java/org/fdroid/fdroid/Utils.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/org/fdroid/fdroid/Utils.java b/app/src/main/java/org/fdroid/fdroid/Utils.java index 9ca43a347..703b169d7 100644 --- a/app/src/main/java/org/fdroid/fdroid/Utils.java +++ b/app/src/main/java/org/fdroid/fdroid/Utils.java @@ -406,6 +406,12 @@ public final class Utils { return new Locale(languageTag); } + /** + * Since there have been vulnerabilities in EXIF processing in Android, this + * disables all use of EXIF. + * + * @see CVE-2016-3862 + */ public static DisplayImageOptions.Builder getDefaultDisplayImageOptionsBuilder() { if (defaultDisplayImageOptionsBuilder == null) { defaultDisplayImageOptionsBuilder = new DisplayImageOptions.Builder() From 98aea0127223fb43b4579a8ee91b8482ae8bb4d3 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 6 Apr 2018 11:14:48 +0200 Subject: [PATCH 3/6] remove confusing Exception when checking permissions Might as well just let things fail with a NullPointerException, that will provide more information around the crash. --- app/src/main/java/org/fdroid/fdroid/installer/Installer.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java index c60d53883..86739fa06 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java @@ -113,10 +113,6 @@ public abstract class Installer { } AppDiff appDiff = new AppDiff(context.getPackageManager(), apk); - if (appDiff.pkgInfo == null) { - // could not get diff because we couldn't parse the package - throw new RuntimeException("cannot parse!"); - } AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.pkgInfo); if (appDiff.installedAppInfo != null) { // update to an existing app From 7ba1966538557a64dd90729de66819eefd1d1426 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 6 Apr 2018 11:26:26 +0200 Subject: [PATCH 4/6] use standard code style and var naming patterns for AppDiff The standard pattern is to pass a Context in rather than call things like getPackageManager in. It should only pass a PackageManager if that is actually being reused. This shouldn't change the logic at all. --- .../fdroid/fdroid/installer/Installer.java | 14 +++-- .../fdroid/privileged/views/AppDiff.java | 52 +++++++++---------- .../views/InstallConfirmActivity.java | 12 ++--- .../views/AppDetailsRecyclerViewAdapter.java | 4 +- 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java index 86739fa06..bb0735b4b 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/Installer.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/Installer.java @@ -106,15 +106,23 @@ public abstract class Installer { return intent; } + /** + * Return if this installation process has any new permissions that the user + * should be aware of. Starting in {@code android-23}, all new permissions + * are requested when they are used, and the permissions prompt at time of + * install is not used. All permissions in a new install are considered new. + * + * @return the number of new permissions + */ private int newPermissionCount() { boolean supportsRuntimePermissions = apk.targetSdkVersion >= 23; if (supportsRuntimePermissions) { return 0; } - AppDiff appDiff = new AppDiff(context.getPackageManager(), apk); - AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.pkgInfo); - if (appDiff.installedAppInfo != null) { + AppDiff appDiff = new AppDiff(context, apk); + AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.apkPackageInfo); + if (appDiff.installedApplicationInfo != null) { // update to an existing app return perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW); } diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java index 036a78533..5e080e69f 100644 --- a/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java +++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/AppDiff.java @@ -18,55 +18,51 @@ package org.fdroid.fdroid.privileged.views; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; - import org.fdroid.fdroid.data.Apk; +/** + * Represents the permissions difference between the installed APK, and the + * update APK represented by {@link Apk}. + */ public class AppDiff { - private final PackageManager pm; - public final PackageInfo pkgInfo; - public ApplicationInfo installedAppInfo; + public final PackageInfo apkPackageInfo; + public final ApplicationInfo installedApplicationInfo; - /** - * Constructor based on F-Droids Apk object - */ - public AppDiff(PackageManager pm, Apk apk) { - this.pm = pm; + public AppDiff(Context context, Apk apk) { + PackageManager pm = context.getPackageManager(); + apkPackageInfo = new PackageInfo(); + apkPackageInfo.packageName = apk.packageName; + apkPackageInfo.applicationInfo = new ApplicationInfo(); + apkPackageInfo.requestedPermissions = apk.requestedPermissions; - pkgInfo = new PackageInfo(); - pkgInfo.packageName = apk.packageName; - pkgInfo.applicationInfo = new ApplicationInfo(); - pkgInfo.requestedPermissions = apk.requestedPermissions; - - init(); - } - - private void init() { - String pkgName = pkgInfo.packageName; + String packageName = apkPackageInfo.packageName; // Check if there is already a package on the device with this name // but it has been renamed to something else. - final String[] oldName = pm.canonicalToCurrentPackageNames(new String[]{pkgName}); + final String[] oldName = pm.canonicalToCurrentPackageNames(new String[]{packageName}); if (oldName != null && oldName.length > 0 && oldName[0] != null) { - pkgName = oldName[0]; - pkgInfo.packageName = pkgName; - pkgInfo.applicationInfo.packageName = pkgName; + packageName = oldName[0]; + apkPackageInfo.packageName = packageName; + apkPackageInfo.applicationInfo.packageName = packageName; } // Check if package is already installed + ApplicationInfo applicationInfo; try { // This is a little convoluted because we want to get all uninstalled // apps, but this may include apps with just data, and if it is just // data we still want to count it as "installed". //noinspection WrongConstant (lint is actually wrong here!) - installedAppInfo = pm.getApplicationInfo(pkgName, - PackageManager.GET_UNINSTALLED_PACKAGES); - if ((installedAppInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { - installedAppInfo = null; + applicationInfo = pm.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); + if ((applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { + applicationInfo = null; } } catch (PackageManager.NameNotFoundException e) { - installedAppInfo = null; + applicationInfo = null; } + installedApplicationInfo = applicationInfo; } } diff --git a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java index f9f9791b8..fb450594f 100644 --- a/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/privileged/views/InstallConfirmActivity.java @@ -92,9 +92,9 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel scrollView = null; okCanInstall = false; int msg = 0; - AppSecurityPermissions perms = new AppSecurityPermissions(this, appDiff.pkgInfo); - if (appDiff.installedAppInfo != null) { - msg = (appDiff.installedAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 + AppSecurityPermissions perms = new AppSecurityPermissions(this, appDiff.apkPackageInfo); + if (appDiff.installedApplicationInfo != null) { + msg = (appDiff.installedApplicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? R.string.install_confirm_update_system : R.string.install_confirm_update; scrollView = new CaffeinatedScrollView(this); @@ -131,10 +131,10 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel } if (!permVisible) { - if (appDiff.installedAppInfo != null) { + if (appDiff.installedApplicationInfo != null) { // This is an update to an application, but there are no // permissions at all. - msg = (appDiff.installedAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 + msg = (appDiff.installedApplicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? R.string.install_confirm_update_system_no_perms : R.string.install_confirm_update_no_perms; } else { @@ -182,7 +182,7 @@ public class InstallConfirmActivity extends FragmentActivity implements OnCancel app = AppProvider.Helper.findSpecificApp(getContentResolver(), apk.packageName, apk.repoId, Schema.AppMetadataTable.Cols.ALL); - appDiff = new AppDiff(getPackageManager(), apk); + appDiff = new AppDiff(this, apk); setContentView(R.layout.install_start); diff --git a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java index 110a622ba..12ffd19bc 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java +++ b/app/src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java @@ -789,8 +789,8 @@ public class AppDetailsRecyclerViewAdapter headerView.setText(R.string.permissions); updateExpandableItem(false); contentView.removeAllViews(); - AppDiff appDiff = new AppDiff(context.getPackageManager(), versions.get(0)); - AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.pkgInfo); + AppDiff appDiff = new AppDiff(context, versions.get(0)); + AppSecurityPermissions perms = new AppSecurityPermissions(context, appDiff.apkPackageInfo); contentView.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL)); } From 5bd276c8042c856adf5d9efa45850441717c3fd9 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 6 Apr 2018 15:50:57 +0200 Subject: [PATCH 5/6] make "Only on WiFi" also apply to updates, graphics, and icons The "Only on WiFi" pref originally only controlled index updates, but now it makes sense to include all of the various files that are downloaded. #1381 --- app/src/main/AndroidManifest.xml | 3 + .../java/org/fdroid/fdroid/FDroidApp.java | 8 +- .../java/org/fdroid/fdroid/Preferences.java | 11 ++- .../java/org/fdroid/fdroid/UpdateService.java | 45 ++-------- .../net/ConnectivityMonitorService.java | 86 +++++++++++++++++++ .../fdroid/views/ScreenShotsActivity.java | 15 ++++ 6 files changed, 125 insertions(+), 43 deletions(-) create mode 100644 app/src/main/java/org/fdroid/fdroid/net/ConnectivityMonitorService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a8e9d7557..91ea6c205 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -271,6 +271,9 @@ android:name=".DeleteCacheService" android:exported="false"/> + = 16 && cm.isActiveNetworkMetered()) { - return FLAG_NET_METERED; - } else { - return FLAG_NET_NO_LIMIT; - } - default: - return FLAG_NET_METERED; - } - } - /** * In order to send a {@link Toast} from a {@link IntentService}, we have to do these tricks. */ @@ -371,8 +339,8 @@ public class UpdateService extends IntentService { try { // See if it's time to actually do anything yet... - int netState = getNetworkState(this); - if (netState == FLAG_NET_UNAVAILABLE) { + int netState = ConnectivityMonitorService.getNetworkState(this); + if (netState == ConnectivityMonitorService.FLAG_NET_UNAVAILABLE) { Utils.debugLog(TAG, "No internet, cannot update"); if (manualUpdate) { sendNoInternetToast(); @@ -380,10 +348,10 @@ public class UpdateService extends IntentService { return; } + final Preferences fdroidPrefs = Preferences.get(); if (manualUpdate || forcedUpdate) { Utils.debugLog(TAG, "manually requested or forced update"); - } else if (!verifyIsTimeForScheduledRun() - || (netState == FLAG_NET_METERED && Preferences.get().isUpdateOnlyOnUnmeteredNetworks())) { + } else if (!verifyIsTimeForScheduledRun() || !fdroidPrefs.isBackgroundDownloadAllowed()) { Utils.debugLog(TAG, "don't run update"); return; } @@ -403,7 +371,6 @@ public class UpdateService extends IntentService { ArrayList repoErrors = new ArrayList<>(); boolean changes = false; boolean singleRepoUpdate = !TextUtils.isEmpty(address); - final Preferences fdroidPrefs = Preferences.get(); for (final Repo repo : repos) { if (!repo.inuse) { continue; @@ -442,7 +409,7 @@ public class UpdateService extends IntentService { } // now that downloading the index is done, start downloading updates - if (changes && fdroidPrefs.isAutoDownloadEnabled()) { + if (changes && fdroidPrefs.isAutoDownloadEnabled() && fdroidPrefs.isBackgroundDownloadAllowed()) { autoDownloadUpdates(this); } } diff --git a/app/src/main/java/org/fdroid/fdroid/net/ConnectivityMonitorService.java b/app/src/main/java/org/fdroid/fdroid/net/ConnectivityMonitorService.java new file mode 100644 index 000000000..13df8ee6f --- /dev/null +++ b/app/src/main/java/org/fdroid/fdroid/net/ConnectivityMonitorService.java @@ -0,0 +1,86 @@ +package org.fdroid.fdroid.net; + +import android.app.IntentService; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Build; +import com.nostra13.universalimageloader.core.ImageLoader; +import org.fdroid.fdroid.FDroidApp; +import org.fdroid.fdroid.Preferences; + +/** + * An {@link IntentService} subclass for tracking whether there is metered or + * unmetered internet available, based on + * {@link android.net.ConnectivityManager#CONNECTIVITY_ACTION} + */ +public class ConnectivityMonitorService extends IntentService { + public static final String TAG = "ConnectivityMonitorServ"; + + public static final int FLAG_NET_UNAVAILABLE = 0; + public static final int FLAG_NET_METERED = 1; + public static final int FLAG_NET_NO_LIMIT = 2; + + private static final String ACTION_START = "org.fdroid.fdroid.net.action.CONNECTIVITY_MONITOR"; + + private static final BroadcastReceiver CONNECTIVITY_RECEIVER = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + start(context); + } + }; + + public ConnectivityMonitorService() { + super("ConnectivityMonitorService"); + } + + public static void registerAndStart(Context context) { + context.registerReceiver(CONNECTIVITY_RECEIVER, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + } + + public static void start(Context context) { + Intent intent = new Intent(context, ConnectivityMonitorService.class); + intent.setAction(ACTION_START); + context.startService(intent); + } + + /** + * Gets the state of internet availability, whether there is no connection at all, + * whether the connection has no usage limit (like most WiFi), or whether this is + * a metered connection like most cellular plans or hotspot WiFi connections. + */ + public static int getNetworkState(Context context) { + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE); + if (cm == null) { + return FLAG_NET_UNAVAILABLE; + } + NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); + if (activeNetwork == null || !activeNetwork.isConnected()) { + return FLAG_NET_UNAVAILABLE; + } + + int networkType = activeNetwork.getType(); + switch (networkType) { + case ConnectivityManager.TYPE_ETHERNET: + case ConnectivityManager.TYPE_WIFI: + if (Build.VERSION.SDK_INT >= 16 && cm.isActiveNetworkMetered()) { + return FLAG_NET_METERED; + } else { + return FLAG_NET_NO_LIMIT; + } + default: + return FLAG_NET_METERED; + } + } + + @Override + protected void onHandleIntent(Intent intent) { + if (intent != null && ACTION_START.equals(intent.getAction())) { + FDroidApp.networkState = getNetworkState(this); + ImageLoader.getInstance().denyNetworkDownloads(!Preferences.get().isBackgroundDownloadAllowed()); + } + } +} diff --git a/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java b/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java index bc88526df..4e930ce06 100644 --- a/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java +++ b/app/src/main/java/org/fdroid/fdroid/views/ScreenShotsActivity.java @@ -18,6 +18,7 @@ import android.widget.ImageView; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import org.fdroid.fdroid.FDroidApp; +import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.R; import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.App; @@ -36,6 +37,8 @@ public class ScreenShotsActivity extends AppCompatActivity { private static final String EXTRA_PACKAGE_NAME = "EXTRA_PACKAGE_NAME"; private static final String EXTRA_START_POSITION = "EXTRA_START_POSITION"; + private static final ImageLoader IMAGE_LOADER = ImageLoader.getInstance(); + public static Intent getStartIntent(Context context, String packageName, int startPosition) { Intent intent = new Intent(context, ScreenShotsActivity.class); intent.putExtra(EXTRA_PACKAGE_NAME, packageName); @@ -68,6 +71,18 @@ public class ScreenShotsActivity extends AppCompatActivity { } } + @Override + protected void onResume() { + super.onResume(); + IMAGE_LOADER.denyNetworkDownloads(false); + } + + @Override + protected void onPause() { + super.onPause(); + IMAGE_LOADER.denyNetworkDownloads(!Preferences.get().isBackgroundDownloadAllowed()); + } + private static class ScreenShotPagerAdapter extends FragmentStatePagerAdapter { private final String[] screenshots; From fbf7e98df960803a37388896a391963ec499ce68 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Fri, 6 Apr 2018 15:02:05 +0200 Subject: [PATCH 6/6] no need to export WifiStateChangeService to the world --- app/src/main/AndroidManifest.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 91ea6c205..bb851b750 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -270,7 +270,9 @@ - +