Skip to content

Commit 716cbef

Browse files
committed
Merge branch 'release/5.202.0' into main
2 parents 18d8eeb + ca3c07d commit 716cbef

File tree

221 files changed

+6172
-784
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

221 files changed

+6172
-784
lines changed

anrs/anrs-impl/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies {
3636
implementation project(':verified-installation-api')
3737
implementation project(':library-loader-api')
3838
implementation project(':feature-toggles-api')
39+
implementation project(':data-store-api')
3940

4041
implementation AndroidX.core.ktx
4142
implementation KotlinX.coroutines.core
@@ -48,6 +49,7 @@ dependencies {
4849
implementation AndroidX.room.rxJava2
4950

5051
testImplementation project(':common-test')
52+
implementation project(':data-store-test')
5153
testImplementation Testing.junit4
5254
testImplementation AndroidX.archCore.testing
5355
testImplementation AndroidX.test.ext.junit

anrs/anrs-impl/src/main/cpp/jni.cpp

Lines changed: 1 addition & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,6 @@
99

1010
///////////////////////////////////////////////////////////////////////////
1111

12-
static JavaVM *JVM = NULL;
13-
jclass clsCrash;
14-
jobject CLASS_JVM_CRASH = NULL;
15-
16-
17-
static jobject jniGlobalRef(JNIEnv *env, jobject cls);
18-
static jclass jniFindClass(JNIEnv *env, const char *name);
19-
static jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature);
20-
2112
int loglevel = 0;
2213
char appVersion[256];
2314
char pname[256];
@@ -35,63 +26,6 @@ void __platform_log_print(int prio, const char *tag, const char *fmt, ...) {
3526
va_end(argptr);
3627
}
3728

38-
///////////////////////////////////////////////////////////////////////////
39-
// JNI utils
40-
///////////////////////////////////////////////////////////////////////////
41-
42-
static jobject jniGlobalRef(JNIEnv *env, jobject cls) {
43-
jobject gcls = env->NewGlobalRef(cls);
44-
if (gcls == NULL)
45-
log_print(ANDROID_LOG_ERROR, "Global ref failed (out of memory?)");
46-
return gcls;
47-
}
48-
49-
static jclass jniFindClass(JNIEnv *env, const char *name) {
50-
jclass cls = env->FindClass(name);
51-
if (cls == NULL)
52-
log_print(ANDROID_LOG_ERROR, "Class %s not found", name);
53-
return cls;
54-
}
55-
56-
static jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature) {
57-
jmethodID method = env->GetMethodID(cls, name, signature);
58-
if (method == NULL) {
59-
log_print(ANDROID_LOG_ERROR, "Method %s %s not found", name, signature);
60-
}
61-
return method;
62-
}
63-
64-
///////////////////////////////////////////////////////////////////////////
65-
// JNI lifecycle
66-
///////////////////////////////////////////////////////////////////////////
67-
68-
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
69-
JNIEnv *env;
70-
if ((vm)->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
71-
log_print(ANDROID_LOG_INFO, "JNI load GetEnv failed");
72-
return -1;
73-
}
74-
75-
jint rs = env->GetJavaVM(&JVM);
76-
if (rs != JNI_OK) {
77-
log_print(ANDROID_LOG_ERROR, "Could not get JVM");
78-
return -1;
79-
}
80-
81-
return JNI_VERSION_1_6;
82-
}
83-
84-
void JNI_OnUnload(JavaVM *vm, void *reserved) {
85-
log_print(ANDROID_LOG_INFO, "JNI unload");
86-
87-
JNIEnv *env;
88-
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
89-
log_print(ANDROID_LOG_INFO, "JNI load GetEnv failed");
90-
else {
91-
env->DeleteGlobalRef(clsCrash);
92-
}
93-
}
94-
9529
///////////////////////////////////////////////////////////////////////////
9630
// native<>JVM interface
9731
///////////////////////////////////////////////////////////////////////////
@@ -129,13 +63,9 @@ Java_com_duckduckgo_app_anr_ndk_NativeCrashInit_jni_1register_1sighandler(
12963
// get and set isCustomTabs
13064
isCustomTab = customtab_;
13165

132-
clsCrash = env->GetObjectClass(instance);
133-
const char *emptyParamVoidSig = "()V";
134-
CLASS_JVM_CRASH = env->NewGlobalRef(instance);
135-
13666
send_crash_handle_init_pixel();
13767

138-
log_print(ANDROID_LOG_ERROR, "Native crash handler successfully initialized.");
68+
log_print(ANDROID_LOG_ERROR, "Native crash handler successfully initialized on %s.", pname);
13969
}
14070

