Skip to content

Commit

Permalink
FF for inactive beta (duckduckgo#3445)
Browse files Browse the repository at this point in the history
  • Loading branch information
aitorvs authored Aug 15, 2023
1 parent 78577ca commit af41570
Show file tree
Hide file tree
Showing 29 changed files with 538 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ import com.duckduckgo.networkprotection.impl.configuration.WgTunnel
import com.duckduckgo.networkprotection.impl.configuration.WgTunnel.WgTunnelData
import com.duckduckgo.networkprotection.impl.configuration.toCidrString
import com.duckduckgo.networkprotection.impl.pixels.NetworkProtectionPixels
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ClientInterface
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ServerDetails
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ClientInterface
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ServerDetails
import com.squareup.anvil.annotations.ContributesMultibinding
import dagger.Lazy
import dagger.SingleInstanceIn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import android.content.SharedPreferences
import androidx.core.content.edit
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.mobile.android.vpn.prefs.VpnSharedPreferencesProvider
import com.duckduckgo.networkprotection.impl.state.NetPFeatureRemover
import com.squareup.anvil.annotations.ContributesBinding
import com.squareup.anvil.annotations.ContributesMultibinding
import javax.inject.Inject
import org.threeten.bp.LocalDate
import org.threeten.bp.format.DateTimeFormatter
Expand All @@ -33,9 +35,13 @@ interface NetpCohortStore {
scope = AppScope::class,
boundType = NetpCohortStore::class,
)
@ContributesMultibinding(
scope = AppScope::class,
boundType = NetPFeatureRemover.NetPStoreRemovalPlugin::class,
)
class RealNetpCohortStore @Inject constructor(
private val sharedPreferencesProvider: VpnSharedPreferencesProvider,
) : NetpCohortStore {
) : NetpCohortStore, NetPFeatureRemover.NetPStoreRemovalPlugin {
private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")

private val preferences: SharedPreferences by lazy {
Expand All @@ -58,4 +64,8 @@ class RealNetpCohortStore @Inject constructor(
private const val FILENAME = "com.duckduckgo.networkprotection.cohort.prefs.v1"
private const val KEY_COHORT_LOCAL_DATE = "KEY_COHORT_LOCAL_DATE"
}

override fun clearStore() {
preferences.edit { clear() }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package com.duckduckgo.networkprotection.impl.configuration

import com.duckduckgo.di.scopes.VpnScope
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository
import com.squareup.anvil.annotations.ContributesBinding
import com.wireguard.crypto.Key
import com.wireguard.crypto.KeyPair
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,14 @@ package com.duckduckgo.networkprotection.impl.di

import android.content.Context
import androidx.room.Room
import com.duckduckgo.app.global.DispatcherProvider
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.mobile.android.vpn.prefs.VpnSharedPreferencesProvider
import com.duckduckgo.mobile.android.vpn.ui.AppBreakageCategory
import com.duckduckgo.networkprotection.impl.R
import com.duckduckgo.networkprotection.impl.waitlist.store.NetPWaitlistDataStoreSharedPreferences
import com.duckduckgo.networkprotection.impl.waitlist.store.NetPWaitlistRepository
import com.duckduckgo.networkprotection.impl.waitlist.store.RealNetPWaitlistRepository
import com.duckduckgo.networkprotection.store.NetPExclusionListRepository
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.store.NetworkProtectionPrefs
import com.duckduckgo.networkprotection.store.RealNetPExclusionListRepository
import com.duckduckgo.networkprotection.store.RealNetworkProtectionPrefs
import com.duckduckgo.networkprotection.store.RealNetworkProtectionRepository
import com.duckduckgo.networkprotection.store.db.NetPDatabase
import com.squareup.anvil.annotations.ContributesTo
import dagger.Module
Expand All @@ -39,18 +34,12 @@ import dagger.SingleInstanceIn

@Module
@ContributesTo(AppScope::class)
class DataModule {
object DataModule {
@Provides
@SingleInstanceIn(AppScope::class)
fun provideNetworkProtectionRepository(
vpnSharedPreferencesProvider: VpnSharedPreferencesProvider,
): NetworkProtectionRepository = RealNetworkProtectionRepository(RealNetworkProtectionPrefs(vpnSharedPreferencesProvider))

@Provides
fun provideNetPWaitlistRepository(
vpnSharedPreferencesProvider: VpnSharedPreferencesProvider,
dispatcherProvider: DispatcherProvider,
): NetPWaitlistRepository = RealNetPWaitlistRepository(NetPWaitlistDataStoreSharedPreferences(vpnSharedPreferencesProvider), dispatcherProvider)
): NetworkProtectionPrefs = RealNetworkProtectionPrefs(vpnSharedPreferencesProvider)

@SingleInstanceIn(AppScope::class)
@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ import com.duckduckgo.networkprotection.impl.management.NetworkProtectionManagem
import com.duckduckgo.networkprotection.impl.management.NetworkProtectionManagementViewModel.ConnectionState.Disconnected
import com.duckduckgo.networkprotection.impl.management.NetworkProtectionManagementViewModel.ConnectionState.Unknown
import com.duckduckgo.networkprotection.impl.pixels.NetworkProtectionPixels
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus.NotReconnecting
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus.Reconnecting
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus.ReconnectingFailed
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus.NotReconnecting
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus.Reconnecting
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus.ReconnectingFailed
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import com.duckduckgo.mobile.android.vpn.VpnFeaturesRegistry
import com.duckduckgo.mobile.android.vpn.service.VpnServiceCallbacks
import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason
import com.duckduckgo.networkprotection.impl.NetPVpnFeature
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository
import com.squareup.anvil.annotations.ContributesMultibinding
import java.util.concurrent.TimeUnit
import javax.inject.Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ import com.duckduckgo.mobile.android.vpn.service.connectivity.VpnConnectivityLos
import com.duckduckgo.networkprotection.impl.NetPVpnFeature
import com.duckduckgo.networkprotection.impl.alerts.reconnect.NetPReconnectNotifications
import com.duckduckgo.networkprotection.impl.pixels.NetworkProtectionPixels
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus.NotReconnecting
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus.Reconnecting
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus.ReconnectingFailed
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus.NotReconnecting
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus.Reconnecting
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus.ReconnectingFailed
import com.squareup.anvil.annotations.ContributesMultibinding
import dagger.Lazy
import dagger.SingleInstanceIn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import com.duckduckgo.mobile.android.vpn.VpnFeaturesRegistry
import com.duckduckgo.networkprotection.impl.NetPVpnFeature
import com.duckduckgo.networkprotection.impl.pixels.NetworkProtectionPixels
import com.duckduckgo.networkprotection.impl.rekey.NetPRekeyScheduler.Companion.DAILY_NETP_REKEY_TAG
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject
import logcat.logcat
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 2023 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.networkprotection.impl.state

import com.duckduckgo.anvil.annotations.ContributesPluginPoint
import com.duckduckgo.app.global.DispatcherProvider
import com.duckduckgo.app.global.plugins.PluginPoint
import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.mobile.android.vpn.VpnFeaturesRegistry
import com.duckduckgo.networkprotection.impl.NetPVpnFeature
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject
import kotlinx.coroutines.withContext
import logcat.logcat

interface NetPFeatureRemover {

/**
* Call this method to clear all NetP state.
* DO NOT set any dispatcher for this method, it's done directly from it
*/
suspend fun removeFeature()

@ContributesPluginPoint(AppScope::class)
interface NetPStoreRemovalPlugin {
/**
* Any NetP store that keeps some state data about NetP should contribute an implementation of this type
* so that the entire state can be cleared when necessary
*/
fun clearStore()
}
}

@ContributesBinding(AppScope::class)
class NetPFeatureRemoverImpl @Inject constructor(
private val netpStores: PluginPoint<NetPFeatureRemover.NetPStoreRemovalPlugin>,
private val dispatcherProvider: DispatcherProvider,
private val vpnFeaturesRegistry: VpnFeaturesRegistry,
) : NetPFeatureRemover {
override suspend fun removeFeature() = withContext(dispatcherProvider.io()) {
netpStores.getPlugins().forEach {
logcat { "NetP clearing state for ${it.javaClass}" }
it.clearStore()
}
vpnFeaturesRegistry.unregisterFeature(NetPVpnFeature.NETP_VPN)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import com.duckduckgo.mobile.android.vpn.VpnFeaturesRegistry
import com.duckduckgo.networkprotection.api.NetworkProtectionState
import com.duckduckgo.networkprotection.impl.NetPVpnFeature
import com.duckduckgo.networkprotection.impl.cohort.NetpCohortStore
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 DuckDuckGo
* Copyright (c) 2023 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -14,14 +14,20 @@
* limitations under the License.
*/

package com.duckduckgo.networkprotection.store

import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ClientInterface
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus.NotReconnecting
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus.Reconnecting
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ReconnectStatus.ReconnectingFailed
import com.duckduckgo.networkprotection.store.NetworkProtectionRepository.ServerDetails
package com.duckduckgo.networkprotection.impl.store

import com.duckduckgo.di.scopes.AppScope
import com.duckduckgo.networkprotection.impl.state.NetPFeatureRemover
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ClientInterface
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus.NotReconnecting
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus.Reconnecting
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ReconnectStatus.ReconnectingFailed
import com.duckduckgo.networkprotection.impl.store.NetworkProtectionRepository.ServerDetails
import com.duckduckgo.networkprotection.store.NetworkProtectionPrefs
import com.squareup.anvil.annotations.ContributesBinding
import com.squareup.anvil.annotations.ContributesMultibinding
import javax.inject.Inject

interface NetworkProtectionRepository {
var reconnectStatus: ReconnectStatus
Expand Down Expand Up @@ -49,9 +55,17 @@ interface NetworkProtectionRepository {
)
}

class RealNetworkProtectionRepository constructor(
@ContributesBinding(
scope = AppScope::class,
boundType = NetworkProtectionRepository::class,
)
@ContributesMultibinding(
scope = AppScope::class,
boundType = NetPFeatureRemover.NetPStoreRemovalPlugin::class,
)
class RealNetworkProtectionRepository @Inject constructor(
private val networkProtectionPrefs: NetworkProtectionPrefs,
) : NetworkProtectionRepository {
) : NetworkProtectionRepository, NetPFeatureRemover.NetPStoreRemovalPlugin {

override var privateKey: String?
get() = networkProtectionPrefs.getString(KEY_WG_PRIVATE_KEY, null)
Expand Down Expand Up @@ -111,6 +125,10 @@ class RealNetworkProtectionRepository constructor(
networkProtectionPrefs.setStringSet(KEY_WG_CLIENT_IFACE_TUNNEL_IP, value?.tunnelCidrSet ?: emptySet())
}

override fun clearStore() {
networkProtectionPrefs.clear()
}

override var reconnectStatus: ReconnectStatus
get() = when (networkProtectionPrefs.getInt(KEY_WG_RECONNECT_STATUS, 0)) {
-1 -> ReconnectingFailed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,10 @@ interface NetPRemoteFeature {
*/
@Toggle.DefaultValue(false)
fun waitlist(): Toggle

/**
*
*/
@Toggle.DefaultValue(true)
fun waitlistBetaActive(): Toggle
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,18 @@ import com.duckduckgo.networkprotection.api.NetworkProtectionWaitlist.NetPWaitli
import com.duckduckgo.networkprotection.api.NetworkProtectionWaitlist.NetPWaitlistState.JoinedWaitlist
import com.duckduckgo.networkprotection.api.NetworkProtectionWaitlist.NetPWaitlistState.NotUnlocked
import com.duckduckgo.networkprotection.api.NetworkProtectionWaitlist.NetPWaitlistState.PendingInviteCode
import com.duckduckgo.networkprotection.impl.state.NetPFeatureRemover
import com.duckduckgo.networkprotection.impl.waitlist.store.NetPWaitlistRepository
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import logcat.logcat

@ContributesBinding(AppScope::class)
class NetworkProtectionWaitlistImpl @Inject constructor(
private val netPRemoteFeature: NetPRemoteFeature,
private val netPRemoteFeature: NetPRemoteFeatureWrapper,
private val appBuildConfig: AppBuildConfig,
private val netPWaitlistRepository: NetPWaitlistRepository,
private val networkProtectionState: NetworkProtectionState,
Expand Down Expand Up @@ -75,10 +79,11 @@ class NetworkProtectionWaitlistImpl @Inject constructor(
}
// User is in beta already
if (netPWaitlistRepository.getAuthenticationToken() != null) {
return true
return netPRemoteFeature.isWaitlistActive()
}

// Both NetP and the waitlist features need to be enabled
return netPRemoteFeature.self().isEnabled() && netPRemoteFeature.waitlist().isEnabled()
return netPRemoteFeature.isWaitlistEnabled() && netPRemoteFeature.isWaitlistActive()
}

private fun didJoinBeta(): Boolean = netPWaitlistRepository.getAuthenticationToken() != null
Expand All @@ -87,3 +92,43 @@ class NetworkProtectionWaitlistImpl @Inject constructor(
return runBlocking { netPWaitlistRepository.getWaitlistToken() != null }
}
}

/**
* This class is a wrapper around the [NetPRemoteFeature] just to enclose some of the logic we need to do while
* checking the feature and sub-feature flags
*/
class NetPRemoteFeatureWrapper @Inject constructor(
private val netPRemoteFeature: NetPRemoteFeature,
private val netPFeatureRemover: NetPFeatureRemover,
private val appBuildConfig: AppBuildConfig,
private val coroutineScope: CoroutineScope,
) {
/**
* @return `true` if the waitlist beta is active. This is different that having waitlist enabled and they are
* independent states.
* If/when this method returns `false` (ie. waitlist beta is no longer active) we'll wipe out all internal NetP
* data so that users cannot use NetP anymore
*/
fun isWaitlistActive(): Boolean {
return if (appBuildConfig.isInternalBuild()) {
true
} else if (netPRemoteFeature.waitlistBetaActive().isEnabled()) {
true
} else {
// waitlistBetaActive == false means the waitlist beta period has ended, ie. wipe out NetP
// Skip for Internal users
coroutineScope.launch {
logcat { "NetP waitlist beta ended, wiping out everything" }
netPFeatureRemover.removeFeature()
}
return false
}
}

/**
* @return `true` when NetP waitlist beta is enabled, `false` otherwise
*/
fun isWaitlistEnabled(): Boolean {
return netPRemoteFeature.self().isEnabled() && netPRemoteFeature.waitlist().isEnabled()
}
}
Loading

0 comments on commit af41570

Please sign in to comment.