From e8994d503ebdcc47dafaff04a62cc829ddbea0cc Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 12 Sep 2022 16:11:33 -0300 Subject: [PATCH 01/11] Bump targetSdk to 31 (Android 12) --- bramble-android/build.gradle | 7 ++++--- briar-android/build.gradle | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bramble-android/build.gradle b/bramble-android/build.gradle index f23cb213b..e6761ac2f 100644 --- a/bramble-android/build.gradle +++ b/bramble-android/build.gradle @@ -5,8 +5,8 @@ apply plugin: 'witness' apply from: 'witness.gradle' android { - compileSdkVersion 30 - buildToolsVersion '30.0.3' + compileSdkVersion 33 + buildToolsVersion '33.0.0' packagingOptions { doNotStrip '**/*.so' @@ -14,12 +14,13 @@ android { defaultConfig { minSdkVersion 16 - targetSdkVersion 30 + targetSdkVersion 31 versionCode 10415 versionName "1.4.15" consumerProguardFiles 'proguard-rules.txt' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunnerArguments disableAnalytics: 'true' } compileOptions { diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 5dd1f297a..54fd73642 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -16,8 +16,8 @@ def getStdout = { command, defaultValue -> } android { - compileSdkVersion 30 - buildToolsVersion '30.0.3' + compileSdkVersion 33 + buildToolsVersion '33.0.0' packagingOptions { doNotStrip '**/*.so' @@ -25,7 +25,7 @@ android { defaultConfig { minSdkVersion 16 - targetSdkVersion 30 + targetSdkVersion 31 versionCode 10415 versionName "1.4.15" applicationId "org.briarproject.briar.android" From c04937b1fa2eda034c87c7fe0d61cd1f0aa120d9 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 12 Sep 2022 16:44:46 -0300 Subject: [PATCH 02/11] Do export only activities that need to react to external intents For some reason SettingsActivity does not need to be exported for it to be launched from system app settings. androidx.test.ext:junit needed to be upgraded because it somehow brought in an activity without exported attribute --- briar-android/build.gradle | 2 +- briar-android/src/main/AndroidManifest.xml | 44 ++++++++++++++++++- .../android/settings/SettingsActivity.java | 13 +++++- briar-android/witness.gradle | 3 -- 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 54fd73642..90f822a37 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -148,7 +148,7 @@ dependencies { testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version" androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput') - androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion" androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion" diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 007b82ee4..3b67935d4 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -49,8 +49,7 @@ android:networkSecurityConfig="@xml/network_security_config" android:supportsRtl="true" android:theme="@style/BriarTheme" - tools:ignore="GoogleAppIndexingWarning,UnusedAttribute" - tools:targetApi="16"> + tools:ignore="GoogleAppIndexingWarning,UnusedAttribute"> @@ -126,6 +130,7 @@ @@ -145,6 +150,7 @@ @@ -175,6 +183,7 @@ @@ -223,6 +236,7 @@ @@ -251,6 +267,7 @@ @@ -272,6 +290,7 @@ @@ -282,6 +301,7 @@ @@ -319,6 +342,7 @@ @@ -329,6 +353,7 @@ @@ -339,6 +364,7 @@ @@ -359,6 +385,7 @@ @@ -373,6 +400,7 @@ @@ -392,6 +421,7 @@ @@ -421,26 +453,31 @@ diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java index 00e4583a2..fbc882e07 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsActivity.java @@ -1,5 +1,6 @@ package org.briarproject.briar.android.settings; +import android.content.Intent; import android.os.Bundle; import android.view.MenuItem; @@ -18,6 +19,8 @@ import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback; +import static android.content.Intent.ACTION_MANAGE_NETWORK_USAGE; + @MethodsNotNullByDefault @ParametersNotNullByDefault public class SettingsActivity extends BriarActivity @@ -40,12 +43,18 @@ public class SettingsActivity extends BriarActivity actionBar.setDisplayHomeAsUpEnabled(true); } - // show display fragment after theme change - Bundle extras = getIntent().getExtras(); + Intent i = getIntent(); + Bundle extras = i.getExtras(); if (bundle == null && extras != null && extras.getBoolean(EXTRA_THEME_CHANGE, false)) { + // show display fragment after theme change FragmentManager fragmentManager = getSupportFragmentManager(); showNextFragment(fragmentManager, new DisplayFragment()); + } else if (bundle == null && + ACTION_MANAGE_NETWORK_USAGE.equals(i.getAction())) { + // show connection if coming from network settings + FragmentManager fragmentManager = getSupportFragmentManager(); + showNextFragment(fragmentManager, new ConnectionsFragment()); } setContentView(R.layout.activity_settings); diff --git a/briar-android/witness.gradle b/briar-android/witness.gradle index d2a4e2319..59bac6e09 100644 --- a/briar-android/witness.gradle +++ b/briar-android/witness.gradle @@ -54,14 +54,11 @@ dependencyVerification { 'androidx.test.espresso:espresso-core:3.3.0:espresso-core-3.3.0.aar:23ebf6014645e0c60aec7d1ed924d4d4c848ae8c3673b7d8d06b2ec6a56cafee', 'androidx.test.espresso:espresso-idling-resource:3.3.0:espresso-idling-resource-3.3.0.aar:29519b112731f289cc6e2f9b2eccc5ea72c754b04272bb93370f45d7e170a7c6', 'androidx.test.espresso:espresso-intents:3.3.0:espresso-intents-3.3.0.aar:5b6cd6aadce78edc705d93c1e81ace3b59be97128aca0e88fd9c5c176aa9bf10', - 'androidx.test.ext:junit:1.1.2:junit-1.1.2.aar:6c6ab120c640bf16fcaae69cb83c144d0ed6b6298562be0ac35e37ed969c0409', 'androidx.test.ext:junit:1.1.3:junit-1.1.3.aar:a97209d75a9a85815fa8934f5a4a320de1163ffe94e2f0b328c0c98a59660690', 'androidx.test.services:storage:1.4.0:storage-1.4.0.aar:35cfbf442abb83e5876cd5deb9de02ae047459f18f831097c5caa76d626bc38a', 'androidx.test.services:test-services:1.3.0:test-services-1.3.0.apk:1b88faab6864baf25c5d0b92a610c283c159a566e7a56c03307117fa1b542993', 'androidx.test.uiautomator:uiautomator:2.2.0:uiautomator-2.2.0.aar:2838e9d961dbffefbbd229a2bd4f6f82ac4fb2462975862a9e75e9ed325a3197', - 'androidx.test:core:1.3.0:core-1.3.0.aar:86549cae8c5b848f817e2c716e174c7dab61caf0b4df9848680eeb753089a337', 'androidx.test:core:1.4.0:core-1.4.0.aar:671284e62e393f16ceae1a99a3a9a07bf1aacda29f8fe7b6b884355ef34c09cf', - 'androidx.test:monitor:1.3.0:monitor-1.3.0.aar:f73a31306a783e63150c60c49e140dc38da39a1b7947690f4b73387b5ebad77e', 'androidx.test:monitor:1.4.0:monitor-1.4.0.aar:46a912a1e175f27a97521af3f50e5af87c22c49275dd2c57c043740012806325', 'androidx.test:orchestrator:1.3.0:orchestrator-1.3.0.apk:676f808d08a3d05050eae30c3b7d92ce5cef1e00a54d68355bb7e7d4b72366fe', 'androidx.test:rules:1.3.0:rules-1.3.0.aar:c1753946c498b0d5d7cf341cfed661f66915c4c9deb4ed10462a08ae33b2429a', From 113793045f8c771bc458b95d9d45ea6486da481e Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 12 Sep 2022 16:50:21 -0300 Subject: [PATCH 03/11] Set pending intents to be immutable --- .../bramble/system/AndroidTaskScheduler.java | 3 +- .../bramble/util/AndroidUtils.java | 8 ++++ .../AndroidNotificationManagerImpl.java | 40 ++++++++++++------- .../android/account/LockManagerImpl.java | 4 +- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskScheduler.java b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskScheduler.java index b57a91d1f..92fb8c4d5 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskScheduler.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/system/AndroidTaskScheduler.java @@ -41,6 +41,7 @@ import static java.util.logging.Level.INFO; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.system.AlarmConstants.EXTRA_PID; import static org.briarproject.bramble.system.AlarmConstants.REQUEST_ALARM; +import static org.briarproject.bramble.util.AndroidUtils.getImmutableFlags; @ThreadSafe @NotNullByDefault @@ -199,7 +200,7 @@ class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener { Intent i = new Intent(app, AlarmReceiver.class); i.putExtra(EXTRA_PID, android.os.Process.myPid()); return PendingIntent.getBroadcast(app, REQUEST_ALARM, i, - FLAG_CANCEL_CURRENT); + getImmutableFlags(FLAG_CANCEL_CURRENT)); } private class ScheduledTask diff --git a/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java b/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java index 7e3adef92..6fe18e080 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java @@ -22,6 +22,7 @@ import java.util.Scanner; import javax.annotation.Nullable; +import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.content.Context.MODE_PRIVATE; import static android.os.Build.VERSION.SDK_INT; import static java.lang.Runtime.getRuntime; @@ -139,4 +140,11 @@ public class AndroidUtils { public static boolean isUiThread() { return Looper.myLooper() == Looper.getMainLooper(); } + + public static int getImmutableFlags(int flags) { + if (SDK_INT >= 23) { + return FLAG_IMMUTABLE | flags; + } + return flags; + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java index 64b53bc93..08fe7067b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidNotificationManagerImpl.java @@ -69,6 +69,7 @@ import static android.app.Notification.DEFAULT_VIBRATE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.PendingIntent.getActivity; +import static android.app.PendingIntent.getBroadcast; import static android.content.Context.NOTIFICATION_SERVICE; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -83,6 +84,7 @@ import static androidx.core.app.NotificationCompat.PRIORITY_LOW; import static androidx.core.app.NotificationCompat.PRIORITY_MIN; import static androidx.core.app.NotificationCompat.VISIBILITY_SECRET; import static androidx.core.content.ContextCompat.getColor; +import static org.briarproject.bramble.util.AndroidUtils.getImmutableFlags; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID; import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.BLOG_URI; @@ -291,7 +293,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, b.setWhen(0); // Don't show the time b.setOngoing(true); Intent i = new Intent(appContext, SplashScreenActivity.class); - b.setContentIntent(getActivity(appContext, 0, i, 0)); + b.setContentIntent(getActivity(appContext, 0, i, getImmutableFlags(0))); if (SDK_INT >= 21) { b.setCategory(CATEGORY_SERVICE); b.setVisibility(VISIBILITY_SECRET); @@ -351,7 +353,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, TaskStackBuilder t = TaskStackBuilder.create(appContext); t.addParentStack(ConversationActivity.class); t.addNextIntent(i); - b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); + b.setContentIntent(t.getPendingIntent(nextRequestId++, + getImmutableFlags(0))); } else { // Touching the notification shows the contact list Intent i = new Intent(appContext, NavDrawerActivity.class); @@ -360,7 +363,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, TaskStackBuilder t = TaskStackBuilder.create(appContext); t.addParentStack(NavDrawerActivity.class); t.addNextIntent(i); - b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); + b.setContentIntent(t.getPendingIntent(nextRequestId++, + getImmutableFlags(0))); } notificationManager.notify(PRIVATE_MESSAGE_NOTIFICATION_ID, b.build()); @@ -399,7 +403,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, Intent i = new Intent(appContext, NotificationCleanupService.class); i.setData(uri); b.setDeleteIntent(PendingIntent.getService(appContext, nextRequestId++, - i, 0)); + i, getImmutableFlags(0))); } @Override @@ -454,7 +458,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, TaskStackBuilder t = TaskStackBuilder.create(appContext); t.addParentStack(GroupActivity.class); t.addNextIntent(i); - b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); + b.setContentIntent(t.getPendingIntent(nextRequestId++, + getImmutableFlags(0))); } else { // Touching the notification shows the group list Intent i = new Intent(appContext, NavDrawerActivity.class); @@ -463,7 +468,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, TaskStackBuilder t = TaskStackBuilder.create(appContext); t.addParentStack(NavDrawerActivity.class); t.addNextIntent(i); - b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); + b.setContentIntent(t.getPendingIntent(nextRequestId++, + getImmutableFlags(0))); } notificationManager.notify(GROUP_MESSAGE_NOTIFICATION_ID, b.build()); @@ -522,7 +528,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, TaskStackBuilder t = TaskStackBuilder.create(appContext); t.addParentStack(ForumActivity.class); t.addNextIntent(i); - b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); + b.setContentIntent(t.getPendingIntent(nextRequestId++, + getImmutableFlags(0))); } else { // Touching the notification shows the forum list Intent i = new Intent(appContext, NavDrawerActivity.class); @@ -531,7 +538,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, TaskStackBuilder t = TaskStackBuilder.create(appContext); t.addParentStack(NavDrawerActivity.class); t.addNextIntent(i); - b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); + b.setContentIntent(t.getPendingIntent(nextRequestId++, + getImmutableFlags(0))); } notificationManager.notify(FORUM_POST_NOTIFICATION_ID, b.build()); } @@ -583,7 +591,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, TaskStackBuilder t = TaskStackBuilder.create(appContext); t.addParentStack(NavDrawerActivity.class); t.addNextIntent(i); - b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); + b.setContentIntent( + t.getPendingIntent(nextRequestId++, getImmutableFlags(0))); notificationManager.notify(BLOG_POST_NOTIFICATION_ID, b.build()); } @@ -621,7 +630,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, TaskStackBuilder t = TaskStackBuilder.create(appContext); t.addParentStack(NavDrawerActivity.class); t.addNextIntent(i); - b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); + b.setContentIntent( + t.getPendingIntent(nextRequestId++, getImmutableFlags(0))); notificationManager.notify(CONTACT_ADDED_NOTIFICATION_ID, b.build()); @@ -662,12 +672,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, Intent i1 = new Intent(appContext, SignInReminderReceiver.class); i1.setAction(ACTION_DISMISS_REMINDER); PendingIntent actionIntent = - PendingIntent.getBroadcast(appContext, 0, i1, 0); + getBroadcast(appContext, 0, i1, getImmutableFlags(0)); b.addAction(0, actionTitle, actionIntent); Intent i = new Intent(appContext, SplashScreenActivity.class); i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP); - b.setContentIntent(getActivity(appContext, 0, i, 0)); + b.setContentIntent(getActivity(appContext, 0, i, getImmutableFlags(0))); notificationManager.notify(REMINDER_NOTIFICATION_ID, b.build()); } @@ -761,7 +771,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, Intent i = new Intent(appContext, HotspotActivity.class); i.addFlags(FLAG_ACTIVITY_SINGLE_TOP); i.setAction(ACTION_STOP_HOTSPOT); - PendingIntent actionIntent = getActivity(appContext, 0, i, 0); + PendingIntent actionIntent = + getActivity(appContext, 0, i, getImmutableFlags(0)); int icon = SDK_INT >= 21 ? R.drawable.ic_portable_wifi_off : 0; b.addAction(icon, actionTitle, actionIntent); notificationManager.notify(HOTSPOT_NOTIFICATION_ID, b.build()); @@ -803,7 +814,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, TaskStackBuilder t = TaskStackBuilder.create(appContext); t.addParentStack(MailboxActivity.class); t.addNextIntent(i); - b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); + b.setContentIntent( + t.getPendingIntent(nextRequestId++, getImmutableFlags(0))); notificationManager.notify(MAILBOX_PROBLEM_NOTIFICATION_ID, b.build()); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/account/LockManagerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/account/LockManagerImpl.java index 5ace463a3..d48500290 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/account/LockManagerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/LockManagerImpl.java @@ -39,6 +39,7 @@ import static android.os.Process.myPid; import static android.os.SystemClock.elapsedRealtime; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.util.AndroidUtils.getImmutableFlags; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.settings.SecurityFragment.PREF_SCREEN_LOCK; import static org.briarproject.briar.android.settings.SecurityFragment.PREF_SCREEN_LOCK_TIMEOUT; @@ -87,7 +88,8 @@ public class LockManagerImpl implements LockManager, Service, EventListener { new Intent(ACTION_LOCK, null, appContext, BriarService.class); i.putExtra(EXTRA_PID, myPid()); // When not using FLAG_UPDATE_CURRENT, the intent might have no extras - lockIntent = getService(appContext, 0, i, FLAG_UPDATE_CURRENT); + lockIntent = getService(appContext, 0, i, + getImmutableFlags(FLAG_UPDATE_CURRENT)); timeoutNever = Integer.parseInt( appContext.getString(R.string.pref_lock_timeout_value_never)); timeoutDefault = Integer.parseInt( From 824a9e112480b3487badfaec5ac89401987ca257 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 12 Sep 2022 17:05:52 -0300 Subject: [PATCH 04/11] Handle new BLUETOOTH_SCAN and BLUETOOTH_CONNECT permission We need to have those permissions before doing things like accessing the Bluetooth address. So we force-disable the Bluetooth plugin if the permission is not granted. The UI then forces the permission before allowing to enable the plugin. --- bramble-android/src/main/AndroidManifest.xml | 30 ++++-- .../bluetooth/AndroidBluetoothPlugin.java | 6 ++ .../system/AndroidSecureRandomProvider.java | 39 +++++--- .../bramble/util/AndroidUtils.java | 15 +++ .../bluetooth/AbstractBluetoothPlugin.java | 23 ++++- briar-android/src/main/AndroidManifest.xml | 2 - .../AddNearbyContactPermissionManager.java | 98 ++++++++++++++----- .../add/nearby/AddNearbyContactViewModel.java | 2 + .../connect/BluetoothConditionManager.java | 89 ++++++++++++++--- .../connect/BluetoothIntroFragment.java | 18 ++-- .../android/navdrawer/TransportsActivity.java | 52 +++++++++- .../reporting/BriarReportCollector.java | 20 ++-- .../android/settings/ConnectionsFragment.java | 53 ++++++++++ .../briar/android/util/UiUtils.java | 26 ++++- briar-android/src/main/res/values/strings.xml | 4 + 15 files changed, 387 insertions(+), 90 deletions(-) diff --git a/bramble-android/src/main/AndroidManifest.xml b/bramble-android/src/main/AndroidManifest.xml index 9a4bd9810..d1aebe349 100644 --- a/bramble-android/src/main/AndroidManifest.xml +++ b/bramble-android/src/main/AndroidManifest.xml @@ -1,15 +1,25 @@ - + - + - - - - - - + + + + + + + + + deviceSet = bt.getBondedDevices(); + for (BluetoothDevice device : deviceSet) + parcel.writeParcelable(device, 0); + } + out.write(parcel.marshall()); + parcel.recycle(); } - out.write(parcel.marshall()); - parcel.recycle(); } @Override @@ -77,7 +88,7 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider { .invoke(null, (Object) seed); // Mix the output of the Linux PRNG into the OpenSSL PRNG int bytesRead = (Integer) Class.forName( - "org.apache.harmony.xnet.provider.jsse.NativeCrypto") + "org.apache.harmony.xnet.provider.jsse.NativeCrypto") .getMethod("RAND_load_file", String.class, long.class) .invoke(null, "/dev/urandom", 1024); if (bytesRead != 1024) throw new IOException(); diff --git a/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java b/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java index 6fe18e080..ffb4216cc 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java @@ -22,9 +22,14 @@ import java.util.Scanner; import javax.annotation.Nullable; +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_SCAN; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.content.Context.MODE_PRIVATE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION.SDK_INT; +import static android.os.Process.myPid; +import static android.os.Process.myUid; import static java.lang.Runtime.getRuntime; import static java.util.Arrays.asList; import static org.briarproject.nullsafety.NullSafety.requireNonNull; @@ -49,6 +54,16 @@ public class AndroidUtils { return abis; } + public static boolean hasBtScanPermission(Context ctx) { + return SDK_INT < 31 || ctx.checkPermission(BLUETOOTH_SCAN, myPid(), + myUid()) == PERMISSION_GRANTED; + } + + public static boolean hasBtConnectPermission(Context ctx) { + return SDK_INT < 31 || ctx.checkPermission(BLUETOOTH_CONNECT, myPid(), + myUid()) == PERMISSION_GRANTED; + } + public static String getBluetoothAddress(Context ctx, BluetoothAdapter adapter) { return getBluetoothAddressAndMethod(ctx, adapter).getFirst(); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/AbstractBluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/AbstractBluetoothPlugin.java index 374c10d4f..18a9be894 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/AbstractBluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/AbstractBluetoothPlugin.java @@ -89,6 +89,16 @@ abstract class AbstractBluetoothPlugin implements BluetoothPlugin, private volatile String contactConnectionsUuid = null; + /** + * Override and return true, if the plugin is now allowed to access the + * Bluetooth hardware, so it must be + * {@link org.briarproject.bramble.api.plugin.Plugin.State#DISABLED} + * in {@link #start()}. + */ + protected boolean isBluetoothAccessible() { + return true; + } + abstract void initialiseAdapter() throws IOException; abstract boolean isAdapterEnabled(); @@ -176,19 +186,28 @@ abstract class AbstractBluetoothPlugin implements BluetoothPlugin, DEFAULT_PREF_PLUGIN_ENABLE); everConnected.set(settings.getBoolean(PREF_EVER_CONNECTED, DEFAULT_PREF_EVER_CONNECTED)); + // disable plugin, if conditions for enabling are not met + if (enabledByUser && !isBluetoothAccessible()) { + enabledByUser = false; + settings.putBoolean(PREF_PLUGIN_ENABLE, false); + callback.mergeSettings(settings); + } state.setStarted(enabledByUser); try { initialiseAdapter(); } catch (IOException e) { throw new PluginException(e); } - updateProperties(); - if (enabledByUser && isAdapterEnabled()) bind(); + if (enabledByUser) { + updateProperties(); + if (isAdapterEnabled()) bind(); + } } private void bind() { ioExecutor.execute(() -> { if (getState() != INACTIVE) return; + if (contactConnectionsUuid == null) updateProperties(); // Bind a server socket to accept connections from contacts SS ss; try { diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 3b67935d4..3518fe872 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -19,8 +19,6 @@ - - diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java index f40dbe094..3ff872c64 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java @@ -11,20 +11,31 @@ import androidx.core.util.Consumer; import androidx.fragment.app.FragmentActivity; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.BLUETOOTH_ADVERTISE; +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_SCAN; import static android.Manifest.permission.CAMERA; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION.SDK_INT; import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale; import static androidx.core.content.ContextCompat.checkSelfPermission; +import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission; +import static org.briarproject.bramble.util.AndroidUtils.hasBtScanPermission; +import static org.briarproject.briar.android.util.Permission.GRANTED; +import static org.briarproject.briar.android.util.Permission.PERMANENTLY_DENIED; +import static org.briarproject.briar.android.util.Permission.SHOW_RATIONALE; +import static org.briarproject.briar.android.util.Permission.UNKNOWN; import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled; import static org.briarproject.briar.android.util.UiUtils.showDenialDialog; import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; import static org.briarproject.briar.android.util.UiUtils.showRationale; +import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions; class AddNearbyContactPermissionManager { - private Permission cameraPermission = Permission.UNKNOWN; - private Permission locationPermission = Permission.UNKNOWN; + private Permission cameraPermission = UNKNOWN; + private Permission locationPermission = SDK_INT < 31 ? UNKNOWN : GRANTED; + private Permission bluetoothPermissions = SDK_INT < 31 ? GRANTED : UNKNOWN; private final FragmentActivity ctx; private final Consumer requestPermissions; @@ -39,23 +50,32 @@ class AddNearbyContactPermissionManager { } void resetPermissions() { - cameraPermission = Permission.UNKNOWN; - locationPermission = Permission.UNKNOWN; + cameraPermission = UNKNOWN; + locationPermission = SDK_INT < 31 ? UNKNOWN : GRANTED; + bluetoothPermissions = SDK_INT < 31 ? GRANTED : UNKNOWN; } static boolean areEssentialPermissionsGranted(Context ctx, boolean isBluetoothSupported) { int ok = PERMISSION_GRANTED; - return checkSelfPermission(ctx, CAMERA) == ok && - (SDK_INT < 23 || - checkSelfPermission(ctx, ACCESS_FINE_LOCATION) == ok || - !isBluetoothSupported); + boolean bluetoothOk; + if (!isBluetoothSupported || SDK_INT < 23) { + bluetoothOk = true; + } else if (SDK_INT < 31) { + bluetoothOk = checkSelfPermission(ctx, ACCESS_FINE_LOCATION) == ok; + } else { + bluetoothOk = hasBtConnectPermission(ctx) && + hasBtScanPermission(ctx) && + checkSelfPermission(ctx, BLUETOOTH_ADVERTISE) == ok; + } + return bluetoothOk && checkSelfPermission(ctx, CAMERA) == ok; } private boolean areEssentialPermissionsGranted() { - return cameraPermission == Permission.GRANTED && - (SDK_INT < 23 || locationPermission == Permission.GRANTED || - !isBluetoothSupported); + boolean bluetoothGranted = locationPermission == GRANTED && + bluetoothPermissions == GRANTED; + return cameraPermission == GRANTED && + (SDK_INT < 23 || !isBluetoothSupported || bluetoothGranted); } boolean checkPermissions() { @@ -63,31 +83,40 @@ class AddNearbyContactPermissionManager { if (locationEnabled && areEssentialPermissionsGranted()) return true; // If an essential permission has been permanently denied, ask the // user to change the setting - if (cameraPermission == Permission.PERMANENTLY_DENIED) { + if (cameraPermission == PERMANENTLY_DENIED) { showDenialDialog(ctx, R.string.permission_camera_title, R.string.permission_camera_denied_body); return false; } - if (isBluetoothSupported && - locationPermission == Permission.PERMANENTLY_DENIED) { + if (isBluetoothSupported && locationPermission == PERMANENTLY_DENIED) { showDenialDialog(ctx, R.string.permission_location_title, R.string.permission_location_denied_body); return false; } + if (isBluetoothSupported && + bluetoothPermissions == PERMANENTLY_DENIED) { + showDenialDialog(ctx, R.string.permission_bluetooth_title, + R.string.permission_bluetooth_denied_body); + return false; + } // Should we show the rationale for one or both permissions? - if (cameraPermission == Permission.SHOW_RATIONALE && - locationPermission == Permission.SHOW_RATIONALE) { + if (cameraPermission == SHOW_RATIONALE && + locationPermission == SHOW_RATIONALE) { showRationale(ctx, R.string.permission_camera_location_title, R.string.permission_camera_location_request_body, this::requestPermissions); - } else if (cameraPermission == Permission.SHOW_RATIONALE) { + } else if (cameraPermission == SHOW_RATIONALE) { showRationale(ctx, R.string.permission_camera_title, R.string.permission_camera_request_body, this::requestPermissions); - } else if (locationPermission == Permission.SHOW_RATIONALE) { + } else if (locationPermission == SHOW_RATIONALE) { showRationale(ctx, R.string.permission_location_title, R.string.permission_location_request_body, this::requestPermissions); + } else if (bluetoothPermissions == SHOW_RATIONALE) { + showRationale(ctx, R.string.permission_bluetooth_title, + R.string.permission_bluetooth_body, + this::requestPermissions); } else if (locationEnabled) { requestPermissions(); } else { @@ -99,7 +128,12 @@ class AddNearbyContactPermissionManager { private void requestPermissions() { String[] permissions; if (isBluetoothSupported) { - permissions = new String[] {CAMERA, ACCESS_FINE_LOCATION}; + if (SDK_INT < 31) { + permissions = new String[] {CAMERA, ACCESS_FINE_LOCATION}; + } else { + permissions = new String[] {CAMERA, BLUETOOTH_ADVERTISE, + BLUETOOTH_CONNECT, BLUETOOTH_SCAN}; + } } else { permissions = new String[] {CAMERA}; } @@ -108,19 +142,29 @@ class AddNearbyContactPermissionManager { void onRequestPermissionResult(Map result) { if (gotPermission(CAMERA, result)) { - cameraPermission = Permission.GRANTED; + cameraPermission = GRANTED; } else if (shouldShowRationale(CAMERA)) { - cameraPermission = Permission.SHOW_RATIONALE; + cameraPermission = SHOW_RATIONALE; } else { - cameraPermission = Permission.PERMANENTLY_DENIED; + cameraPermission = PERMANENTLY_DENIED; } if (isBluetoothSupported) { - if (gotPermission(ACCESS_FINE_LOCATION, result)) { - locationPermission = Permission.GRANTED; - } else if (shouldShowRationale(ACCESS_FINE_LOCATION)) { - locationPermission = Permission.SHOW_RATIONALE; + if (SDK_INT < 31) { + if (gotPermission(ACCESS_FINE_LOCATION, result)) { + locationPermission = GRANTED; + } else if (shouldShowRationale(ACCESS_FINE_LOCATION)) { + locationPermission = SHOW_RATIONALE; + } else { + locationPermission = PERMANENTLY_DENIED; + } } else { - locationPermission = Permission.PERMANENTLY_DENIED; + if (wasGrantedBluetoothPermissions(result)) { + bluetoothPermissions = GRANTED; + } else if (shouldShowRationale(BLUETOOTH_CONNECT)) { + bluetoothPermissions = SHOW_RATIONALE; + } else { + bluetoothPermissions = PERMANENTLY_DENIED; + } } } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactViewModel.java index f5c543145..4920e6da6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactViewModel.java @@ -1,5 +1,6 @@ package org.briarproject.briar.android.contact.add.nearby; +import android.annotation.SuppressLint; import android.app.Application; import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; @@ -250,6 +251,7 @@ class AddNearbyContactViewModel extends AndroidViewModel } @UiThread + @SuppressLint("MissingPermission") // we check permissions before private boolean isBluetoothReady() { if (bt == null || bluetoothPlugin == null) { // Continue without Bluetooth diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothConditionManager.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothConditionManager.java index 7a8ac1d9a..fc80179bc 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothConditionManager.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothConditionManager.java @@ -5,58 +5,101 @@ import android.content.Context; import org.briarproject.briar.R; import org.briarproject.briar.android.util.Permission; +import org.briarproject.briar.android.util.UiUtils; + +import java.util.Map; import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION.SDK_INT; import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale; +import static androidx.core.content.ContextCompat.checkSelfPermission; +import static org.briarproject.briar.android.util.Permission.GRANTED; +import static org.briarproject.briar.android.util.Permission.PERMANENTLY_DENIED; +import static org.briarproject.briar.android.util.Permission.SHOW_RATIONALE; +import static org.briarproject.briar.android.util.Permission.UNKNOWN; import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener; import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled; +import static org.briarproject.briar.android.util.UiUtils.requestBluetoothPermissions; import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; +import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions; class BluetoothConditionManager { - private Permission locationPermission = Permission.UNKNOWN; + private Permission locationPermission = SDK_INT < 31 ? UNKNOWN : GRANTED; + private Permission bluetoothPermissions = SDK_INT < 31 ? GRANTED : UNKNOWN; /** * Call this when the using activity or fragment starts, * because permissions might have changed while it was stopped. */ void reset() { - locationPermission = Permission.UNKNOWN; + locationPermission = SDK_INT < 31 ? UNKNOWN : GRANTED; + bluetoothPermissions = SDK_INT < 31 ? GRANTED : UNKNOWN; + } + + @UiThread + void requestPermissions(ActivityResultLauncher launcher) { + if (SDK_INT < 31) { + launcher.launch(new String[] {ACCESS_FINE_LOCATION}); + } else { + requestBluetoothPermissions(launcher); + } } @UiThread void onLocationPermissionResult(Activity activity, - @Nullable Boolean result) { - if (result != null && result) { - locationPermission = Permission.GRANTED; - } else if (shouldShowRequestPermissionRationale(activity, - ACCESS_FINE_LOCATION)) { - locationPermission = Permission.SHOW_RATIONALE; + @Nullable Map result) { + if (SDK_INT < 31) { + if (gotPermission(activity, result)) { + locationPermission = GRANTED; + } else if (shouldShowRequestPermissionRationale(activity, + ACCESS_FINE_LOCATION)) { + locationPermission = SHOW_RATIONALE; + } else { + locationPermission = PERMANENTLY_DENIED; + } } else { - locationPermission = Permission.PERMANENTLY_DENIED; + if (wasGrantedBluetoothPermissions(result)) { + bluetoothPermissions = GRANTED; + } else if (shouldShowRequestPermissionRationale(activity, + BLUETOOTH_CONNECT)) { + bluetoothPermissions = SHOW_RATIONALE; + } else { + bluetoothPermissions = PERMANENTLY_DENIED; + } } } - boolean areRequirementsFulfilled(Context ctx, - ActivityResultLauncher permissionRequest, + boolean areRequirementsFulfilled(FragmentActivity ctx, + ActivityResultLauncher permissionRequest, Runnable onLocationDenied) { boolean permissionGranted = - SDK_INT < 23 || locationPermission == Permission.GRANTED; + (SDK_INT < 23 || locationPermission == GRANTED) && + bluetoothPermissions == GRANTED; boolean locationEnabled = isLocationEnabled(ctx); if (permissionGranted && locationEnabled) return true; - if (locationPermission == Permission.PERMANENTLY_DENIED) { + if (locationPermission == PERMANENTLY_DENIED) { showDenialDialog(ctx, onLocationDenied); - } else if (locationPermission == Permission.SHOW_RATIONALE) { + } else if (locationPermission == SHOW_RATIONALE) { showRationale(ctx, permissionRequest); } else if (!locationEnabled) { showLocationDialog(ctx); + } else if (bluetoothPermissions == PERMANENTLY_DENIED) { + UiUtils.showDenialDialog(ctx, R.string.permission_bluetooth_title, + R.string.permission_bluetooth_denied_body); + } else if (bluetoothPermissions == SHOW_RATIONALE && SDK_INT >= 31) { + UiUtils.showRationale(ctx, R.string.permission_bluetooth_title, + R.string.permission_bluetooth_body, () -> + requestBluetoothPermissions(permissionRequest)); } return false; } @@ -72,13 +115,27 @@ class BluetoothConditionManager { } private void showRationale(Context ctx, - ActivityResultLauncher permissionRequest) { + ActivityResultLauncher permissionRequest) { new AlertDialog.Builder(ctx, R.style.BriarDialogTheme) .setTitle(R.string.permission_location_title) .setMessage(R.string.permission_location_request_body) .setPositiveButton(R.string.ok, (dialog, which) -> - permissionRequest.launch(ACCESS_FINE_LOCATION)) + permissionRequest.launch( + new String[] {ACCESS_FINE_LOCATION})) .show(); } + private boolean gotPermission(Context ctx, + @Nullable Map result) { + Boolean permissionResult = + result == null ? null : result.get(ACCESS_FINE_LOCATION); + return permissionResult == null ? isLocationPermissionGranted(ctx) : + permissionResult; + } + + private boolean isLocationPermissionGranted(Context ctx) { + return checkSelfPermission(ctx, ACCESS_FINE_LOCATION) == + PERMISSION_GRANTED; + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothIntroFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothIntroFragment.java index ed774bcd2..2e2413f51 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothIntroFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/connect/BluetoothIntroFragment.java @@ -1,6 +1,5 @@ package org.briarproject.briar.android.contact.connect; -import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; @@ -14,15 +13,17 @@ import org.briarproject.briar.android.util.ActivityLaunchers.RequestBluetoothDis import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault; +import java.util.Map; + import javax.inject.Inject; import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts.RequestPermission; +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.ViewModelProvider; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.widget.Toast.LENGTH_LONG; import static org.briarproject.briar.android.AppModule.getAndroidComponent; @@ -42,8 +43,8 @@ public class BluetoothIntroFragment extends Fragment { private final ActivityResultLauncher bluetoothDiscoverableRequest = registerForActivityResult(new RequestBluetoothDiscoverable(), this::onBluetoothDiscoverable); - private final ActivityResultLauncher permissionRequest = - registerForActivityResult(new RequestPermission(), + private final ActivityResultLauncher permissionRequest = + registerForActivityResult(new RequestMultiplePermissions(), this::onPermissionRequestResult); @Override @@ -80,12 +81,13 @@ public class BluetoothIntroFragment extends Fragment { // if the permission is already granted. // So we can use the request as a generic entry point // to the whole flow. - permissionRequest.launch(ACCESS_FINE_LOCATION); + conditionManager.requestPermissions(permissionRequest); } } - private void onPermissionRequestResult(@Nullable Boolean result) { - Activity a = requireActivity(); + private void onPermissionRequestResult( + @Nullable Map result) { + FragmentActivity a = requireActivity(); // update permission result in BluetoothConnecter conditionManager.onLocationPermissionResult(a, result); // what to do when the user denies granting the location permission diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportsActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportsActivity.java index 81de79aa4..283b9f0eb 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportsActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/TransportsActivity.java @@ -26,18 +26,24 @@ import org.briarproject.nullsafety.ParametersNotNullByDefault; import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.inject.Inject; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions; import androidx.annotation.ColorRes; import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.annotation.StringRes; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.SwitchCompat; import androidx.core.content.ContextCompat; import androidx.lifecycle.ViewModelProvider; +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.os.Build.VERSION.SDK_INT; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE; @@ -47,7 +53,13 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; +import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission; +import static org.briarproject.bramble.util.AndroidUtils.hasBtScanPermission; +import static org.briarproject.briar.android.util.UiUtils.requestBluetoothPermissions; +import static org.briarproject.briar.android.util.UiUtils.showDenialDialog; import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog; +import static org.briarproject.briar.android.util.UiUtils.showRationale; +import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -61,6 +73,11 @@ public class TransportsActivity extends BriarActivity { private PluginViewModel viewModel; private BaseAdapter transportsAdapter; + @RequiresApi(31) + private final ActivityResultLauncher requestPermissionLauncher = + registerForActivityResult(new RequestMultiplePermissions(), + this::handleBtPermissionResult); + @Override public void injectActivity(ActivityComponent component) { component.inject(this); @@ -149,8 +166,7 @@ public class TransportsActivity extends BriarActivity { view.findViewById(R.id.switchCompat); switchCompat.setText(getString(t.switchLabel)); switchCompat.setOnClickListener(v -> - viewModel.enableTransport(t.id, - switchCompat.isChecked())); + onClicked(t.id, switchCompat.isChecked())); switchCompat.setChecked(t.isSwitchChecked); TextView summary = view.findViewById(R.id.summary); @@ -203,6 +219,21 @@ public class TransportsActivity extends BriarActivity { }); } + private void onClicked(TransportId transportId, boolean enable) { + if (enable && SDK_INT >= 31 && + (!hasBtConnectPermission(this) || !hasBtScanPermission(this))) { + if (shouldShowRequestPermissionRationale(BLUETOOTH_CONNECT)) { + showRationale(this, R.string.permission_bluetooth_title, + R.string.permission_bluetooth_body, + this::requestBtPermissions); + } else { + requestBtPermissions(); + } + } else { + viewModel.enableTransport(transportId, enable); + } + } + private String getBulletString(@StringRes int resId) { return "\u2022 " + getString(resId); } @@ -316,6 +347,23 @@ public class TransportsActivity extends BriarActivity { return transport; } + @RequiresApi(31) + private void requestBtPermissions() { + requestBluetoothPermissions(requestPermissionLauncher); + } + + @RequiresApi(31) + private void handleBtPermissionResult(Map grantedMap) { + if (wasGrantedBluetoothPermissions(grantedMap)) { + viewModel.enableTransport(BluetoothConstants.ID, true); + } else { + transportsAdapter.notifyDataSetChanged(); + showDenialDialog(this, + R.string.permission_bluetooth_title, + R.string.permission_bluetooth_denied_body); + } + } + private static class Transport { private final TransportId id; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java index ef9e6ad94..66f90e48c 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java @@ -58,6 +58,8 @@ import static java.util.Locale.US; import static java.util.Objects.requireNonNull; import static java.util.TimeZone.getTimeZone; import static org.briarproject.bramble.util.AndroidUtils.getBluetoothAddressAndMethod; +import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission; +import static org.briarproject.bramble.util.AndroidUtils.hasBtScanPermission; import static org.briarproject.bramble.util.PrivacyUtils.scrubInetAddress; import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -273,12 +275,13 @@ class BriarReportCollector { // Is Bluetooth enabled? @SuppressLint("HardwareIds") - boolean btEnabled = bt.isEnabled() + boolean btEnabled = hasBtConnectPermission(ctx) && bt.isEnabled() && !isNullOrEmpty(bt.getAddress()); connectivityInfo.add("BluetoothEnabled", btEnabled); // Is Bluetooth connectable? - int scanMode = bt.getScanMode(); + @SuppressLint("MissingPermission") + int scanMode = hasBtScanPermission(ctx) ? bt.getScanMode() : -1; boolean btConnectable = scanMode == SCAN_MODE_CONNECTABLE || scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE; connectivityInfo.add("BluetoothConnectable", btConnectable); @@ -298,11 +301,14 @@ class BriarReportCollector { btLeAdvertise); } - Pair p = getBluetoothAddressAndMethod(ctx, bt); - String address = p.getFirst(); - String method = p.getSecond(); - connectivityInfo.add("BluetoothAddress", scrubMacAddress(address)); - connectivityInfo.add("BluetoothAddressMethod", method); + if (hasBtConnectPermission(ctx)) { + Pair p = getBluetoothAddressAndMethod(ctx, bt); + String address = p.getFirst(); + String method = p.getSecond(); + connectivityInfo.add("BluetoothAddress", + scrubMacAddress(address)); + connectivityInfo.add("BluetoothAddressMethod", method); + } } return new ReportItem("Connectivity", R.string.dev_report_connectivity, connectivityInfo); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/settings/ConnectionsFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/settings/ConnectionsFragment.java index afb594ab5..465de4441 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/settings/ConnectionsFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/settings/ConnectionsFragment.java @@ -8,18 +8,32 @@ import org.briarproject.briar.R; import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault; +import java.util.Map; + import javax.inject.Inject; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ViewModelProvider; import androidx.preference.ListPreference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.SwitchPreferenceCompat; +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.os.Build.VERSION.SDK_INT; +import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission; +import static org.briarproject.bramble.util.AndroidUtils.hasBtScanPermission; import static org.briarproject.briar.android.AppModule.getAndroidComponent; import static org.briarproject.briar.android.settings.SettingsActivity.enableAndPersist; +import static org.briarproject.briar.android.util.UiUtils.requestBluetoothPermissions; +import static org.briarproject.briar.android.util.UiUtils.showDenialDialog; +import static org.briarproject.briar.android.util.UiUtils.showRationale; +import static org.briarproject.briar.android.util.UiUtils.wasGrantedBluetoothPermissions; @MethodsNotNullByDefault @ParametersNotNullByDefault @@ -47,6 +61,11 @@ public class ConnectionsFragment extends PreferenceFragmentCompat { private SwitchPreferenceCompat torMobile; private SwitchPreferenceCompat torOnlyWhenCharging; + @RequiresApi(31) + private final ActivityResultLauncher requestPermissionLauncher = + registerForActivityResult(new RequestMultiplePermissions(), + this::handleBtPermissionResult); + @Override public void onAttach(@NonNull Context context) { super.onAttach(context); @@ -69,6 +88,25 @@ public class ConnectionsFragment extends PreferenceFragmentCompat { torNetwork.setSummaryProvider(viewModel.torSummaryProvider); + if (SDK_INT >= 31) { + enableBluetooth.setOnPreferenceChangeListener((p, value) -> { + FragmentActivity ctx = requireActivity(); + if (hasBtConnectPermission(ctx) && hasBtScanPermission(ctx)) { + return true; + } else if (shouldShowRequestPermissionRationale( + BLUETOOTH_CONNECT)) { + showRationale(ctx, R.string.permission_bluetooth_title, + R.string.permission_bluetooth_body, + this::requestBtPermissions); + // we don't update the preference directly, + // but do it via the launcher, if we got the permissions + return false; + } else { + requestBtPermissions(); + return false; + } + }); + } enableBluetooth.setPreferenceDataStore(connectionsManager.btStore); enableWifi.setPreferenceDataStore(connectionsManager.wifiStore); enableTor.setPreferenceDataStore(connectionsManager.torStore); @@ -115,4 +153,19 @@ public class ConnectionsFragment extends PreferenceFragmentCompat { requireActivity().setTitle(R.string.network_settings_title); } + @RequiresApi(31) + private void requestBtPermissions() { + requestBluetoothPermissions(requestPermissionLauncher); + } + + @RequiresApi(31) + private void handleBtPermissionResult(Map grantedMap) { + if (wasGrantedBluetoothPermissions(grantedMap)) { + enableBluetooth.setChecked(true); + } else { + showDenialDialog(requireActivity(), + R.string.permission_bluetooth_title, + R.string.permission_bluetooth_denied_body); + } + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index baa893964..b97ee36e3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -44,6 +44,7 @@ import org.briarproject.nullsafety.MethodsNotNullByDefault; import org.briarproject.nullsafety.ParametersNotNullByDefault; import java.util.Locale; +import java.util.Map; import java.util.logging.Logger; import androidx.activity.result.ActivityResultLauncher; @@ -70,6 +71,9 @@ import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; +import static android.Manifest.permission.BLUETOOTH_ADVERTISE; +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_SCAN; import static android.content.Context.KEYGUARD_SERVICE; import static android.content.Intent.CATEGORY_DEFAULT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; @@ -105,6 +109,7 @@ import static androidx.core.content.ContextCompat.getColor; import static androidx.core.content.ContextCompat.getSystemService; import static androidx.core.graphics.drawable.DrawableCompat.setTint; import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_RTL; +import static java.lang.Boolean.TRUE; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.DAYS; import static java.util.logging.Level.WARNING; @@ -346,10 +351,10 @@ public class UiUtils { /** * @return true if location is enabled, - * or it isn't required due to this being a SDK < 28 device. + * or it isn't required due to this being a device with SDK < 28 or >= 31. */ public static boolean isLocationEnabled(Context ctx) { - if (SDK_INT >= 28) { + if (SDK_INT >= 28 && SDK_INT < 31) { LocationManager lm = ctx.getSystemService(LocationManager.class); return lm.isLocationEnabled(); } else { @@ -625,4 +630,21 @@ public class UiUtils { } Toast.makeText(ctx, R.string.error_start_activity, LENGTH_LONG).show(); } + + @RequiresApi(31) + public static void requestBluetoothPermissions( + ActivityResultLauncher launcher) { + String[] perms = new String[] {BLUETOOTH_ADVERTISE, BLUETOOTH_CONNECT, + BLUETOOTH_SCAN}; + launcher.launch(perms); + } + + @RequiresApi(31) + public static boolean wasGrantedBluetoothPermissions( + @Nullable Map grantedMap) { + return grantedMap != null && + TRUE.equals(grantedMap.get(BLUETOOTH_ADVERTISE)) && + TRUE.equals(grantedMap.get(BLUETOOTH_CONNECT)) && + TRUE.equals(grantedMap.get(BLUETOOTH_SCAN)); + } } diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 3bf3edf49..0d3104986 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -782,6 +782,10 @@ Location setting Your device\'s location setting must be turned on to find other devices via Bluetooth. Please enable location to continue. You can disable it again afterwards. Enable location + Nearby devices permission + To use Bluetooth communication, Briar needs permission to find and connect to nearby devices. + You have denied access to nearby devices, but Briar needs this permission to use Bluetooth.\n\nPlease consider granting access. + QR code Show QR code fullscreen From e6616a8c36383aaeb5d92dcf11e4cea5fae0baa6 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 14 Sep 2022 15:21:40 -0300 Subject: [PATCH 05/11] Exclude all files from D2D transfers --- briar-android/src/main/AndroidManifest.xml | 1 + .../main/res/xml/backup_extraction_rules.xml | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 briar-android/src/main/res/xml/backup_extraction_rules.xml diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 3518fe872..96e2cb178 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -40,6 +40,7 @@ android:name="org.briarproject.briar.android.BriarApplicationImpl" android:allowBackup="false" android:banner="@mipmap/tv_banner" + android:dataExtractionRules="@xml/backup_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher_round" android:label="@string/app_name" diff --git a/briar-android/src/main/res/xml/backup_extraction_rules.xml b/briar-android/src/main/res/xml/backup_extraction_rules.xml new file mode 100644 index 000000000..c03286ff8 --- /dev/null +++ b/briar-android/src/main/res/xml/backup_extraction_rules.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + From e76701f9886f5b66367245ef4b3ebf2e6740f259 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 14 Sep 2022 15:46:39 -0300 Subject: [PATCH 06/11] Add current AppStandbyBucket to BriarReportCollector --- .../briar/android/reporting/BriarReportCollector.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java index 66f90e48c..08322c510 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportCollector.java @@ -7,6 +7,7 @@ package org.briarproject.briar.android.reporting; import android.annotation.SuppressLint; +import android.app.usage.UsageStatsManager; import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.content.pm.FeatureInfo; @@ -48,6 +49,7 @@ import androidx.annotation.Nullable; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; +import static android.content.Context.USAGE_STATS_SERVICE; import static android.content.Context.WIFI_P2P_SERVICE; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; @@ -122,6 +124,12 @@ class BriarReportCollector { .add("Product", Build.PRODUCT) .add("Model", Build.MODEL) .add("Brand", Build.BRAND); + if (SDK_INT >= 28) { + UsageStatsManager usageStatsManager = (UsageStatsManager) + ctx.getSystemService(USAGE_STATS_SERVICE); + deviceInfo.add("AppStandbyBucket", + usageStatsManager.getAppStandbyBucket()); + } return new ReportItem("DeviceInfo", R.string.dev_report_device_info, deviceInfo); } From e6c051fee4c3a39b1daacceaee02f6a40ad0d39e Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 14 Sep 2022 17:26:32 -0300 Subject: [PATCH 07/11] Require location for hotspot on Android 12+ This seems to be necessary. Without the location turned on, the hotspot does not start showing a p2p error. --- .../android/hotspot/ConditionManager29.java | 39 +++++++++++++++---- .../briar/android/util/UiUtils.java | 11 +++++- briar-android/src/main/res/values/strings.xml | 3 ++ 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager29.java b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager29.java index 071fbeeb6..866f9cc91 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager29.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/hotspot/ConditionManager29.java @@ -1,6 +1,7 @@ package org.briarproject.briar.android.hotspot; import android.content.Intent; +import android.location.LocationManager; import android.provider.Settings; import org.briarproject.briar.R; @@ -17,11 +18,13 @@ import androidx.annotation.RequiresApi; import androidx.core.util.Consumer; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.os.Build.VERSION.SDK_INT; import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale; import static java.lang.Boolean.TRUE; import static java.util.logging.Level.INFO; import static java.util.logging.Logger.getLogger; import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener; +import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; /** * This class ensures that the conditions to open a hotspot are fulfilled on @@ -63,13 +66,16 @@ class ConditionManager29 extends AbstractConditionManager { private boolean areEssentialPermissionsGranted() { boolean isWifiEnabled = wifiManager.isWifiEnabled(); + boolean isLocationEnabled = isLocationEnabled(); if (LOG.isLoggable(INFO)) { LOG.info(String.format("areEssentialPermissionsGranted(): " + "locationPermission? %s, " + - "wifiManager.isWifiEnabled()? %b", - locationPermission, isWifiEnabled)); + "wifiManager.isWifiEnabled()? %b" + + "isLocationEnabled? %b", + locationPermission, isWifiEnabled, isLocationEnabled)); } - return locationPermission == Permission.GRANTED && isWifiEnabled; + return locationPermission == Permission.GRANTED && isWifiEnabled && + isLocationEnabled; } @Override @@ -77,15 +83,22 @@ class ConditionManager29 extends AbstractConditionManager { if (areEssentialPermissionsGranted()) return true; if (locationPermission == Permission.UNKNOWN) { - locationRequest.launch(ACCESS_FINE_LOCATION); + requestPermissions(); + return false; + } + // ensure location is enabled (if needed on this device) + if (!isLocationEnabled()) { + showLocationDialog(ctx, false); return false; } // If the location permission has been permanently denied, ask the // user to change the setting if (locationPermission == Permission.PERMANENTLY_DENIED) { - showDenialDialog(ctx, R.string.permission_location_title, - R.string.permission_hotspot_location_denied_body, + int res = SDK_INT >= 31 ? + R.string.permission_hotspot_location_denied_precise_body : + R.string.permission_hotspot_location_denied_body; + showDenialDialog(ctx, R.string.permission_location_title, res, getGoToSettingsListener(ctx), () -> permissionUpdateCallback.accept(false)); return false; @@ -93,8 +106,10 @@ class ConditionManager29 extends AbstractConditionManager { // Should we show the rationale for location permission? if (locationPermission == Permission.SHOW_RATIONALE) { - showRationale(ctx, R.string.permission_location_title, - R.string.permission_hotspot_location_request_body, + int res = SDK_INT >= 31 ? + R.string.permission_hotspot_location_request_precise_body : + R.string.permission_hotspot_location_request_body; + showRationale(ctx, R.string.permission_location_title, res, this::requestPermissions, () -> permissionUpdateCallback.accept(false)); return false; @@ -134,4 +149,12 @@ class ConditionManager29 extends AbstractConditionManager { wifiRequest.launch(new Intent(Settings.Panel.ACTION_WIFI)); } + private boolean isLocationEnabled() { + if (SDK_INT >= 31) { + LocationManager lm = ctx.getSystemService(LocationManager.class); + return lm.isLocationEnabled(); + } + return true; + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index b97ee36e3..d3bcaa11f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -529,10 +529,19 @@ public class UiUtils { } public static void showLocationDialog(Context ctx) { + showLocationDialog(ctx, true); + } + + public static void showLocationDialog(Context ctx, boolean forBluetooth) { AlertDialog.Builder builder = new AlertDialog.Builder(ctx, R.style.BriarDialogTheme); builder.setTitle(R.string.permission_location_setting_title); - builder.setMessage(R.string.permission_location_setting_body); + if (forBluetooth) { + builder.setMessage(R.string.permission_location_setting_body); + } else { + builder.setMessage( + R.string.permission_location_setting_hotspot_body); + } builder.setNegativeButton(R.string.cancel, null); builder.setPositiveButton(R.string.permission_location_setting_button, (dialog, which) -> { diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 0d3104986..f01f4257b 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -781,6 +781,7 @@ You have denied access to your location, but Briar needs this permission to discover Bluetooth devices.\n\nPlease consider granting access. Location setting Your device\'s location setting must be turned on to find other devices via Bluetooth. Please enable location to continue. You can disable it again afterwards. + Your device\'s location setting must be turned on to create a Wi-Fi hotspot. Please enable location to continue. You can disable it again afterwards. Enable location Nearby devices permission To use Bluetooth communication, Briar needs permission to find and connect to nearby devices. @@ -812,7 +813,9 @@ Next To create a Wi-Fi hotspot, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone. + To create a Wi-Fi hotspot, Briar needs permission to access your precise location.\n\nBriar does not store your location or share it with anyone. You have denied access to your location, but Briar needs this permission to create a Wi-Fi hotspot.\n\nPlease consider granting access. + You have denied access to your precise location, but Briar needs this permission to create a Wi-Fi hotspot.\n\nPlease consider granting access. Wi-Fi setting To create a Wi-Fi hotspot, Briar needs to use Wi-Fi. Please enable it. From f395ab1cb5de0843ee6abb45439e5df8321459d2 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 14 Sep 2022 17:38:03 -0300 Subject: [PATCH 08/11] Disable our splash screen on Android 11+ in order to avoid two splash screens from being shown. --- briar-android/build.gradle | 2 +- briar-android/src/main/res/values-v31/attrs.xml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 briar-android/src/main/res/values-v31/attrs.xml diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 90f822a37..303b756cc 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -171,7 +171,7 @@ task verifyTranslations { lc.children().each { value -> translations.add(value.text()) } def folders = ["default", "en-US"] - def exceptions = ["values-night", "values-v21", "values-ldrtl"] + def exceptions = ["values-night", "values-v21", "values-v31", "values-ldrtl"] project.file("src/main/res").eachDir { dir -> if (dir.name.startsWith("values-") && !exceptions.contains(dir.name)) { folders.add(dir.name.substring(7).replace("-r", "-")) diff --git a/briar-android/src/main/res/values-v31/attrs.xml b/briar-android/src/main/res/values-v31/attrs.xml new file mode 100644 index 000000000..becc65199 --- /dev/null +++ b/briar-android/src/main/res/values-v31/attrs.xml @@ -0,0 +1,6 @@ + + + + 0 + + From 4a65bc1726653057e2cd0c53ce22a16d3af76453 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 14 Sep 2022 18:30:30 -0300 Subject: [PATCH 09/11] Update some libraries --- .idea/codeStyles/Project.xml | 3 - bramble-android/witness.gradle | 150 ++------ bramble-api/witness.gradle | 4 +- bramble-core/build.gradle | 2 +- bramble-core/witness.gradle | 37 +- bramble-java/witness.gradle | 29 +- briar-android/build.gradle | 4 +- briar-android/witness.gradle | 451 +++++++++-------------- briar-core/witness.gradle | 29 +- briar-headless/witness.gradle | 6 +- build.gradle | 6 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 12 files changed, 259 insertions(+), 466 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index ced0de8ab..079b23aca 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -35,9 +35,6 @@