14171
extern "C" JNIEXPORT void JNICALL

anrs/anrs-impl/src/main/cpp/pixel.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ void send_crash_pixel() {
5858
char path[2048];
5959
sprintf(path, "/t/m_app_native_crash_android?appVersion=%s&pn=%s&customTab=%s", appVersion, pname, isCustomTab ? "true" : "false");
6060
send_request(host, path);
61-
log_print(ANDROID_LOG_ERROR, "Native crash pixel sent");
61+
log_print(ANDROID_LOG_ERROR, "Native crash pixel sent on %s", pname);
6262
}
6363

6464
void send_crash_handle_init_pixel() {
6565
const char* host = "improving.duckduckgo.com";
6666
char path[2048];
6767
sprintf(path, "/t/m_app_register_native_crash_handler_android?appVersion=%s&pn=%s&customTab=%s", appVersion, pname, isCustomTab ? "true" : "false");
6868
send_request(host, path);
69-
log_print(ANDROID_LOG_ERROR, "Native crash handler init pixel sent");
69+
log_print(ANDROID_LOG_ERROR, "Native crash handler init pixel sent on %s", pname);
7070
}

anrs/anrs-impl/src/main/java/com/duckduckgo/app/anr/AnrOfflinePixelSender.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ class AnrOfflinePixelSender @Inject constructor(
5757
}
5858

5959
companion object {
60-
const val ANR_STACKTRACE = "stackTrace"
61-
const val ANR_WEBVIEW_VERSION = "webView"
62-
const val ANR_CUSTOM_TAB = "customTab"
60+
private const val ANR_STACKTRACE = "stackTrace"
61+
private const val ANR_WEBVIEW_VERSION = "webView"
62+
private const val ANR_CUSTOM_TAB = "customTab"
6363
}
6464
}
6565

anrs/anrs-impl/src/main/java/com/duckduckgo/app/anr/ndk/NativeCrashFeature.kt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,29 @@
1616

1717
package com.duckduckgo.app.anr.ndk
1818

19+
import android.content.SharedPreferences
20+
import androidx.core.content.edit
1921
import com.duckduckgo.anvil.annotations.ContributesRemoteFeature
22+
import com.duckduckgo.app.di.AppCoroutineScope
23+
import com.duckduckgo.common.utils.DispatcherProvider
24+
import com.duckduckgo.data.store.api.SharedPreferencesProvider
2025
import com.duckduckgo.di.scopes.AppScope
26+
import com.duckduckgo.feature.toggles.api.RemoteFeatureStoreNamed
2127
import com.duckduckgo.feature.toggles.api.Toggle
28+
import com.duckduckgo.feature.toggles.api.Toggle.State
29+
import com.squareup.anvil.annotations.ContributesBinding
30+
import com.squareup.moshi.JsonAdapter
31+
import com.squareup.moshi.Moshi
32+
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
33+
import dagger.SingleInstanceIn
34+
import javax.inject.Inject
35+
import kotlinx.coroutines.CoroutineScope
36+
import kotlinx.coroutines.launch
2237

