From 2dc2974eaa263a7e2f40dd7ba02ddc6908287ba5 Mon Sep 17 00:00:00 2001 From: TuxPaper <725353+TuxPaper@users.noreply.github.com> Date: Thu, 18 Jan 2024 16:15:59 -0800 Subject: [PATCH] Warn user if "Start Core on Boot" needs special perms --- .../client/receiver/BootCompleteReceiver.java | 26 ++--- .../biglybt/android/client/RemoteUtils.java | 105 +++++++++++++----- .../client/activity/IntentHandler.java | 62 ++++++----- .../client/activity/LoginActivity.java | 4 +- .../fragment/ProfileSelectorFragment.java | 2 +- 5 files changed, 131 insertions(+), 68 deletions(-) diff --git a/app/src/coreFlavor/java/com/biglybt/android/client/receiver/BootCompleteReceiver.java b/app/src/coreFlavor/java/com/biglybt/android/client/receiver/BootCompleteReceiver.java index 707cd8d7b..bfc527420 100644 --- a/app/src/coreFlavor/java/com/biglybt/android/client/receiver/BootCompleteReceiver.java +++ b/app/src/coreFlavor/java/com/biglybt/android/client/receiver/BootCompleteReceiver.java @@ -19,15 +19,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.os.Build; +import android.os.SystemClock; import android.util.Log; import androidx.core.content.ContextCompat; import com.biglybt.android.client.*; import com.biglybt.android.client.service.BiglyBTService; -import com.biglybt.android.client.session.RemoteProfile; -import com.biglybt.util.Thunk; /** * Simple Broadcast Receiver that launches BiglyBT Core on boot if configured by @@ -42,6 +40,11 @@ public class BootCompleteReceiver @Override public void onReceive(final Context context, Intent intent) { + final long startedOn = SystemClock.elapsedRealtime(); + final IAnalyticsTracker tracker = AnalyticsTracker.getInstance(); + tracker.setDeviceName( + AndroidUtils.getDeviceNameForLogger(context.getContentResolver())); + if (AndroidUtils.DEBUG) { Log.d(TAG, "BroadcastReceiver.onReceive"); } @@ -51,9 +54,12 @@ public void onReceive(final Context context, Intent intent) { if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { return; } + if (AndroidUtils.DEBUG) { + Log.d(TAG, "BroadcastReceiver.onReceive ACTION_BOOT_COMPLETED"); + } final PendingResult pendingResult = goAsync(); - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); new Thread(() -> { try { if (RemoteUtils.getCoreProfile() == null @@ -64,21 +70,15 @@ public void onReceive(final Context context, Intent intent) { if (AndroidUtils.DEBUG) { Log.d(TAG, "startForegroundService BiglyBTService"); } - + ContextCompat.startForegroundService(context, new Intent(BiglyBTService.INTENT_ACTION_START, null, context, BiglyBTService.class)); //BiglyCoreUtils.startBiglyBTCoreService() does bindings which BroadcastReceivers shouldn't do } catch (Throwable t) { - // TODO: We get an IllegalAccessError on certain devices (Skyworth; Android 8.0) - // due to R8/Proguard. Hopefully it will get fixed, but since it's - // only been reported on one device on one OS version, it may not be - // We could warn the user, provide a non-optimized apk for them - // via non-Google Play - IAnalyticsTracker tracker = AnalyticsTracker.getInstance(); - tracker.setDeviceName( - AndroidUtils.getDeviceNameForLogger(context.getContentResolver())); + tracker.setLastViewName( + (SystemClock.elapsedRealtime() - startedOn) + "ms"); tracker.logError(t, stackTrace); } finally { pendingResult.finish(); diff --git a/app/src/main/java/com/biglybt/android/client/RemoteUtils.java b/app/src/main/java/com/biglybt/android/client/RemoteUtils.java index 316469ad2..b164f7e10 100644 --- a/app/src/main/java/com/biglybt/android/client/RemoteUtils.java +++ b/app/src/main/java/com/biglybt/android/client/RemoteUtils.java @@ -17,13 +17,18 @@ package com.biglybt.android.client; import android.Manifest.permission; +import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.PowerManager; +import android.provider.Settings; +import android.util.Log; import androidx.annotation.*; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; @@ -40,7 +45,11 @@ import com.biglybt.android.client.session.RemoteProfileFactory; import com.biglybt.android.client.session.SessionManager; import com.biglybt.android.util.JSONUtils; +import com.biglybt.util.RunnableUIThread; import com.biglybt.util.Thunk; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import net.grandcentrix.tray.TrayPreferences; import java.util.List; import java.util.Map; @@ -54,13 +63,11 @@ public class RemoteUtils //private static final String TAG = "RemoteUtils"; public static String lastOpenDebug = null; - /** - * - * @return true if opened immediately - */ - public static boolean openRemote(final AppCompatActivityM activity, + public static void openRemote(final AppCompatActivityM activity, final RemoteProfile remoteProfile, final boolean isMain, - final boolean closeActivityOnSuccess) { + final RunnableUIThread runOnNoOpen) { + + // Ensure remote is saved in AppPreferences OffThread.runOffUIThread(() -> { AppPreferences appPreferences = BiglyBTApp.getAppPreferences(); @@ -69,21 +76,73 @@ public static boolean openRemote(final AppCompatActivityM activity, } }); + // Ensure remote can autostart if option enabled + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M + && remoteProfile.isLocalHost()) { + AppPreferences appPreferences = BiglyBTApp.getAppPreferences(); + TrayPreferences prefs = appPreferences.getPreferences(); + if (prefs.getBoolean(CorePrefs.PREF_CORE_AUTOSTART, false)) { + + PowerManager pm = (PowerManager) activity.getSystemService( + Context.POWER_SERVICE); + boolean isIgnoringBatteryOptimizations = pm.isIgnoringBatteryOptimizations( + activity.getApplicationContext().getPackageName()); + + boolean isRestricted = false; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { + ActivityManager am = (ActivityManager) activity.getSystemService( + Context.ACTIVITY_SERVICE); + isRestricted = am.isBackgroundRestricted(); + } + + if (AndroidUtils.DEBUG) { + Log.d("BatteryOpt", + "isIgnoringBatteryOptimizations: " + + isIgnoringBatteryOptimizations + "; isRestricted: " + + isRestricted); + } + if (isRestricted || !isIgnoringBatteryOptimizations) { + OffThread.runOnUIThread(activity, false, (a) -> { + AlertDialog.Builder builder = new MaterialAlertDialogBuilder( + activity).setCancelable(true); + builder.setTitle(R.string.core_auto_start_on_boot); + builder.setMessage(R.string.core_auto_start_on_boot_auth); + builder.setPositiveButton(R.string.settings, (dialog, which) -> { + Intent intent = new Intent(); + intent.setAction( + Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); + activity.startActivity(intent); + if (runOnNoOpen != null) { + runOnNoOpen.run(); + } + }); + builder.setNegativeButton(R.string.disable_auto_start, + (dialog, which) -> OffThread.runOffUIThread(() -> { + prefs.put(CorePrefs.PREF_CORE_AUTOSTART, false); + + OffThread.runOnUIThread( + () -> reallyOpenRemote(activity, remoteProfile, isMain)); + })); + if (runOnNoOpen != null) { + builder.setOnCancelListener(dialog -> runOnNoOpen.run()); + } + builder.show(); + }); + return; + } + } + } + + // Ensure permissions List requiredPermissions = remoteProfile.getRequiredPermissions(); if (requiredPermissions.size() > 0) { - return activity.requestPermissions( - requiredPermissions.toArray(new String[0]), + activity.requestPermissions(requiredPermissions.toArray(new String[0]), new PermissionResultHandler() { @WorkerThread @Override public void onAllGranted() { - OffThread.runOnUIThread(() -> { - - if (closeActivityOnSuccess && !isMain) { - activity.finish(); - } - reallyOpenRemote(activity, remoteProfile, isMain); - }); + OffThread.runOnUIThread( + () -> reallyOpenRemote(activity, remoteProfile, isMain)); } @WorkerThread @@ -96,23 +155,19 @@ public void onSomeDenied(PermissionRequestResults results) { if (denies.size() > 0) { AndroidUtilsUI.showDialog(activity, R.string.permission_denied, R.string.error_client_requires_permissions); + if (runOnNoOpen != null) { + OffThread.runOnUIThread(runOnNoOpen); + } } else { - OffThread.runOnUIThread(() -> { - if (closeActivityOnSuccess && !isMain) { - activity.finish(); - } - reallyOpenRemote(activity, remoteProfile, isMain); - }); + OffThread.runOnUIThread( + () -> reallyOpenRemote(activity, remoteProfile, isMain)); } } }); + return; } - if (closeActivityOnSuccess && !isMain) { - activity.finish(); - } reallyOpenRemote(activity, remoteProfile, isMain); - return true; } @Thunk diff --git a/app/src/main/java/com/biglybt/android/client/activity/IntentHandler.java b/app/src/main/java/com/biglybt/android/client/activity/IntentHandler.java index 010a02032..88ba6e3d2 100644 --- a/app/src/main/java/com/biglybt/android/client/activity/IntentHandler.java +++ b/app/src/main/java/com/biglybt/android/client/activity/IntentHandler.java @@ -21,6 +21,8 @@ import android.os.Bundle; import android.util.Log; +import androidx.annotation.AnyThread; +import androidx.annotation.WorkerThread; import androidx.fragment.app.FragmentManager; import com.biglybt.android.client.*; @@ -50,25 +52,22 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); - handleIntent(getIntent()); + OffThread.runOffUIThread(() -> handleIntent(getIntent())); } - private void handleIntent(Intent intent) { - OffThread.runOffUIThread(() -> { - boolean handled = handleIntent2(intent); - if (!handled) { - // .commit will send it over to UI thread for us - FragmentManager fm = getSupportFragmentManager(); - if (fm.isDestroyed() || fm.findFragmentByTag("PSF") != null) { - return; - } - fm.beginTransaction().add(R.id.fragment_container, - new ProfileSelectorFragment(), "PSF").commit(); - } - }); + @AnyThread + private void showProfileSelector() { + // .commit will send it over to UI thread for us + FragmentManager fm = getSupportFragmentManager(); + if (fm.isDestroyed() || fm.findFragmentByTag("PSF") != null) { + return; + } + fm.beginTransaction().add(R.id.fragment_container, + new ProfileSelectorFragment(), "PSF").commit(); } - private boolean handleIntent2(Intent intent) { + @WorkerThread + private void handleIntent(Intent intent) { boolean forceProfileListOpen = false; if (AndroidUtils.DEBUG) { @@ -124,17 +123,19 @@ private boolean handleIntent2(Intent intent) { openAfterEdit = true; RemoteUtils.editProfile(remoteProfile, getSupportFragmentManager(), "1".equals(reqPW)); - return false; + showProfileSelector(); + return; } else if (ac.length() < 100) { RemoteProfile remoteProfile = RemoteProfileFactory.create("vuze", ac); - return RemoteUtils.openRemote(this, remoteProfile, true, true); + RemoteUtils.openRemote(this, remoteProfile, true, + () -> showProfileSelector()); + return; } } - // check for http[s]://remote.vuze.com/ac=* - if (("remote.vuze.com".equals(host) - || "remote.biglybt.com".equals(host)) + // check for http[s]://remote.biglybt.com/ac=* + if ("remote.biglybt.com".equals(host) && data.getQueryParameter("ac") != null) { String ac = data.getQueryParameter("ac"); if (AndroidUtils.DEBUG) { @@ -144,7 +145,9 @@ private boolean handleIntent2(Intent intent) { if (ac != null && ac.length() < 100) { RemoteProfile remoteProfile = RemoteProfileFactory.create("vuze", ac); - return RemoteUtils.openRemote(this, remoteProfile, true, true); + RemoteUtils.openRemote(this, remoteProfile, true, + () -> showProfileSelector()); + return; } } } catch (Exception e) { @@ -169,7 +172,7 @@ private boolean handleIntent2(Intent intent) { } // New User: Send them to Login (Account Creation) LoginActivity.launch(this); - return true; + return; } if (!clearTop && noSavedInstanceState) { @@ -178,7 +181,8 @@ private boolean handleIntent2(Intent intent) { if (AndroidUtils.DEBUG) { Log.d(TAG, "No last remote"); } - return false; + showProfileSelector(); + return; } if (intent.getData() == null @@ -188,14 +192,17 @@ private boolean handleIntent2(Intent intent) { + appPreferences.getRemotes().length); } try { - return RemoteUtils.openRemote(this, remoteProfile, true, true); + RemoteUtils.openRemote(this, remoteProfile, true, + () -> showProfileSelector()); + return; } catch (Throwable t) { AnalyticsTracker.getInstance(this).logError(t); } } } } - return false; + + showProfileSelector(); } @Override @@ -205,14 +212,15 @@ protected void onNewIntent(Intent intent) { Log.d(TAG, "onNewIntent " + intent); } setIntent(intent); - handleIntent(intent); + OffThread.runOffUIThread(() -> handleIntent(intent)); } @Override public void profileEditDone(RemoteProfile oldProfile, RemoteProfile newProfile) { if (openAfterEdit) { - RemoteUtils.openRemote(this, newProfile, true, true); + RemoteUtils.openRemote(this, newProfile, true, + () -> showProfileSelector()); } } } diff --git a/app/src/main/java/com/biglybt/android/client/activity/LoginActivity.java b/app/src/main/java/com/biglybt/android/client/activity/LoginActivity.java index 99cf2e70e..fa138eaa7 100644 --- a/app/src/main/java/com/biglybt/android/client/activity/LoginActivity.java +++ b/app/src/main/java/com/biglybt/android/client/activity/LoginActivity.java @@ -405,7 +405,7 @@ private void loginButtonClicked(View v) { private void openRemote(String ac) { RemoteProfile remoteProfile = RemoteProfileFactory.create( RemoteProfile.DEFAULT_USERNAME, ac); - RemoteUtils.openRemote(this, remoteProfile, false, false); + RemoteUtils.openRemote(this, remoteProfile, false, null); } @SuppressWarnings("UnusedParameters") @@ -443,7 +443,7 @@ private void createCore() { @Override public void profileEditDone(RemoteProfile oldProfile, RemoteProfile newProfile) { - RemoteUtils.openRemote(this, newProfile, false, false); + RemoteUtils.openRemote(this, newProfile, false, null); } } diff --git a/app/src/main/java/com/biglybt/android/client/fragment/ProfileSelectorFragment.java b/app/src/main/java/com/biglybt/android/client/fragment/ProfileSelectorFragment.java index d529149ce..1ed32dcc0 100644 --- a/app/src/main/java/com/biglybt/android/client/fragment/ProfileSelectorFragment.java +++ b/app/src/main/java/com/biglybt/android/client/fragment/ProfileSelectorFragment.java @@ -127,7 +127,7 @@ public void onActivityCreated(@Nullable Bundle savedInstanceState) { if (item instanceof RemoteProfile) { RemoteProfile remote = (RemoteProfile) item; boolean isMain = activity.getIntent().getData() != null; - RemoteUtils.openRemote(activity, remote, isMain, isMain); + RemoteUtils.openRemote(activity, remote, isMain, null); } });