Skip to content

Commit

Permalink
Update most recent purchase time when autologging is off
Browse files Browse the repository at this point in the history
Summary: To prevent any over-reporting of events, we should update the time that is used in the client side dedupe whenever autologging or implicit purchases are disabled.

Reviewed By: jjiang10

Differential Revision: D66401888

fbshipit-source-id: cf21063f9fff7531f5c99381f9af356681042f7d
  • Loading branch information
maxalbrightmeta authored and facebook-github-bot committed Jan 2, 2025
1 parent 9a05289 commit b2e4873
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.facebook.appevents.integrity.ProtectedModeManager
import com.facebook.appevents.integrity.RedactedEventsManager
import com.facebook.appevents.integrity.SensitiveParamsManager
import com.facebook.appevents.integrity.StdParamsEnforcementManager
import com.facebook.appevents.internal.AutomaticAnalyticsLogger.isImplicitPurchaseLoggingEnabled
import com.facebook.appevents.restrictivedatafilter.RestrictiveDataManager
import com.facebook.internal.FeatureManager
import com.facebook.internal.FeatureManager.checkFeature
Expand Down Expand Up @@ -72,7 +73,7 @@ object AppEventsManager {
}
}
checkFeature(FeatureManager.Feature.IapLogging) { enabled ->
if (enabled && UserSettingsManager.getAutoLogAppEventsEnabled()) {
if (enabled) {
InAppPurchaseManager.enableAutoLogging()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,23 @@ object InAppPurchaseLoggerManager {

@JvmStatic
fun migrateOldCacheHistory() {
var newestCandidateTime = APPROXIMATE_IAP_ENHANCEMENT_RELEASE_TIME
val iapCache =
getApplicationContext().getSharedPreferences(
IAP_CACHE_GPBLV2V7,
Context.MODE_PRIVATE
)
var newestCandidateTime = max(
max(
iapCache.getLong(
TIME_OF_LAST_LOGGED_PURCHASE_KEY,
0L
),
iapCache.getLong(
TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY,
0L
)
), APPROXIMATE_IAP_ENHANCEMENT_RELEASE_TIME
)
val cachedPurchaseSet: MutableSet<String> = CopyOnWriteArraySet()
val sharedPreferences =
getApplicationContext().getSharedPreferences(
Expand Down Expand Up @@ -80,11 +96,6 @@ object InAppPurchaseLoggerManager {
}
}
}
val iapCache =
getApplicationContext().getSharedPreferences(
IAP_CACHE_GPBLV2V7,
Context.MODE_PRIVATE
)
iapCache.edit()
.putLong(TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY, newestCandidateTime)
.apply()
Expand Down Expand Up @@ -141,6 +152,26 @@ object InAppPurchaseLoggerManager {
}
}

@JvmStatic
fun updateLatestPossiblePurchaseTime() {
try {
val iapCache =
getApplicationContext().getSharedPreferences(
IAP_CACHE_GPBLV2V7,
Context.MODE_PRIVATE
)
val currTime = System.currentTimeMillis()
iapCache.edit()
.putLong(TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY, currTime)
.apply()
iapCache.edit()
.putLong(TIME_OF_LAST_LOGGED_PURCHASE_KEY, currTime)
.apply()
} catch (e: Exception) {
/* Swallow */
}
}

@JvmStatic
fun filterPurchaseLogging(
purchaseDetailsMap: MutableMap<String, JSONObject>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ import com.facebook.appevents.iap.InAppPurchaseUtils.BillingClientVersion.V1
import com.facebook.appevents.iap.InAppPurchaseUtils.BillingClientVersion.V2_V4
import com.facebook.appevents.iap.InAppPurchaseUtils.BillingClientVersion.V5_V7
import com.facebook.FacebookSdk.getApplicationContext
import com.facebook.UserSettingsManager
import com.facebook.appevents.OperationalData
import com.facebook.appevents.OperationalDataEnum
import com.facebook.appevents.iap.InAppPurchaseLoggerManager.updateLatestPossiblePurchaseTime
import com.facebook.appevents.internal.AutomaticAnalyticsLogger.isImplicitPurchaseLoggingEnabled
import com.facebook.appevents.internal.Constants
import com.facebook.internal.FeatureManager
import com.facebook.internal.FeatureManager.isEnabled
Expand All @@ -40,6 +43,10 @@ object InAppPurchaseManager {

@JvmStatic
fun enableAutoLogging() {
if (!isImplicitPurchaseLoggingEnabled()) {
updateLatestPossiblePurchaseTime()
return
}
enabled.set(true)
startTracking()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.powermock.api.mockito.PowerMockito
import org.powermock.core.classloader.annotations.PrepareForTest
import java.util.concurrent.ConcurrentHashMap

@PrepareForTest(FacebookSdk::class)
class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {
private val mockExecutor: Executor = FacebookSerialExecutor()
private lateinit var mockNewCachePrefs: SharedPreferences
private lateinit var mockOldCachePrefs: SharedPreferences
private var mockNewCachedMap: MutableMap<String, Any> = mutableMapOf()
private var mockNewCachedMap: MutableMap<String, Long> = ConcurrentHashMap()
private lateinit var editor: SharedPreferences.Editor
private val packageName = "sample.packagename"
private val TIME_OF_LAST_LOGGED_PURCHASE_KEY = "TIME_OF_LAST_LOGGED_PURCHASE"
Expand Down Expand Up @@ -68,15 +69,15 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {
any()
)
).thenAnswer {
return@thenAnswer mockNewCachedMap[it.getArgument(0)]
return@thenAnswer mockNewCachedMap.getOrDefault(it.getArgument(0), it.getArgument(1))
}
whenever(
mockNewCachePrefs.getLong(
eq(TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY),
any()
)
).thenAnswer {
return@thenAnswer mockNewCachedMap[it.getArgument(0)]
return@thenAnswer mockNewCachedMap.getOrDefault(it.getArgument(0), it.getArgument(1))
}
whenever(editor.putStringSet(any(), any())).thenReturn(editor)
doNothing().whenever(editor).apply()
Expand Down Expand Up @@ -104,6 +105,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupPurchaseOnFirstTimeLogging() {
mockNewCachedMap.clear()
// Construct purchase details map
val mockPurchaseDetailsMap: MutableMap<String, JSONObject> = mutableMapOf()
val purchaseDetailJson1 =
Expand Down Expand Up @@ -136,6 +138,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupPurchaseOnFirstTimeLoggingAndNewPurchase() {
mockNewCachedMap.clear()
// Construct purchase details map
val mockPurchaseDetailsMap: MutableMap<String, JSONObject> = mutableMapOf()
val purchaseDetailJson1 =
Expand Down Expand Up @@ -168,6 +171,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupPurchaseOnFirstTimeLoggingAndInvalidCacheHistory() {
mockNewCachedMap.clear()
// Construct purchase details map
val mockPurchaseDetailsMap: MutableMap<String, JSONObject> = mutableMapOf()
val purchaseDetailJson1 =
Expand Down Expand Up @@ -200,6 +204,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupPurchaseNotFirstTimeLogging() {
mockNewCachedMap.clear()
mockNewCachedMap[TIME_OF_LAST_LOGGED_PURCHASE_KEY] = 1630000000000

// Construct purchase details map
Expand All @@ -226,6 +231,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupPurchaseNotFirstTimeLoggingAndNewPurchase() {
mockNewCachedMap.clear()

mockNewCachedMap[TIME_OF_LAST_LOGGED_PURCHASE_KEY] = 1620000000000

Expand Down Expand Up @@ -253,6 +259,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupSubscriptionOnFirstTimeLogging() {
mockNewCachedMap.clear()
mockNewCachedMap[TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY] = 0
// Construct purchase details map
val mockPurchaseDetailsMap: MutableMap<String, JSONObject> = mutableMapOf()
Expand Down Expand Up @@ -287,6 +294,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupSubscriptionOnFirstTimeLoggingAndNewPurchase() {
mockNewCachedMap.clear()
mockNewCachedMap[TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY] = 0
// Construct purchase details map
val mockPurchaseDetailsMap: MutableMap<String, JSONObject> = mutableMapOf()
Expand Down Expand Up @@ -319,6 +327,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupSubscriptionOnFirstTimeLoggingAndNewerPurchasesInOldCache() {
mockNewCachedMap.clear()
mockNewCachedMap[TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY] = 0
// Construct purchase details map
val mockPurchaseDetailsMap: MutableMap<String, JSONObject> = mutableMapOf()
Expand Down Expand Up @@ -351,6 +360,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupSubscriptionOnFirstTimeLoggingAndNewPurchasesInOldCache() {
mockNewCachedMap.clear()
mockNewCachedMap[TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY] = 0
// Construct purchase details map
val mockPurchaseDetailsMap: MutableMap<String, JSONObject> = mutableMapOf()
Expand Down Expand Up @@ -383,6 +393,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupSubscriptionOnFirstTimeLoggingAndInvalidValueInOldCache() {
mockNewCachedMap.clear()
mockNewCachedMap[TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY] = 0
// Construct purchase details map
val mockPurchaseDetailsMap: MutableMap<String, JSONObject> = mutableMapOf()
Expand Down Expand Up @@ -416,6 +427,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupSubscriptionNotFirstTimeLogging() {
mockNewCachedMap.clear()
mockNewCachedMap[TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY] = 1620000000002

// Construct purchase details map
Expand All @@ -442,6 +454,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testCacheDeDupSubscriptionNotFirstTimeLoggingAndNewPurchase() {
mockNewCachedMap.clear()

mockNewCachedMap[TIME_OF_LAST_LOGGED_SUBSCRIPTION_KEY] = 1620000000000

Expand Down Expand Up @@ -470,6 +483,7 @@ class InAppPurchaseLoggerManagerTest : FacebookPowerMockTestCase() {

@Test
fun testConstructLoggingReadyMap() {
mockNewCachedMap.clear()
val mockPurchaseDetailsMap: MutableMap<String, JSONObject> = mutableMapOf()
val mockSkuDetailsMap: MutableMap<String, JSONObject> = HashMap()
val skuDetailJson =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ import android.os.Bundle
import androidx.core.os.bundleOf
import com.facebook.FacebookPowerMockTestCase
import com.facebook.FacebookSdk
import com.facebook.UserSettingsManager
import com.facebook.appevents.AppEventsConstants
import com.facebook.appevents.OperationalData
import com.facebook.appevents.OperationalDataEnum
import com.facebook.appevents.internal.AutomaticAnalyticsLogger
import com.facebook.appevents.internal.Constants
import com.facebook.internal.FacebookRequestErrorClassification
import com.facebook.internal.FeatureManager
Expand Down Expand Up @@ -112,6 +114,11 @@ class InAppPurchaseManagerTest : FacebookPowerMockTestCase() {
whenever(FetchedAppSettingsManager.getAppSettingsWithoutQuery(anyOrNull())).thenReturn(
mockFetchedAppSettings
)
Whitebox.setInternalState(
InAppPurchaseManager::class.java,
"enabled",
AtomicBoolean(true)
)
}

@Before
Expand Down Expand Up @@ -145,7 +152,7 @@ class InAppPurchaseManagerTest : FacebookPowerMockTestCase() {
isStartIapLoggingCalled = true
Unit
}
InAppPurchaseManager.enableAutoLogging()
InAppPurchaseManager.startTracking()
assertThat(isStartIapLoggingCalled).isTrue
}

Expand All @@ -160,7 +167,7 @@ class InAppPurchaseManagerTest : FacebookPowerMockTestCase() {
isStartIapLoggingCalled = true
Unit
}
InAppPurchaseManager.enableAutoLogging()
InAppPurchaseManager.startTracking()
assertThat(isStartIapLoggingCalled).isFalse
}

Expand All @@ -176,7 +183,7 @@ class InAppPurchaseManagerTest : FacebookPowerMockTestCase() {
isStartIapLoggingCalled = true
Unit
}
InAppPurchaseManager.enableAutoLogging()
InAppPurchaseManager.startTracking()
assertThat(isStartIapLoggingCalled).isTrue
}

Expand Down Expand Up @@ -207,7 +214,7 @@ class InAppPurchaseManagerTest : FacebookPowerMockTestCase() {
isStartIapLoggingCalled = true
Unit
}
InAppPurchaseManager.enableAutoLogging()
InAppPurchaseManager.startTracking()
assertThat(isStartIapLoggingCalled).isTrue
}

Expand Down Expand Up @@ -238,7 +245,7 @@ class InAppPurchaseManagerTest : FacebookPowerMockTestCase() {
isStartIapLoggingCalled = true
Unit
}
InAppPurchaseManager.enableAutoLogging()
InAppPurchaseManager.startTracking()
assertThat(isStartIapLoggingCalled).isTrue
}

Expand Down Expand Up @@ -271,7 +278,7 @@ class InAppPurchaseManagerTest : FacebookPowerMockTestCase() {
isStartIapLoggingCalled = true
Unit
}
InAppPurchaseManager.enableAutoLogging()
InAppPurchaseManager.startTracking()
assertThat(isStartIapLoggingCalled).isFalse
}

Expand Down

0 comments on commit b2e4873

Please sign in to comment.