From a2020ac7f6c4c7c57ef80eaf2c7a052788e37b73 Mon Sep 17 00:00:00 2001 From: Daymon Date: Mon, 24 Mar 2025 13:32:01 -0500 Subject: [PATCH 01/16] Implement temp DataStore class --- firebase-common/firebase-common.gradle.kts | 1 + .../google/firebase/datastore/DataStore.kt | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt diff --git a/firebase-common/firebase-common.gradle.kts b/firebase-common/firebase-common.gradle.kts index 88bfa58e27d..c0c379a6ace 100644 --- a/firebase-common/firebase-common.gradle.kts +++ b/firebase-common/firebase-common.gradle.kts @@ -57,6 +57,7 @@ dependencies { api("com.google.firebase:firebase-components:18.0.0") api("com.google.firebase:firebase-annotations:16.2.0") + implementation("androidx.datastore:datastore-preferences:1.1.3") implementation(libs.androidx.annotation) implementation(libs.androidx.futures) implementation(libs.kotlin.stdlib) diff --git a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt new file mode 100644 index 00000000000..0828e5c0519 --- /dev/null +++ b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt @@ -0,0 +1,68 @@ +package com.google.firebase.datastore + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.SharedPreferencesMigration +import androidx.datastore.preferences.core.MutablePreferences +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking + +/** + * Don't use this unless you're bridging Java code Use your own [dataStore] directly in Kotlin code, + * or if you're writing new code. + */ +class DataStorage(val context: Context, val name: String) { + private val transforming = ThreadLocal() + + private val Context.dataStore: DataStore by + preferencesDataStore( + name = name, + produceMigrations = { listOf(SharedPreferencesMigration(it, name)) } + ) + + private val dataStore = context.dataStore + + fun getSync(key: Preferences.Key, defaultValue: T): T = runBlocking { + dataStore.data.firstOrNull()?.get(key) ?: defaultValue + } + + fun contains(key: Preferences.Key): Boolean = runBlocking { + dataStore.data.firstOrNull()?.contains(key) ?: false + } + + fun putSync(key: Preferences.Key, value: T): Preferences = runBlocking { + dataStore.edit { it[key] = value } + } + + /** Do not modify the returned map (should be obvious, since it's immutable though) */ + fun getAllSync(): Map, Any> = runBlocking { + dataStore.data.firstOrNull()?.asMap() ?: emptyMap() + } + + /** Edit calls should not be called within edit calls. Is there a way to prevent this? */ + fun editSync(transform: (MutablePreferences) -> Unit): Preferences = runBlocking { + if (transforming.get() == true) { + throw IllegalStateException( + """ + Don't call DataStorage.edit() from within an existing edit() callback. + This causes deadlocks, and is generally indicative of a code smell. + Instead, either pass around the initial `MutablePreferences` instance, or don't do everything in a single callback. + """ + .trimIndent() + ) + } + transforming.set(true) + try { + dataStore.edit { transform(it) } + } finally { + transforming.set(false) + } + } +} + +/** Helper for Java code */ +fun Preferences.getOrDefault(key: Preferences.Key, defaultValue: T) = + get(key) ?: defaultValue From 58c97dfc6b4fc39ded5e5552f95e3378757dfe14 Mon Sep 17 00:00:00 2001 From: Daymon Date: Mon, 24 Mar 2025 13:32:09 -0500 Subject: [PATCH 02/16] Migrate common --- .../heartbeatinfo/HeartBeatInfoStorage.java | 231 ++++++++++-------- .../DefaultHeartBeatControllerTest.java | 14 +- .../HeartBeatInfoStorageTest.java | 47 ++-- 3 files changed, 157 insertions(+), 135 deletions(-) diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java index a8a9fee5104..c2f04ccf0a0 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java @@ -15,10 +15,13 @@ package com.google.firebase.heartbeatinfo; import android.content.Context; -import android.content.SharedPreferences; import android.os.Build; import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; +import androidx.datastore.preferences.core.*; +import androidx.datastore.preferences.core.Preferences; +import com.google.firebase.datastore.DataStorage; +import com.google.firebase.datastore.DataStoreKt; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; @@ -40,91 +43,97 @@ class HeartBeatInfoStorage { private static HeartBeatInfoStorage instance = null; - private static final String GLOBAL = "fire-global"; + private static final Preferences.Key GLOBAL = PreferencesKeys.longKey("fire-global"); private static final String PREFERENCES_NAME = "FirebaseAppHeartBeat"; private static final String HEARTBEAT_PREFERENCES_NAME = "FirebaseHeartBeat"; - private static final String HEART_BEAT_COUNT_TAG = "fire-count"; + private static final Preferences.Key HEART_BEAT_COUNT_TAG = + PreferencesKeys.longKey("fire-count"); - private static final String LAST_STORED_DATE = "last-used-date"; + private static final Preferences.Key LAST_STORED_DATE = + PreferencesKeys.stringKey("last-used-date"); // As soon as you hit the limit of heartbeats. The number of stored heartbeats is halved. private static final int HEART_BEAT_COUNT_LIMIT = 30; - private final SharedPreferences firebaseSharedPreferences; + private final DataStorage firebaseDataStore; public HeartBeatInfoStorage(Context applicationContext, String persistenceKey) { - this.firebaseSharedPreferences = - applicationContext.getSharedPreferences( - HEARTBEAT_PREFERENCES_NAME + persistenceKey, Context.MODE_PRIVATE); + this.firebaseDataStore = + new DataStorage(applicationContext, HEARTBEAT_PREFERENCES_NAME + persistenceKey); } @VisibleForTesting @RestrictTo(RestrictTo.Scope.TESTS) - HeartBeatInfoStorage(SharedPreferences firebaseSharedPreferences) { - this.firebaseSharedPreferences = firebaseSharedPreferences; + HeartBeatInfoStorage(DataStorage dataStorage) { + this.firebaseDataStore = dataStorage; } @VisibleForTesting @RestrictTo(RestrictTo.Scope.TESTS) int getHeartBeatCount() { - return (int) this.firebaseSharedPreferences.getLong(HEART_BEAT_COUNT_TAG, 0); + return this.firebaseDataStore.getSync(HEART_BEAT_COUNT_TAG, 0L).intValue(); } synchronized void deleteAllHeartBeats() { - SharedPreferences.Editor editor = firebaseSharedPreferences.edit(); - int counter = 0; - for (Map.Entry entry : this.firebaseSharedPreferences.getAll().entrySet()) { - if (entry.getValue() instanceof Set) { - // All other heartbeats other than the heartbeats stored today will be deleted. - Set dates = (Set) entry.getValue(); - String today = getFormattedDate(System.currentTimeMillis()); - String key = entry.getKey(); - if (dates.contains(today)) { - Set userAgentDateSet = new HashSet<>(); - userAgentDateSet.add(today); - counter += 1; - editor.putStringSet(key, userAgentDateSet); - } else { - editor.remove(key); - } - } - } - if (counter == 0) { - editor.remove(HEART_BEAT_COUNT_TAG); - } else { - editor.putLong(HEART_BEAT_COUNT_TAG, counter); - } + firebaseDataStore.editSync( + (pref) -> { + long counter = 0; + for (Map.Entry, Object> entry : pref.asMap().entrySet()) { + if (entry.getValue() instanceof Set) { + // All other heartbeats other than the heartbeats stored today will be deleted. + Preferences.Key> key = (Preferences.Key>) entry.getKey(); + Set dates = (Set) entry.getValue(); + String today = getFormattedDate(System.currentTimeMillis()); + + if (dates.contains(today)) { + pref.set(key, Set.of(today)); + counter += 1; + } else { + pref.remove(key); + } + } + } + if (counter == 0) { + pref.remove(HEART_BEAT_COUNT_TAG); + } else { + pref.set(HEART_BEAT_COUNT_TAG, counter); + } - editor.commit(); + return null; + }); } synchronized List getAllHeartBeats() { ArrayList heartBeatResults = new ArrayList<>(); - for (Map.Entry entry : this.firebaseSharedPreferences.getAll().entrySet()) { + String today = getFormattedDate(System.currentTimeMillis()); + + for (Map.Entry, Object> entry : + this.firebaseDataStore.getAllSync().entrySet()) { if (entry.getValue() instanceof Set) { Set dates = new HashSet<>((Set) entry.getValue()); - String today = getFormattedDate(System.currentTimeMillis()); dates.remove(today); if (!dates.isEmpty()) { - heartBeatResults.add( - HeartBeatResult.create(entry.getKey(), new ArrayList(dates))); + heartBeatResults.add(HeartBeatResult.create(entry.getKey().getName(), new ArrayList<>())); } } } + updateGlobalHeartBeat(System.currentTimeMillis()); + return heartBeatResults; } - private synchronized String getStoredUserAgentString(String dateString) { - for (Map.Entry entry : firebaseSharedPreferences.getAll().entrySet()) { + private synchronized Preferences.Key> getStoredUserAgentString( + MutablePreferences preferences, String dateString) { + for (Map.Entry, Object> entry : preferences.asMap().entrySet()) { if (entry.getValue() instanceof Set) { Set dateSet = (Set) entry.getValue(); for (String date : dateSet) { if (dateString.equals(date)) { - return entry.getKey(); + return PreferencesKeys.stringSetKey(entry.getKey().getName()); } } } @@ -132,36 +141,40 @@ private synchronized String getStoredUserAgentString(String dateString) { return null; } - private synchronized void updateStoredUserAgent(String userAgent, String dateString) { - removeStoredDate(dateString); + private synchronized void updateStoredUserAgent( + MutablePreferences preferences, Preferences.Key> userAgent, String dateString) { + removeStoredDate(preferences, dateString); Set userAgentDateSet = - new HashSet( - firebaseSharedPreferences.getStringSet(userAgent, new HashSet())); + new HashSet<>(DataStoreKt.getOrDefault(preferences, userAgent, new HashSet<>())); userAgentDateSet.add(dateString); - firebaseSharedPreferences.edit().putStringSet(userAgent, userAgentDateSet).commit(); + preferences.set(userAgent, userAgentDateSet); } - private synchronized void removeStoredDate(String dateString) { + private synchronized void removeStoredDate(MutablePreferences preferences, String dateString) { // Find stored heartbeat and clear it. - String userAgentString = getStoredUserAgentString(dateString); - if (userAgentString == null) { + Preferences.Key> userAgent = getStoredUserAgentString(preferences, dateString); + if (userAgent == null) { return; } Set userAgentDateSet = - new HashSet( - firebaseSharedPreferences.getStringSet(userAgentString, new HashSet())); + new HashSet<>(DataStoreKt.getOrDefault(preferences, userAgent, new HashSet<>())); userAgentDateSet.remove(dateString); if (userAgentDateSet.isEmpty()) { - firebaseSharedPreferences.edit().remove(userAgentString).commit(); + preferences.remove(userAgent); } else { - firebaseSharedPreferences.edit().putStringSet(userAgentString, userAgentDateSet).commit(); + preferences.set(userAgent, userAgentDateSet); } } synchronized void postHeartBeatCleanUp() { String dateString = getFormattedDate(System.currentTimeMillis()); - firebaseSharedPreferences.edit().putString(LAST_STORED_DATE, dateString).commit(); - removeStoredDate(dateString); + + firebaseDataStore.editSync( + (pref) -> { + pref.set(LAST_STORED_DATE, dateString); + removeStoredDate(pref, dateString); + return null; + }); } private synchronized String getFormattedDate(long millis) { @@ -176,71 +189,77 @@ private synchronized String getFormattedDate(long millis) { synchronized void storeHeartBeat(long millis, String userAgentString) { String dateString = getFormattedDate(millis); - String lastDateString = firebaseSharedPreferences.getString(LAST_STORED_DATE, ""); - if (lastDateString.equals(dateString)) { - String storedUserAgentString = getStoredUserAgentString(dateString); - if (storedUserAgentString == null) { - // Heartbeat already sent for today. - return; - } - if (storedUserAgentString.equals(userAgentString)) { - // UserAgent not updated. - return; - } else { - updateStoredUserAgent(userAgentString, dateString); - return; - } - } - long heartBeatCount = firebaseSharedPreferences.getLong(HEART_BEAT_COUNT_TAG, 0); - if (heartBeatCount + 1 == HEART_BEAT_COUNT_LIMIT) { - cleanUpStoredHeartBeats(); - heartBeatCount = firebaseSharedPreferences.getLong(HEART_BEAT_COUNT_TAG, 0); - } - Set userAgentDateSet = - new HashSet( - firebaseSharedPreferences.getStringSet(userAgentString, new HashSet())); - userAgentDateSet.add(dateString); - heartBeatCount += 1; - firebaseSharedPreferences - .edit() - .putStringSet(userAgentString, userAgentDateSet) - .putLong(HEART_BEAT_COUNT_TAG, heartBeatCount) - .putString(LAST_STORED_DATE, dateString) - .commit(); + Preferences.Key> userAgent = PreferencesKeys.stringSetKey(userAgentString); + firebaseDataStore.editSync( + (pref) -> { + String lastDateString = DataStoreKt.getOrDefault(pref, LAST_STORED_DATE, ""); + if (lastDateString.equals(dateString)) { + Preferences.Key> storedUserAgent = + getStoredUserAgentString(pref, dateString); + if (storedUserAgent == null) { + // Heartbeat already sent for today. + return null; + } else if (storedUserAgent.getName().equals(userAgentString)) { + // UserAgent not updated. + return null; + } else { + updateStoredUserAgent(pref, userAgent, dateString); + return null; + } + } + long heartBeatCount = DataStoreKt.getOrDefault(pref, HEART_BEAT_COUNT_TAG, 0L); + if (heartBeatCount + 1 == HEART_BEAT_COUNT_LIMIT) { + heartBeatCount = cleanUpStoredHeartBeats(pref); + } + Set userAgentDateSet = + new HashSet<>(DataStoreKt.getOrDefault(pref, userAgent, new HashSet<>())); + userAgentDateSet.add(dateString); + heartBeatCount += 1; + + pref.set(userAgent, userAgentDateSet); + pref.set(HEART_BEAT_COUNT_TAG, heartBeatCount); + pref.set(LAST_STORED_DATE, dateString); + + return null; + }); } - private synchronized void cleanUpStoredHeartBeats() { - long heartBeatCount = firebaseSharedPreferences.getLong(HEART_BEAT_COUNT_TAG, 0); + private synchronized long cleanUpStoredHeartBeats(MutablePreferences preferences) { + long heartBeatCount = DataStoreKt.getOrDefault(preferences, HEART_BEAT_COUNT_TAG, 0L); + String lowestDate = null; String userAgentString = ""; - for (Map.Entry entry : firebaseSharedPreferences.getAll().entrySet()) { + Set userAgentDateSet = new HashSet<>(); + for (Map.Entry, Object> entry : preferences.asMap().entrySet()) { if (entry.getValue() instanceof Set) { Set dateSet = (Set) entry.getValue(); for (String date : dateSet) { if (lowestDate == null || lowestDate.compareTo(date) > 0) { + userAgentDateSet = dateSet; lowestDate = date; - userAgentString = entry.getKey(); + userAgentString = entry.getKey().getName(); } } } } - Set userAgentDateSet = - new HashSet( - firebaseSharedPreferences.getStringSet(userAgentString, new HashSet())); + userAgentDateSet = new HashSet<>(userAgentDateSet); userAgentDateSet.remove(lowestDate); - firebaseSharedPreferences - .edit() - .putStringSet(userAgentString, userAgentDateSet) - .putLong(HEART_BEAT_COUNT_TAG, heartBeatCount - 1) - .commit(); + preferences.set(PreferencesKeys.stringSetKey(userAgentString), userAgentDateSet); + preferences.set(HEART_BEAT_COUNT_TAG, heartBeatCount - 1); + + return heartBeatCount - 1; } synchronized long getLastGlobalHeartBeat() { - return firebaseSharedPreferences.getLong(GLOBAL, -1); + return firebaseDataStore.getSync(GLOBAL, -1L); } synchronized void updateGlobalHeartBeat(long millis) { - firebaseSharedPreferences.edit().putLong(GLOBAL, millis).commit(); + firebaseDataStore.editSync( + (pref) -> { + pref.set(GLOBAL, millis); + return null; + }); } synchronized boolean isSameDateUtc(long base, long target) { @@ -252,15 +271,11 @@ synchronized boolean isSameDateUtc(long base, long target) { A sdk heartbeat is sent either when there is no heartbeat sent ever for the sdk or when the last heartbeat send for the sdk was later than a day before. */ - synchronized boolean shouldSendSdkHeartBeat(String heartBeatTag, long millis) { - if (firebaseSharedPreferences.contains(heartBeatTag)) { - if (!this.isSameDateUtc(firebaseSharedPreferences.getLong(heartBeatTag, -1), millis)) { - firebaseSharedPreferences.edit().putLong(heartBeatTag, millis).commit(); - return true; - } + synchronized boolean shouldSendSdkHeartBeat(Preferences.Key heartBeatTag, long millis) { + if (this.isSameDateUtc(firebaseDataStore.getSync(heartBeatTag, -1L), millis)) { return false; } else { - firebaseSharedPreferences.edit().putLong(heartBeatTag, millis).commit(); + firebaseDataStore.putSync(heartBeatTag, millis); return true; } } diff --git a/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatControllerTest.java b/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatControllerTest.java index fe564ca242e..a57c1768f66 100644 --- a/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatControllerTest.java +++ b/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatControllerTest.java @@ -25,10 +25,10 @@ import static org.mockito.Mockito.when; import android.content.Context; -import android.content.SharedPreferences; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableSet; +import com.google.firebase.datastore.DataStorage; import com.google.firebase.platforminfo.UserAgentPublisher; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -107,10 +107,8 @@ public void generateHeartBeat_oneHeartBeat() throws InterruptedException, Timeou public void firstNewThenOld_synchronizedCorrectly() throws InterruptedException, TimeoutException { Context context = ApplicationProvider.getApplicationContext(); - SharedPreferences heartBeatSharedPreferences = - context.getSharedPreferences("testHeartBeat", Context.MODE_PRIVATE); - HeartBeatInfoStorage heartBeatInfoStorage = - new HeartBeatInfoStorage(heartBeatSharedPreferences); + DataStorage heartBeatDataStore = new DataStorage(context, "testHeartBeat"); + HeartBeatInfoStorage heartBeatInfoStorage = new HeartBeatInfoStorage(heartBeatDataStore); DefaultHeartBeatController controller = new DefaultHeartBeatController( () -> heartBeatInfoStorage, logSources, executor, () -> publisher, context); @@ -130,10 +128,8 @@ public void firstNewThenOld_synchronizedCorrectly() public void firstOldThenNew_synchronizedCorrectly() throws InterruptedException, TimeoutException { Context context = ApplicationProvider.getApplicationContext(); - SharedPreferences heartBeatSharedPreferences = - context.getSharedPreferences("testHeartBeat", Context.MODE_PRIVATE); - HeartBeatInfoStorage heartBeatInfoStorage = - new HeartBeatInfoStorage(heartBeatSharedPreferences); + DataStorage heartBeatDataStore = new DataStorage(context, "testHeartBeat"); + HeartBeatInfoStorage heartBeatInfoStorage = new HeartBeatInfoStorage(heartBeatDataStore); DefaultHeartBeatController controller = new DefaultHeartBeatController( () -> heartBeatInfoStorage, logSources, executor, () -> publisher, context); diff --git a/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorageTest.java b/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorageTest.java index 81b191f117d..b83ba4d3523 100644 --- a/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorageTest.java +++ b/firebase-common/src/test/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorageTest.java @@ -17,12 +17,15 @@ import static com.google.common.truth.Truth.assertThat; import android.content.Context; -import android.content.SharedPreferences; +import androidx.datastore.preferences.core.Preferences; +import androidx.datastore.preferences.core.PreferencesKeys; import androidx.test.core.app.ApplicationProvider; import androidx.test.runner.AndroidJUnit4; +import com.google.firebase.datastore.DataStorage; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -31,23 +34,30 @@ @RunWith(AndroidJUnit4.class) public class HeartBeatInfoStorageTest { - private final String testSdk = "testSdk"; - private final String GLOBAL = "fire-global"; + private final Preferences.Key testSdk = PreferencesKeys.longKey("testSdk"); + private final Preferences.Key GLOBAL = PreferencesKeys.longKey("fire-global"); private static final int HEART_BEAT_COUNT_LIMIT = 30; private static Context applicationContext = ApplicationProvider.getApplicationContext(); - private static SharedPreferences heartBeatSharedPreferences = - applicationContext.getSharedPreferences("testHeartBeat", Context.MODE_PRIVATE); - private HeartBeatInfoStorage heartBeatInfoStorage = - new HeartBeatInfoStorage(heartBeatSharedPreferences); + private static DataStorage heartBeatDataStore = + new DataStorage(applicationContext, "testHeartBeat"); + private HeartBeatInfoStorage heartBeatInfoStorage = new HeartBeatInfoStorage(heartBeatDataStore); @Before public void setUp() { - heartBeatSharedPreferences.edit().clear().apply(); + heartBeatDataStore.editSync( + (pref) -> { + pref.clear(); + return null; + }); } @After public void tearDown() { - heartBeatSharedPreferences.edit().clear().apply(); + heartBeatDataStore.editSync( + (pref) -> { + pref.clear(); + return null; + }); } @Config(sdk = 29) @@ -169,31 +179,32 @@ public void storeExcessHeartBeats_cleanUpProperly() { public void shouldSendSdkHeartBeat_answerIsYes() { long currentTime = System.currentTimeMillis(); assertThat(heartBeatInfoStorage.shouldSendSdkHeartBeat(testSdk, 1)).isTrue(); - assertThat(heartBeatSharedPreferences.getLong(testSdk, -1)).isEqualTo(1); + assertThat(heartBeatDataStore.getSync(testSdk, -1L)).isEqualTo(1); assertThat(heartBeatInfoStorage.shouldSendSdkHeartBeat(testSdk, currentTime)).isTrue(); - assertThat(heartBeatSharedPreferences.getLong(testSdk, -1)).isEqualTo(currentTime); + assertThat(heartBeatDataStore.getSync(testSdk, -1L)).isEqualTo(currentTime); } @Test public void shouldSendGlobalHeartBeat_answerIsNo() { - heartBeatSharedPreferences.edit().putLong(GLOBAL, 1).apply(); + heartBeatDataStore.putSync(GLOBAL, 1L); assertThat(heartBeatInfoStorage.shouldSendGlobalHeartBeat(1)).isFalse(); } @Test public void currentDayHeartbeatNotSent_updatesCorrectly() { long millis = System.currentTimeMillis(); + Preferences.Key> testAgent = PreferencesKeys.stringSetKey("test-agent"); + Preferences.Key> testAgent1 = PreferencesKeys.stringSetKey("test-agent-1"); assertThat(heartBeatInfoStorage.getHeartBeatCount()).isEqualTo(0); heartBeatInfoStorage.storeHeartBeat(millis, "test-agent"); assertThat(heartBeatInfoStorage.getHeartBeatCount()).isEqualTo(1); assertThat(heartBeatInfoStorage.getAllHeartBeats().size()).isEqualTo(0); heartBeatInfoStorage.deleteAllHeartBeats(); assertThat(heartBeatInfoStorage.getHeartBeatCount()).isEqualTo(1); - assertThat(heartBeatSharedPreferences.getStringSet("test-agent", new HashSet<>())).isNotEmpty(); + assertThat(heartBeatDataStore.getSync(testAgent, new HashSet<>())).isNotEmpty(); heartBeatInfoStorage.storeHeartBeat(millis, "test-agent-1"); - assertThat(heartBeatSharedPreferences.getStringSet("test-agent", new HashSet<>())).isEmpty(); - assertThat(heartBeatSharedPreferences.getStringSet("test-agent-1", new HashSet<>())) - .isNotEmpty(); + assertThat(heartBeatDataStore.getSync(testAgent, new HashSet<>())).isEmpty(); + assertThat(heartBeatDataStore.getSync(testAgent1, new HashSet<>())).isNotEmpty(); } @Test @@ -222,8 +233,8 @@ public void isSameDate_returnsCorrectly() { public void shouldSendGlobalHeartBeat_answerIsYes() { long currentTime = System.currentTimeMillis(); assertThat(heartBeatInfoStorage.shouldSendGlobalHeartBeat(1)).isTrue(); - assertThat(heartBeatSharedPreferences.getLong(GLOBAL, -1)).isEqualTo(1); + assertThat(heartBeatDataStore.getSync(GLOBAL, -1L)).isEqualTo(1); assertThat(heartBeatInfoStorage.shouldSendGlobalHeartBeat(currentTime)).isTrue(); - assertThat(heartBeatSharedPreferences.getLong(GLOBAL, -1)).isEqualTo(currentTime); + assertThat(heartBeatDataStore.getSync(GLOBAL, -1L)).isEqualTo(currentTime); } } From 77be82fefca8193305cbe358e31c93fc0305c389 Mon Sep 17 00:00:00 2001 From: Daymon Date: Mon, 24 Mar 2025 14:49:28 -0500 Subject: [PATCH 03/16] Update DataStore.kt --- .../com/google/firebase/datastore/DataStore.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt index 0828e5c0519..97afadcec6b 100644 --- a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt +++ b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2025 Google LLC + * + * 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.google.firebase.datastore import android.content.Context From 638eb670cfdb79ae5e900f4549f062dde71f2860 Mon Sep 17 00:00:00 2001 From: Daymon Date: Tue, 25 Mar 2025 12:54:30 -0500 Subject: [PATCH 04/16] Update HeartBeatInfoStorage.java --- .../google/firebase/heartbeatinfo/HeartBeatInfoStorage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java index c2f04ccf0a0..0d59f0103c4 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java @@ -116,7 +116,8 @@ synchronized List getAllHeartBeats() { Set dates = new HashSet<>((Set) entry.getValue()); dates.remove(today); if (!dates.isEmpty()) { - heartBeatResults.add(HeartBeatResult.create(entry.getKey().getName(), new ArrayList<>())); + heartBeatResults.add( + HeartBeatResult.create(entry.getKey().getName(), new ArrayList<>(dates))); } } } From 697ed13439cb531aa248126cfe54a4d7a729f88b Mon Sep 17 00:00:00 2001 From: Daymon Date: Tue, 25 Mar 2025 13:41:39 -0500 Subject: [PATCH 05/16] Update DataStore.kt --- .../google/firebase/datastore/DataStore.kt | 163 +++++++++++++++++- 1 file changed, 154 insertions(+), 9 deletions(-) diff --git a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt index 97afadcec6b..a4ee0431129 100644 --- a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt +++ b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt @@ -27,11 +27,28 @@ import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.runBlocking /** - * Don't use this unless you're bridging Java code Use your own [dataStore] directly in Kotlin code, - * or if you're writing new code. + * Wrapper around [DataStore] for easier migration from `SharedPreferences` in Java code. + * + * Automatically migrates data from any `SharedPreferences` that share the same context and name. + * + * There should only ever be _one_ instance of this class per context and name variant. + * + * > Do **NOT** use this _unless_ you're bridging Java code. If you're writing new code, or your > + * code is in Kotlin, then you should create your own singleton that uses [DataStore] directly. + * + * Example: + * ```java + * DataStorage heartBeatStorage = new DataStorage(applicationContext, "FirebaseHeartBeat"); + * ``` + * + * @property context The [Context] that this data will be saved under. + * @property name What the storage file should be named. */ class DataStorage(val context: Context, val name: String) { - private val transforming = ThreadLocal() + /** + * Used to ensure that there's only ever one call to [editSync] per thread; as to avoid deadlocks. + */ + private val editLock = ThreadLocal() private val Context.dataStore: DataStore by preferencesDataStore( @@ -41,26 +58,134 @@ class DataStorage(val context: Context, val name: String) { private val dataStore = context.dataStore + /** + * Get data from the datastore _synchronously_. + * + * Note that if the key is _not_ in the datastore, while the [defaultValue] will be returned + * instead- it will **not** be saved to the datastore; you'll have to manually do that. + * + * Blocks on the currently running thread. + * + * Example: + * ```java + * Preferences.Key fireCountKey = PreferencesKeys.longKey("fire-count"); + * assert dataStore.get(fireCountKey, 0L) == 0L; + * + * dataStore.putSync(fireCountKey, 102L); + * assert dataStore.get(fireCountKey, 0L) == 102L; + * ``` + * + * @param key The typed key of the entry to get data for. + * @param defaultValue A value to default to, if the key isn't found. + * + * @see Preferences.getOrDefault + */ fun getSync(key: Preferences.Key, defaultValue: T): T = runBlocking { dataStore.data.firstOrNull()?.get(key) ?: defaultValue } + /** + * Checks if a key is present in the datastore _synchronously_. + * + * Blocks on the currently running thread. + * + * Example: + * ```java + * Preferences.Key fireCountKey = PreferencesKeys.longKey("fire-count"); + * assert !dataStore.contains(fireCountKey); + * + * dataStore.putSync(fireCountKey, 102L); + * assert dataStore.contains(fireCountKey); + * ``` + * + * @param key The typed key of the entry to find. + */ fun contains(key: Preferences.Key): Boolean = runBlocking { dataStore.data.firstOrNull()?.contains(key) ?: false } + /** + * Sets and saves data in the datastore _synchronously_. + * + * Existing values will be overwritten. + * + * Blocks on the currently running thread. + * + * Example: + * ```java + * dataStore.putSync(PreferencesKeys.longKey("fire-count"), 102L); + * ``` + * + * @param key The typed key of the entry to save the data under. + * @param value The data to save. + * + * @return The [Preferences] object that the data was saved under. + */ fun putSync(key: Preferences.Key, value: T): Preferences = runBlocking { dataStore.edit { it[key] = value } } - /** Do not modify the returned map (should be obvious, since it's immutable though) */ + /** + * Gets all data in the datastore _synchronously_. + * + * Blocks on the currently running thread. + * + * Example: + * ```java + * ArrayList allDates = new ArrayList<>(); + * + * for (Map.Entry, Object> entry : dataStore.getAllSync().entrySet()) { + * if (entry.getValue() instanceof Set) { + * Set dates = new HashSet<>((Set) entry.getValue()); + * if (!dates.isEmpty()) { + * allDates.add(new ArrayList<>(dates)); + * } + * } + * } + * ``` + * + * @return An _immutable_ map of data currently present in the datastore. + */ fun getAllSync(): Map, Any> = runBlocking { dataStore.data.firstOrNull()?.asMap() ?: emptyMap() } - /** Edit calls should not be called within edit calls. Is there a way to prevent this? */ + /** + * Transactionally edit data in the datastore _synchronously_. + * + * Edits made within the [transform] callback will be saved (committed) all at once once the + * [transform] block exits. + * + * Because of the blocking nature of this function, you should _never_ call [editSync] within an + * already running [transform] block. Since this can cause a deadlock, [editSync] will instead + * throw an exception if it's caught. + * + * Blocks on the currently running thread. + * + * Example: + * ```java + * dataStore.editSync((pref) -> { + * Long heartBeatCount = pref.get(HEART_BEAT_COUNT_TAG); + * if (heartBeatCount == null || heartBeatCount > 30) { + * heartBeatCount = 0L; + * } + * pref.set(HEART_BEAT_COUNT_TAG, heartBeatCount); + * pref.set(LAST_STORED_DATE, "1970-0-1"); + * + * return null; + * }); + * ``` + * + * @param transform A callback to invoke with the [MutablePreferences] object. + * + * @return The [Preferences] object that the data was saved under. + * @throws IllegalStateException If you attempt to call [editSync] within another [transform] + * block. + * + * @see Preferences.getOrDefault + */ fun editSync(transform: (MutablePreferences) -> Unit): Preferences = runBlocking { - if (transforming.get() == true) { + if (editLock.get() == true) { throw IllegalStateException( """ Don't call DataStorage.edit() from within an existing edit() callback. @@ -70,15 +195,35 @@ class DataStorage(val context: Context, val name: String) { .trimIndent() ) } - transforming.set(true) + editLock.set(true) try { dataStore.edit { transform(it) } } finally { - transforming.set(false) + editLock.set(false) } } } -/** Helper for Java code */ +/** + * Helper method for getting the value out of a [Preferences] object if it exists, else falling back + * to the default value. + * + * This is primarily useful when working with an instance of [MutablePreferences] + * - like when working within an [DataStorage.editSync] callback. + * + * Example: + * ```java + * dataStore.editSync((pref) -> { + * long heartBeatCount = DataStoreKt.getOrDefault(pref, HEART_BEAT_COUNT_TAG, 0L); + * heartBeatCount+=1; + * pref.set(HEART_BEAT_COUNT_TAG, heartBeatCount); + * + * return null; + * }); + * ``` + * + * @param key The typed key of the entry to get data for. + * @param defaultValue A value to default to, if the key isn't found. + */ fun Preferences.getOrDefault(key: Preferences.Key, defaultValue: T) = get(key) ?: defaultValue From a00351d7a12b367a71f9fe7bfe6c12f38a2a0ef0 Mon Sep 17 00:00:00 2001 From: Daymon Date: Tue, 25 Mar 2025 15:11:47 -0500 Subject: [PATCH 06/16] Bump components to see if it fixes the crash --- firebase-common/firebase-common.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-common/firebase-common.gradle.kts b/firebase-common/firebase-common.gradle.kts index c0c379a6ace..e337e084027 100644 --- a/firebase-common/firebase-common.gradle.kts +++ b/firebase-common/firebase-common.gradle.kts @@ -55,7 +55,7 @@ android { dependencies { api(libs.kotlin.coroutines.tasks) - api("com.google.firebase:firebase-components:18.0.0") + api("com.google.firebase:firebase-components:18.0.1") api("com.google.firebase:firebase-annotations:16.2.0") implementation("androidx.datastore:datastore-preferences:1.1.3") implementation(libs.androidx.annotation) From 1c756ee587ea4f40c62c444367c5a7bf4ad5b5b4 Mon Sep 17 00:00:00 2001 From: Daymon Date: Tue, 25 Mar 2025 15:59:21 -0500 Subject: [PATCH 07/16] Add logging to try to catch error. --- .../main/java/com/google/firebase/datastore/DataStore.kt | 8 ++++++++ .../firebase/heartbeatinfo/HeartBeatInfoStorage.java | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt index a4ee0431129..2cdf4c0b208 100644 --- a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt +++ b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt @@ -17,6 +17,7 @@ package com.google.firebase.datastore import android.content.Context +import android.util.Log import androidx.datastore.core.DataStore import androidx.datastore.preferences.SharedPreferencesMigration import androidx.datastore.preferences.core.MutablePreferences @@ -56,6 +57,13 @@ class DataStorage(val context: Context, val name: String) { produceMigrations = { listOf(SharedPreferencesMigration(it, name)) } ) + init { + Log.i( + "DataStorage", + "Datastore instance created for context $context (${context.packageName}) with name $name" + ) + } + private val dataStore = context.dataStore /** diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java index 0d59f0103c4..7387a617386 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java @@ -16,6 +16,7 @@ import android.content.Context; import android.os.Build; +import android.util.Log; import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; import androidx.datastore.preferences.core.*; @@ -61,6 +62,11 @@ class HeartBeatInfoStorage { private final DataStorage firebaseDataStore; public HeartBeatInfoStorage(Context applicationContext, String persistenceKey) { + Log.i( + "DataStorage", + String.format( + "Creating a DataStorage instance for context %s (%s) with name %s", + applicationContext, applicationContext.getPackageName(), persistenceKey)); this.firebaseDataStore = new DataStorage(applicationContext, HEARTBEAT_PREFERENCES_NAME + persistenceKey); } @@ -68,6 +74,7 @@ public HeartBeatInfoStorage(Context applicationContext, String persistenceKey) { @VisibleForTesting @RestrictTo(RestrictTo.Scope.TESTS) HeartBeatInfoStorage(DataStorage dataStorage) { + Log.i("DataStorage", "New test storage created"); this.firebaseDataStore = dataStorage; } From 1b53a942e1a532d9f085452783d5c3cab33f336d Mon Sep 17 00:00:00 2001 From: Daymon Date: Wed, 26 Mar 2025 14:17:45 -0500 Subject: [PATCH 08/16] Add additional logging --- .../DefaultHeartBeatController.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java index ff9ba5123fc..b11fb09b65f 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java @@ -17,6 +17,7 @@ import android.content.Context; import android.util.Base64; import android.util.Base64OutputStream; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.core.os.UserManagerCompat; @@ -130,6 +131,11 @@ private DefaultHeartBeatController( Executor executor, Provider userAgentProvider, Context context) { + Log.i( + "DefHeartBeatController", + String.format( + "[DAYMON] Constructor called with context %s and testStorage %s", + context.toString(), testStorage.toString())); storageProvider = testStorage; this.consumers = consumers; this.backgroundExecutor = executor; @@ -139,6 +145,7 @@ private DefaultHeartBeatController( public static @NonNull Component component() { Qualified backgroundExecutor = Qualified.qualified(Background.class, Executor.class); + Log.i("DefHeartBeatController", "[DAYMON] component() called"); return Component.builder( DefaultHeartBeatController.class, HeartBeatController.class, HeartBeatInfo.class) .add(Dependency.required(Context.class)) @@ -147,13 +154,15 @@ private DefaultHeartBeatController( .add(Dependency.requiredProvider(UserAgentPublisher.class)) .add(Dependency.required(backgroundExecutor)) .factory( - c -> - new DefaultHeartBeatController( - c.get(Context.class), - c.get(FirebaseApp.class).getPersistenceKey(), - c.setOf(HeartBeatConsumer.class), - c.getProvider(UserAgentPublisher.class), - c.get(backgroundExecutor))) + c -> { + Log.i("DefHeartBeatController", "[DAYMON] Factory called"); + return new DefaultHeartBeatController( + c.get(Context.class), + c.get(FirebaseApp.class).getPersistenceKey(), + c.setOf(HeartBeatConsumer.class), + c.getProvider(UserAgentPublisher.class), + c.get(backgroundExecutor)); + }) .build(); } From 8459026b58f7f2f47312480619994c72c5929c6d Mon Sep 17 00:00:00 2001 From: Daymon Date: Wed, 26 Mar 2025 14:17:54 -0500 Subject: [PATCH 09/16] Make provider lazy --- .../firebase/heartbeatinfo/DefaultHeartBeatController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java index b11fb09b65f..ce5cae3f975 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java @@ -27,6 +27,7 @@ import com.google.firebase.annotations.concurrent.Background; import com.google.firebase.components.Component; import com.google.firebase.components.Dependency; +import com.google.firebase.components.Lazy; import com.google.firebase.components.Qualified; import com.google.firebase.inject.Provider; import com.google.firebase.platforminfo.UserAgentPublisher; @@ -117,7 +118,7 @@ private DefaultHeartBeatController( Provider userAgentProvider, Executor backgroundExecutor) { this( - () -> new HeartBeatInfoStorage(context, persistenceKey), + new Lazy<>(() -> new HeartBeatInfoStorage(context, persistenceKey)), consumers, backgroundExecutor, userAgentProvider, From 09ac9c30d1fbb58ff9bec18ba53ac09d68b32b9a Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 27 Mar 2025 16:26:13 -0500 Subject: [PATCH 10/16] Update compile/target sdk versions in health metrics --- health-metrics/benchmark/template/app/build.gradle.mustache | 4 ++-- health-metrics/benchmark/template/macrobenchmark/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/health-metrics/benchmark/template/app/build.gradle.mustache b/health-metrics/benchmark/template/app/build.gradle.mustache index 77279ae25d9..a8bf11948eb 100644 --- a/health-metrics/benchmark/template/app/build.gradle.mustache +++ b/health-metrics/benchmark/template/app/build.gradle.mustache @@ -21,14 +21,14 @@ plugins { } android { - compileSdkVersion 32 + compileSdkVersion 34 namespace "com.google.firebase.benchmark" defaultConfig { applicationId 'com.google.firebase.benchmark' minSdkVersion 29 - targetSdkVersion 32 + targetSdkVersion 34 versionCode 1 versionName '1.0' diff --git a/health-metrics/benchmark/template/macrobenchmark/build.gradle b/health-metrics/benchmark/template/macrobenchmark/build.gradle index 8b4556eee76..086de25e4a2 100644 --- a/health-metrics/benchmark/template/macrobenchmark/build.gradle +++ b/health-metrics/benchmark/template/macrobenchmark/build.gradle @@ -33,7 +33,7 @@ android { defaultConfig { minSdk 29 - targetSdk 32 + targetSdk 34 testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } From 08aab02a87e7a88b869af299f5cb620ed3124ace Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 27 Mar 2025 16:26:30 -0500 Subject: [PATCH 11/16] Use version toml --- firebase-common/firebase-common.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-common/firebase-common.gradle.kts b/firebase-common/firebase-common.gradle.kts index e337e084027..fc9ebdf00c3 100644 --- a/firebase-common/firebase-common.gradle.kts +++ b/firebase-common/firebase-common.gradle.kts @@ -57,7 +57,7 @@ dependencies { api("com.google.firebase:firebase-components:18.0.1") api("com.google.firebase:firebase-annotations:16.2.0") - implementation("androidx.datastore:datastore-preferences:1.1.3") + implementation(libs.androidx.datastore.preferences) implementation(libs.androidx.annotation) implementation(libs.androidx.futures) implementation(libs.kotlin.stdlib) From 4dda28bb60d4497493cb277c5d3c5f9db5b31927 Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 27 Mar 2025 16:26:36 -0500 Subject: [PATCH 12/16] Update DataStore.kt --- .../src/main/java/com/google/firebase/datastore/DataStore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt index 2cdf4c0b208..df230774203 100644 --- a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt +++ b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.runBlocking * * There should only ever be _one_ instance of this class per context and name variant. * - * > Do **NOT** use this _unless_ you're bridging Java code. If you're writing new code, or your > + * > Do **NOT** use this _unless_ you're bridging Java code. If you're writing new code, or your * code is in Kotlin, then you should create your own singleton that uses [DataStore] directly. * * Example: From 5c004c5953c3975845873976416cb3c25cb52ed5 Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 27 Mar 2025 16:31:44 -0500 Subject: [PATCH 13/16] Remove logging --- .../google/firebase/datastore/DataStore.kt | 8 ------- .../DefaultHeartBeatController.java | 23 ++++++------------- .../heartbeatinfo/HeartBeatInfoStorage.java | 7 ------ 3 files changed, 7 insertions(+), 31 deletions(-) diff --git a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt index df230774203..831700f75a4 100644 --- a/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt +++ b/firebase-common/src/main/java/com/google/firebase/datastore/DataStore.kt @@ -17,7 +17,6 @@ package com.google.firebase.datastore import android.content.Context -import android.util.Log import androidx.datastore.core.DataStore import androidx.datastore.preferences.SharedPreferencesMigration import androidx.datastore.preferences.core.MutablePreferences @@ -57,13 +56,6 @@ class DataStorage(val context: Context, val name: String) { produceMigrations = { listOf(SharedPreferencesMigration(it, name)) } ) - init { - Log.i( - "DataStorage", - "Datastore instance created for context $context (${context.packageName}) with name $name" - ) - } - private val dataStore = context.dataStore /** diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java index ce5cae3f975..7aa8cfb613d 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/DefaultHeartBeatController.java @@ -17,7 +17,6 @@ import android.content.Context; import android.util.Base64; import android.util.Base64OutputStream; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.core.os.UserManagerCompat; @@ -132,11 +131,6 @@ private DefaultHeartBeatController( Executor executor, Provider userAgentProvider, Context context) { - Log.i( - "DefHeartBeatController", - String.format( - "[DAYMON] Constructor called with context %s and testStorage %s", - context.toString(), testStorage.toString())); storageProvider = testStorage; this.consumers = consumers; this.backgroundExecutor = executor; @@ -146,7 +140,6 @@ private DefaultHeartBeatController( public static @NonNull Component component() { Qualified backgroundExecutor = Qualified.qualified(Background.class, Executor.class); - Log.i("DefHeartBeatController", "[DAYMON] component() called"); return Component.builder( DefaultHeartBeatController.class, HeartBeatController.class, HeartBeatInfo.class) .add(Dependency.required(Context.class)) @@ -155,15 +148,13 @@ private DefaultHeartBeatController( .add(Dependency.requiredProvider(UserAgentPublisher.class)) .add(Dependency.required(backgroundExecutor)) .factory( - c -> { - Log.i("DefHeartBeatController", "[DAYMON] Factory called"); - return new DefaultHeartBeatController( - c.get(Context.class), - c.get(FirebaseApp.class).getPersistenceKey(), - c.setOf(HeartBeatConsumer.class), - c.getProvider(UserAgentPublisher.class), - c.get(backgroundExecutor)); - }) + c -> + new DefaultHeartBeatController( + c.get(Context.class), + c.get(FirebaseApp.class).getPersistenceKey(), + c.setOf(HeartBeatConsumer.class), + c.getProvider(UserAgentPublisher.class), + c.get(backgroundExecutor))) .build(); } diff --git a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java index 7387a617386..0d59f0103c4 100644 --- a/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java +++ b/firebase-common/src/main/java/com/google/firebase/heartbeatinfo/HeartBeatInfoStorage.java @@ -16,7 +16,6 @@ import android.content.Context; import android.os.Build; -import android.util.Log; import androidx.annotation.RestrictTo; import androidx.annotation.VisibleForTesting; import androidx.datastore.preferences.core.*; @@ -62,11 +61,6 @@ class HeartBeatInfoStorage { private final DataStorage firebaseDataStore; public HeartBeatInfoStorage(Context applicationContext, String persistenceKey) { - Log.i( - "DataStorage", - String.format( - "Creating a DataStorage instance for context %s (%s) with name %s", - applicationContext, applicationContext.getPackageName(), persistenceKey)); this.firebaseDataStore = new DataStorage(applicationContext, HEARTBEAT_PREFERENCES_NAME + persistenceKey); } @@ -74,7 +68,6 @@ public HeartBeatInfoStorage(Context applicationContext, String persistenceKey) { @VisibleForTesting @RestrictTo(RestrictTo.Scope.TESTS) HeartBeatInfoStorage(DataStorage dataStorage) { - Log.i("DataStorage", "New test storage created"); this.firebaseDataStore = dataStorage; } From c7e34a9ef3da1f94473e1d3ebfbed18c253df2cd Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 10 Apr 2025 14:30:02 -0500 Subject: [PATCH 14/16] Update firebase-common.gradle.kts --- firebase-common/firebase-common.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-common/firebase-common.gradle.kts b/firebase-common/firebase-common.gradle.kts index fc9ebdf00c3..52114f82558 100644 --- a/firebase-common/firebase-common.gradle.kts +++ b/firebase-common/firebase-common.gradle.kts @@ -55,7 +55,7 @@ android { dependencies { api(libs.kotlin.coroutines.tasks) - api("com.google.firebase:firebase-components:18.0.1") + api("com.google.firebase:firebase-components:18.0.0") api("com.google.firebase:firebase-annotations:16.2.0") implementation(libs.androidx.datastore.preferences) implementation(libs.androidx.annotation) From 607c4171f59a54407bef77fa1c96c47d34bfa621 Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 10 Apr 2025 15:59:08 -0500 Subject: [PATCH 15/16] Exclude datastore proto deps --- firebase-common/firebase-common.gradle.kts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/firebase-common/firebase-common.gradle.kts b/firebase-common/firebase-common.gradle.kts index 52114f82558..64c64e3b1a6 100644 --- a/firebase-common/firebase-common.gradle.kts +++ b/firebase-common/firebase-common.gradle.kts @@ -57,7 +57,10 @@ dependencies { api("com.google.firebase:firebase-components:18.0.0") api("com.google.firebase:firebase-annotations:16.2.0") - implementation(libs.androidx.datastore.preferences) + implementation(libs.androidx.datastore.preferences) { + exclude("androidx.datastore", "datastore-preferences-proto") + exclude("androidx.datastore", "datastore-preferences-external-protobuf") + } implementation(libs.androidx.annotation) implementation(libs.androidx.futures) implementation(libs.kotlin.stdlib) From 42824aa95718b37fac4b9ce01c3d6d37129826e4 Mon Sep 17 00:00:00 2001 From: Daymon Date: Thu, 10 Apr 2025 16:01:13 -0500 Subject: [PATCH 16/16] Revert "Exclude datastore proto deps" This reverts commit 607c4171f59a54407bef77fa1c96c47d34bfa621. --- firebase-common/firebase-common.gradle.kts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/firebase-common/firebase-common.gradle.kts b/firebase-common/firebase-common.gradle.kts index 64c64e3b1a6..52114f82558 100644 --- a/firebase-common/firebase-common.gradle.kts +++ b/firebase-common/firebase-common.gradle.kts @@ -57,10 +57,7 @@ dependencies { api("com.google.firebase:firebase-components:18.0.0") api("com.google.firebase:firebase-annotations:16.2.0") - implementation(libs.androidx.datastore.preferences) { - exclude("androidx.datastore", "datastore-preferences-proto") - exclude("androidx.datastore", "datastore-preferences-external-protobuf") - } + implementation(libs.androidx.datastore.preferences) implementation(libs.androidx.annotation) implementation(libs.androidx.futures) implementation(libs.kotlin.stdlib)