2338
@ContributesRemoteFeature(
2439
scope = AppScope::class,
2540
featureName = "androidNativeCrash",
41+
toggleStore = NativeCrashFeatureMultiProcessStore::class,
2642
)
2743
interface NativeCrashFeature {
2844
@Toggle.DefaultValue(true)
@@ -31,4 +47,42 @@ interface NativeCrashFeature {
3147

3248
@Toggle.DefaultValue(true)
3349
fun nativeCrashHandling(): Toggle
50+
51+
@Toggle.DefaultValue(true)
52+
fun nativeCrashHandlingSecondaryProcess(): Toggle
53+
}
54+
55+
@ContributesBinding(AppScope::class)
56+
@SingleInstanceIn(AppScope::class)
57+
@RemoteFeatureStoreNamed(NativeCrashFeature::class)
58+
class NativeCrashFeatureMultiProcessStore @Inject constructor(
59+
@AppCoroutineScope private val coroutineScope: CoroutineScope,
60+
private val dispatcherProvider: DispatcherProvider,
61+
private val sharedPreferencesProvider: SharedPreferencesProvider,
62+
moshi: Moshi,
63+
) : Toggle.Store {
64+
65+
private val preferences: SharedPreferences by lazy {
66+
sharedPreferencesProvider.getSharedPreferences(PREFS_FILENAME, multiprocess = true, migrate = false)
67+
}
68+
69+
private val stateAdapter: JsonAdapter<State> by lazy {
70+
moshi.newBuilder().add(KotlinJsonAdapterFactory()).build().adapter(State::class.java)
71+
}
72+
73+
override fun set(key: String, state: State) {
74+
coroutineScope.launch(dispatcherProvider.io()) {
75+
preferences.edit(commit = true) { putString(key, stateAdapter.toJson(state)) }
76+
}
77+
}
78+
79+
override fun get(key: String): State? {
80+
return preferences.getString(key, null)?.let {
81+
stateAdapter.fromJson(it)
82+
}
83+
}
84+
85+
companion object {
86+
private const val PREFS_FILENAME = "com.duckduckgo.app.androidNativeCrash.feature.v1"
87+
}
3488
}

anrs/anrs-impl/src/main/java/com/duckduckgo/app/anr/ndk/NativeCrashInit.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import com.duckduckgo.app.browser.customtabs.CustomTabDetector
2323
import com.duckduckgo.app.di.AppCoroutineScope
2424
import com.duckduckgo.app.di.IsMainProcess
2525
import com.duckduckgo.app.lifecycle.MainProcessLifecycleObserver
26+
import com.duckduckgo.app.lifecycle.VpnProcessLifecycleObserver
2627
import com.duckduckgo.appbuildconfig.api.AppBuildConfig
2728
import com.duckduckgo.appbuildconfig.api.isInternalBuild
2829
import com.duckduckgo.common.utils.DispatcherProvider
@@ -42,6 +43,10 @@ import logcat.logcat
4243
scope = AppScope::class,
4344
boundType = MainProcessLifecycleObserver::class,
4445
)
46+
@ContributesMultibinding(
47+
scope = AppScope::class,
48+
boundType = VpnProcessLifecycleObserver::class,
49+
)
4550
@SingleInstanceIn(AppScope::class)
4651
class NativeCrashInit @Inject constructor(
4752
context: Context,
@@ -51,7 +56,7 @@ class NativeCrashInit @Inject constructor(
5156
private val nativeCrashFeature: NativeCrashFeature,
5257
private val dispatcherProvider: DispatcherProvider,
5358
@AppCoroutineScope private val coroutineScope: CoroutineScope,
54-
) : MainProcessLifecycleObserver {
59+
) : MainProcessLifecycleObserver, VpnProcessLifecycleObserver {
5560

5661
private val isCustomTab: Boolean by lazy { customTabDetector.isCustomTab() }
5762
private val processName: String by lazy { if (isMainProcess) "main" else "vpn" }
@@ -76,9 +81,20 @@ class NativeCrashInit @Inject constructor(
7681
}
7782
}
7883

84+
override fun onVpnProcessCreated() {
85+
if (!isMainProcess) {
86+
coroutineScope.launch {
87+
jniRegisterNativeSignalHandler()
88+
}
89+
} else {
90+
logcat(ERROR) { "ndk-crash: onCreate wrongly called in the main process" }
91+
}
92+
}
93+
7994
private suspend fun jniRegisterNativeSignalHandler() = withContext(dispatcherProvider.io()) {
8095
runCatching {
81-
if (!nativeCrashFeature.nativeCrashHandling().isEnabled()) return@withContext
96+
if (isMainProcess && !nativeCrashFeature.nativeCrashHandling().isEnabled()) return@withContext
97+
if (!isMainProcess && !nativeCrashFeature.nativeCrashHandlingSecondaryProcess().isEnabled()) return@withContext
8298

8399
val logLevel = if (appBuildConfig.isDebug || appBuildConfig.isInternalBuild()) {
84100
Log.VERBOSE
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.duckduckgo.app.anr.ndk
2+
3+
import com.duckduckgo.common.test.CoroutineTestRule
4+
import com.duckduckgo.data.store.api.FakeSharedPreferencesProvider
5+
import com.duckduckgo.feature.toggles.api.Toggle
6+
import com.squareup.moshi.Moshi
7+
import kotlinx.coroutines.test.runTest
8+
import org.junit.Assert
9+
import org.junit.Rule
10+
import org.junit.Test
11+
12+
class NativeCrashFeatureMultiProcessStoreTest {
13+
14+
@get:Rule var coroutineRule = CoroutineTestRule()
15+
16+
private val store = NativeCrashFeatureMultiProcessStore(
17+
coroutineRule.testScope,
18+
coroutineRule.testDispatcherProvider,
19+
FakeSharedPreferencesProvider(),
20+
Moshi.Builder().build(),
21+
)
22+
23+
@Test
24+
fun `test set value`() = runTest {
25+
val expected = Toggle.State(enable = true)
26+
store.set("key", expected)
27+
28+
Assert.assertEquals(expected, store.get("key"))
29+
}
30+
31+
@Test
32+
fun `test get missing value`() = runTest {
33+
Assert.assertNull(store.get("key"))
34+
}
35+
36+
@Test
37+
fun `test get when value is not present`() {
38+
val expected = Toggle.State(enable = true)
39+
store.set("key", expected)
40+
41+
Assert.assertNull(store.get("wrong key"))
42+
}
43+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2024 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.anvil.annotations
18+
19+
import kotlin.reflect.KClass
20+
21+
/**
22+
* Anvil annotation to contribute plugins into an Active Plugin Point.
23+
* Active plugins are also guarded by remote feature flags
24+
*
25+
* This annotation is the counterpart of [ContributesActivePluginPoint]
26+
*
27+
* Usage:
28+
* ```kotlin
29+
* @ContributesActivePlugin(SomeDaggerScope::class)
30+
* class MyPluginImpl : MyPlugin {
31+
*
32+
* }
33+
*
34+
* interface MyPlugin : ActivePlugin {...}
35+
* ```
36+
*/
37+
@Target(AnnotationTarget.CLASS)
38+
@Retention(AnnotationRetention.RUNTIME)
39+
annotation class ContributesActivePlugin(
40+
/** The scope in which to include this contributed PluginPoint */
41+
val scope: KClass<*>,
42+
43+
/**
44+
* This is the type of the plugin the annotated class is extending from
45+
* This is a required member to help the code generation.
46+
*/
47+
val boundType: KClass<*>,
48+
49+
/**
50+
* The default value of remote feature flag.
51+
* Default is true (ie. enabled)
52+
*/
53+
val defaultActiveValue: Boolean = true,
54+
55+
/**
56+
* The priority for the plugin.
57+
* Lower priority values mean the associated plugin comes first in the list of plugins.
58+
*
59+
* This is equivalent to the [PriorityKey] annotation we use with [ContributesMultibinding] for normal plugins.
60+
* The [ContributesActivePlugin] coalesce both
61+
*/
62+
val priority: Int = 0,
63+
)

0 commit comments

Comments
 (0)