Skip to content

Commit 94ea52f

Browse files
strsebim7mortalflodolo
authored
VPN-7203 - Add a warning when not running with "unrestricted" background usage permission (#11169)
* VPN-7203 - Part 1: Add a QML API for Androids BatteryOptimization This patch picks up PR #10897 - and wires up android batteryOptimization status to QML Co-authored-by: im7mortal <5336231+im7mortal@users.noreply.github.com> * Part 2: Add a Message Card * Part 3: Remember Popup dismissal * Linting * reset unrelated file * Use matts cool new component * Update src/translations/strings.yaml Co-authored-by: Francesco Lodolo <flod@lodolo.net> * Remove unneded line * Update string * Update src/translations/strings.yaml Co-authored-by: Francesco Lodolo <flod@lodolo.net> --------- Co-authored-by: im7mortal <5336231+im7mortal@users.noreply.github.com> Co-authored-by: Francesco Lodolo <flod@lodolo.net>
1 parent 05ab34d commit 94ea52f

10 files changed

Lines changed: 216 additions & 0 deletions

File tree

android/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
1212
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
1313
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
1414
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
15+
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
1516
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
1617

1718
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package org.mozilla.firefox.qt.common
6+
7+
import android.content.Context
8+
import android.content.Intent
9+
import android.content.pm.PackageManager
10+
import android.net.Uri
11+
import android.os.Build
12+
import android.os.PowerManager
13+
import android.provider.Settings
14+
15+
object BatteryOptimizationHelper {
16+
17+
@JvmStatic
18+
fun isIgnoringBatteryOptimizations(context: Context): Boolean {
19+
return try {
20+
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
21+
pm.isIgnoringBatteryOptimizations(context.packageName)
22+
} catch (e: Exception) { true }
23+
}
24+
25+
@JvmStatic
26+
fun hasRequestIgnoreBatteryOptimizationsPermission(context: Context): Boolean {
27+
return try {
28+
context.packageManager.checkPermission(
29+
"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS",
30+
context.packageName) == PackageManager.PERMISSION_GRANTED
31+
} catch (e: Exception) { false }
32+
}
33+
34+
@JvmStatic
35+
fun getRequestIgnoreBatteryOptimizationsIntent(context: Context): Intent? {
36+
return try {
37+
if (hasRequestIgnoreBatteryOptimizationsPermission(context)) {
38+
Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
39+
data = Uri.parse("package:${context.packageName}")
40+
}
41+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
42+
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
43+
data = Uri.parse("package:${context.packageName}")
44+
}
45+
} else {
46+
Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)
47+
}
48+
} catch (e: Exception) { null }
49+
}
50+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
#include "androidbatteryoptimizer.h"
6+
7+
#include <QJniObject>
8+
9+
#include "androidcommons.h"
10+
11+
constexpr auto BATTERY_HELPER_CLASS =
12+
"org/mozilla/firefox/qt/common/BatteryOptimizationHelper";
13+
14+
// static
15+
bool AndroidBatteryOptimizer::batteryOptimizationEnabled() {
16+
QJniObject ctx = AndroidCommons::getActivity();
17+
jboolean ignoring = QJniObject::callStaticMethod<jboolean>(
18+
BATTERY_HELPER_CLASS, "isIgnoringBatteryOptimizations",
19+
"(Landroid/content/Context;)Z", ctx.object<jobject>());
20+
return !ignoring;
21+
}
22+
23+
// static
24+
bool AndroidBatteryOptimizer::canTriggerIntent() {
25+
QJniObject ctx = AndroidCommons::getActivity();
26+
return (bool)QJniObject::callStaticMethod<jboolean>(
27+
BATTERY_HELPER_CLASS, "hasRequestIgnoreBatteryOptimizationsPermission",
28+
"(Landroid/content/Context;)Z", ctx.object<jobject>());
29+
}
30+
31+
// static
32+
void AndroidBatteryOptimizer::triggerBatteryOptimizationIntent() {
33+
QJniObject ctx = AndroidCommons::getActivity();
34+
QJniObject intent = QJniObject::callStaticObjectMethod(
35+
BATTERY_HELPER_CLASS, "getRequestIgnoreBatteryOptimizationsIntent",
36+
"(Landroid/content/Context;)Landroid/content/Intent;",
37+
ctx.object<jobject>());
38+
if (!intent.isValid()) {
39+
return;
40+
}
41+
ctx.callMethod<void>("startActivity", "(Landroid/content/Intent;)V",
42+
intent.object<jobject>());
43+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
#ifndef ANDROIDBATTERYOPTIMIZER_H
6+
#define ANDROIDBATTERYOPTIMIZER_H
7+
8+
class AndroidBatteryOptimizer {
9+
public:
10+
// true → optimization IS enabled (VPN at risk)
11+
// false → app is exempted (good)
12+
static bool batteryOptimizationEnabled();
13+
14+
// true → app holds REQUEST_IGNORE_BATTERY_OPTIMIZATIONS; system dialog shown
15+
// directly
16+
static bool canTriggerIntent();
17+
18+
// Opens battery settings (system dialog or app details depending on
19+
// permission/SDK)
20+
static void triggerBatteryOptimizationIntent();
21+
};
22+
23+
#endif // ANDROIDBATTERYOPTIMIZER_H

src/platforms/android/sources.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ target_sources(shared-sources INTERFACE
77
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidcryptosettings.cpp
88
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidcommons.cpp
99
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidcommons.h
10+
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidbatteryoptimizer.h
11+
${CMAKE_CURRENT_SOURCE_DIR}/platforms/android/androidbatteryoptimizer.cpp
1012
)
1113

1214
target_link_libraries(shared-sources INTERFACE -ljnigraphics)

src/settingslist.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,16 @@ SETTING_BOOL(onboardingCompleted, // getter
325325
false // sensitive (do not log)
326326
)
327327

328+
SETTING_BOOL(hasDismissedBatteryOptimization, // getter
329+
setHasDismissedBatteryOptimization, // setter
330+
removeHasDismissedBatteryOptimization, // remover
331+
hasHasDismissedBatteryOptimization, // has
332+
"hasDismissedBatteryOptimization", // key
333+
false, // default value
334+
true, // remove when reset (clears on logout)
335+
false // sensitive (do not log)
336+
)
337+
328338
SETTING_BOOL(onboardingDataCollectionEnabled, // getter
329339
setOnboardingDataCollectionEnabled, // setter
330340
removeOnboardingDataCollectionEnabled, // remover

src/translations/strings.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,12 @@ settings:
365365
dnsSettingsWarning: These options may cause some websites to break or not display content correctly.
366366
dnsSettingsInputPlaceholder: Enter custom DNS
367367
dnsSettingsDisconnectWarning: Changing these settings will cause the VPN to disconnect and reconnect automatically.
368+
batteryOptimizationWarning:
369+
value: On some devices, Mozilla VPN may be disconnected in the background. If you’ve experienced this, allow it to run in the background.
370+
comment: Warning shown in the settings screen on Android when battery optimization is enabled for the app.
371+
batteryOptimizationWarningButton:
372+
value: Allow background permissions
373+
comment: Button label for the call to action to allow background permissions on Android.
368374
appExclusionClearAllApps:
369375
value: Clear all
370376
comment: Button label to clear all apps from the exclusions list

src/ui/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,10 @@ target_include_directories(mozillavpn-uiplugin PUBLIC
166166
${CMAKE_CURRENT_SOURCE_DIR}/types
167167
)
168168

169+
target_sources(mozillavpn-ui PRIVATE singletons/BatteryOptimizer.h)
170+
169171
if(${CMAKE_SYSTEM_NAME} STREQUAL "Android")
172+
target_compile_definitions(mozillavpn-ui PRIVATE MZ_ANDROID)
170173
target_sources(mozillavpn-ui PRIVATE singletons/VPNAndroidUtils.h)
171174
target_sources(mozillavpn-ui PRIVATE singletons/VPNAndroidCommons.h)
172175
endif()

src/ui/screens/settings/ViewSettingsMenu.qml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,30 @@ MZViewBase {
2222
spacing: MZTheme.theme.windowMargin
2323
Layout.fillWidth: true
2424

25+
MZInformationCard {
26+
id: batteryOptimizationCard
27+
visible: !MZSettings.hasDismissedBatteryOptimization
28+
&& BatteryOptimizer.batteryOptimizationEnabled
29+
&& BatteryOptimizer.canTriggerIntent
30+
cardType: MZInformationCard.CardType.Warning
31+
Layout.preferredWidth: Math.min(window.width - MZTheme.theme.windowMargin * 2,
32+
MZTheme.theme.navBarMaxWidth)
33+
Layout.minimumHeight: batteryWarningText.height + MZTheme.theme.windowMargin * 2
34+
Layout.alignment: Qt.AlignHCenter
35+
36+
_infoContent: MZTextBlock {
37+
id: batteryWarningText
38+
Layout.fillWidth: true
39+
text: MZI18n.SettingsBatteryOptimizationWarning
40+
}
41+
_buttonAction: function() {
42+
MZSettings.hasDismissedBatteryOptimization = true
43+
BatteryOptimizer.triggerBatteryOptimizationIntent()
44+
}
45+
_buttonText : MZI18n.SettingsBatteryOptimizationWarningButton
46+
47+
}
48+
2549
ColumnLayout {
2650
Layout.fillWidth: true
2751
Layout.leftMargin: MZTheme.theme.windowMargin /2
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
#ifndef BATTERYOPTIMIZER_H
6+
#define BATTERYOPTIMIZER_H
7+
8+
#include <QObject>
9+
#include <QQmlEngine>
10+
11+
#ifdef MZ_ANDROID
12+
# include "platforms/android/androidbatteryoptimizer.h"
13+
#endif
14+
15+
class BatteryOptimizer : public QObject {
16+
Q_OBJECT
17+
QML_ELEMENT
18+
QML_SINGLETON
19+
20+
Q_PROPERTY(bool batteryOptimizationEnabled READ batteryOptimizationEnabled
21+
NOTIFY stateChanged)
22+
Q_PROPERTY(bool canTriggerIntent READ canTriggerIntent NOTIFY stateChanged)
23+
24+
public:
25+
bool batteryOptimizationEnabled() const {
26+
#ifdef MZ_ANDROID
27+
return AndroidBatteryOptimizer::batteryOptimizationEnabled();
28+
#else
29+
return false;
30+
#endif
31+
}
32+
33+
bool canTriggerIntent() const {
34+
#ifdef MZ_ANDROID
35+
return AndroidBatteryOptimizer::canTriggerIntent();
36+
#else
37+
return false;
38+
#endif
39+
}
40+
41+
Q_INVOKABLE void triggerBatteryOptimizationIntent() {
42+
#ifdef MZ_ANDROID
43+
AndroidBatteryOptimizer::triggerBatteryOptimizationIntent();
44+
#endif
45+
}
46+
47+
// Call from QML after returning from the settings screen to refresh bindings.
48+
Q_INVOKABLE void refreshState() { emit stateChanged(); }
49+
50+
signals:
51+
void stateChanged();
52+
};
53+
54+
#endif // BATTERYOPTIMIZER_H

0 commit comments

Comments
 (0)