setCustomInstallationId(@Nullable String customInstallationId) {
+ if (customInstallationId == null) {
+ return clearCustomInstallationId();
+ }
+ return updateCustomInstallationId(customInstallationId);
+ }
+
+ /**
+ * Update custom installation id of the {@link FirebaseApp} on Firebase segmentation backend and
+ * client side cache.
+ *
+ *
+ * The workflow is:
+ * check diff against cache or cache status is not SYNCED
+ * |
+ * get Firebase instance id and token
+ * | |
+ * | update cache with cache status PENDING_UPDATE
+ * | |
+ * send http request to backend
+ * |
+ * on success: set cache entry status to SYNCED
+ * |
+ * return
+ *
+ */
+ private Task updateCustomInstallationId(String customInstallationId) {
+ CustomInstallationIdCacheEntryValue cacheEntryValue = localCache.readCacheEntryValue();
+ if (cacheEntryValue != null
+ && cacheEntryValue.getCustomInstallationId().equals(customInstallationId)
+ && cacheEntryValue.getCacheStatus() == CustomInstallationIdCache.CacheStatus.SYNCED) {
+ // If the given custom installation id matches up the cached
+ // value, there's no need to update.
+ return Tasks.forResult(null);
+ }
+
+ Task instanceIdResultTask = firebaseInstanceId.getInstanceId();
+ Task firstUpdateCacheResultTask =
+ instanceIdResultTask.onSuccessTask(
+ instanceIdResult ->
+ localCache.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
+ customInstallationId,
+ instanceIdResult.getId(),
+ CustomInstallationIdCache.CacheStatus.PENDING_UPDATE)));
+
+ // Start requesting backend when first cache update is done.
+ Task backendRequestResultTask =
+ firstUpdateCacheResultTask.onSuccessTask(
+ firstUpdateCacheResult -> {
+ if (firstUpdateCacheResult) {
+ String iid = instanceIdResultTask.getResult().getId();
+ String iidToken = instanceIdResultTask.getResult().getToken();
+ return backendServiceClient.updateCustomInstallationId(
+ Utils.getProjectNumberFromAppId(firebaseApp.getOptions().getApplicationId()),
+ customInstallationId,
+ iid,
+ iidToken);
+ } else {
+ throw new SetCustomInstallationIdException(
+ "Failed to update client side cache", Status.CLIENT_ERROR);
+ }
+ });
+
+ Task finalUpdateCacheResultTask =
+ backendRequestResultTask.onSuccessTask(
+ backendRequestResult -> {
+ switch (backendRequestResult) {
+ case OK:
+ return localCache.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
+ customInstallationId,
+ instanceIdResultTask.getResult().getId(),
+ CustomInstallationIdCache.CacheStatus.SYNCED));
+ case ALREADY_EXISTS:
+ throw new SetCustomInstallationIdException(
+ Status.DUPLICATED_CUSTOM_INSTALLATION_ID);
+ default:
+ throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
+ }
+ });
+
+ return finalUpdateCacheResultTask.onSuccessTask(
+ finalUpdateCacheResult -> {
+ if (finalUpdateCacheResult) {
+ return Tasks.forResult(null);
+ } else {
+ throw new SetCustomInstallationIdException(
+ "Failed to update client side cache", Status.CLIENT_ERROR);
+ }
+ });
+ }
+
+ /**
+ * Clear custom installation id of the {@link FirebaseApp} on Firebase segmentation backend and
+ * client side cache.
+ *
+ *
+ * The workflow is:
+ * get Firebase instance id and token
+ * | |
+ * | update cache with cache status PENDING_CLEAR
+ * | |
+ * send http request to backend
+ * |
+ * on success: delete cache entry
+ * |
+ * return
+ *
+ */
+ private Task clearCustomInstallationId() {
+ Task instanceIdResultTask = firebaseInstanceId.getInstanceId();
+ Task firstUpdateCacheResultTask =
+ instanceIdResultTask.onSuccessTask(
+ instanceIdResult ->
+ localCache.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
+ "",
+ instanceIdResult.getId(),
+ CustomInstallationIdCache.CacheStatus.PENDING_CLEAR)));
+
+ Task backendRequestResultTask =
+ firstUpdateCacheResultTask.onSuccessTask(
+ firstUpdateCacheResult -> {
+ if (firstUpdateCacheResult) {
+ String iid = instanceIdResultTask.getResult().getId();
+ String iidToken = instanceIdResultTask.getResult().getToken();
+ return backendServiceClient.clearCustomInstallationId(
+ Utils.getProjectNumberFromAppId(firebaseApp.getOptions().getApplicationId()),
+ iid,
+ iidToken);
+ } else {
+ throw new SetCustomInstallationIdException(
+ "Failed to update client side cache", Status.CLIENT_ERROR);
+ }
+ });
+
+ Task finalUpdateCacheResultTask =
+ backendRequestResultTask.onSuccessTask(
+ backendRequestResult -> {
+ if (backendRequestResult == Code.OK) {
+ return localCache.clear();
+ } else {
+ throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
+ }
+ });
+
+ return finalUpdateCacheResultTask.onSuccessTask(
+ finalUpdateCacheResult -> {
+ if (finalUpdateCacheResult) {
+ return Tasks.forResult(null);
+ } else {
+ throw new SetCustomInstallationIdException(
+ "Failed to update client side cache", Status.CLIENT_ERROR);
+ }
+ });
}
}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java
new file mode 100644
index 00000000000..3c957ce3294
--- /dev/null
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java
@@ -0,0 +1,68 @@
+// Copyright 2019 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.segmentation;
+
+import androidx.annotation.NonNull;
+import com.google.firebase.FirebaseException;
+
+/** The class for all Exceptions thrown by {@link FirebaseSegmentation}. */
+public class SetCustomInstallationIdException extends FirebaseException {
+
+ public enum Status {
+ UNKOWN(0),
+
+ /** Error in Firebase SDK. */
+ CLIENT_ERROR(1),
+
+ /** Error when calling Firebase segmentation backend. */
+ BACKEND_ERROR(2),
+
+ /** The given custom installation is already tied to another Firebase installation. */
+ DUPLICATED_CUSTOM_INSTALLATION_ID(3);
+
+ private final int value;
+
+ Status(int value) {
+ this.value = value;
+ }
+ }
+
+ @NonNull private final Status status;
+
+ SetCustomInstallationIdException(@NonNull Status status) {
+ this.status = status;
+ }
+
+ SetCustomInstallationIdException(@NonNull String message, @NonNull Status status) {
+ super(message);
+ this.status = status;
+ }
+
+ SetCustomInstallationIdException(
+ @NonNull String message, @NonNull Status status, Throwable cause) {
+ super(message, cause);
+ this.status = status;
+ }
+
+ /**
+ * Gets the status code for the operation that failed.
+ *
+ * @return the code for the SetCustomInstallationIdException
+ */
+ @NonNull
+ public Status getStatus() {
+ return status;
+ }
+}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/Utils.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/Utils.java
new file mode 100644
index 00000000000..ca231a89cb5
--- /dev/null
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/Utils.java
@@ -0,0 +1,33 @@
+// Copyright 2019 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.segmentation;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Util methods used for {@link FirebaseSegmentation} */
+class Utils {
+
+ private static final Pattern APP_ID_PATTERN =
+ Pattern.compile("^[^:]+:([0-9]+):(android|ios|web):([0-9a-f]+)");
+
+ static long getProjectNumberFromAppId(String appId) {
+ Matcher matcher = APP_ID_PATTERN.matcher(appId);
+ if (matcher.matches()) {
+ return Long.valueOf(matcher.group(1));
+ }
+ throw new IllegalArgumentException("Invalid app id " + appId);
+ }
+}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
index 2b93231eb39..a1346062440 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
@@ -17,27 +17,30 @@
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import com.google.android.gms.common.util.Strings;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.firebase.FirebaseApp;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
-class CustomInstallationIdCache {
+/**
+ * A layer that locally caches a few Firebase Segmentation attributes on top the Segmentation
+ * backend API.
+ */
+public class CustomInstallationIdCache {
// Status of each cache entry
// NOTE: never change the ordinal of the enum values because the enum values are stored in cache
// as their ordinal numbers.
- enum CacheStatus {
+ public enum CacheStatus {
// Cache entry is synced to Firebase backend
SYNCED,
- // Cache entry is waiting for Firebase backend response or pending internal retry for retryable
- // errors.
- PENDING,
- // Cache entry is not accepted by Firebase backend.
- ERROR,
+ // Cache entry is waiting for Firebase backend response or internal network retry (for update
+ // operation).
+ PENDING_UPDATE,
+ // Cache entry is waiting for Firebase backend response or internal network retry (for clear
+ // operation).
+ PENDING_CLEAR
}
private static final String SHARED_PREFS_NAME = "CustomInstallationIdCache";
@@ -46,85 +49,59 @@ enum CacheStatus {
private static final String INSTANCE_ID_KEY = "Iid";
private static final String CACHE_STATUS_KEY = "Status";
- private static CustomInstallationIdCache singleton = null;
private final Executor ioExecuter;
private final SharedPreferences prefs;
+ private final String persistenceKey;
- static synchronized CustomInstallationIdCache getInstance() {
- if (singleton == null) {
- singleton = new CustomInstallationIdCache();
- }
- return singleton;
- }
-
- private CustomInstallationIdCache() {
- // Since different FirebaseApp in the same Android application should have the same application
- // context and same dir path, so that use the context of the default FirebaseApp to create the
- // shared preferences.
+ public CustomInstallationIdCache(FirebaseApp firebaseApp) {
+ // Different FirebaseApp in the same Android application should have the same application
+ // context and same dir path
prefs =
- FirebaseApp.getInstance()
+ firebaseApp
.getApplicationContext()
.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
-
+ persistenceKey = firebaseApp.getPersistenceKey();
ioExecuter = Executors.newFixedThreadPool(2);
}
@Nullable
- synchronized CustomInstallationIdCacheEntryValue readCacheEntryValue(FirebaseApp firebaseApp) {
- String cid =
- prefs.getString(getSharedPreferencesKey(firebaseApp, CUSTOM_INSTALLATION_ID_KEY), null);
- String iid = prefs.getString(getSharedPreferencesKey(firebaseApp, INSTANCE_ID_KEY), null);
- int status = prefs.getInt(getSharedPreferencesKey(firebaseApp, CACHE_STATUS_KEY), -1);
+ public synchronized CustomInstallationIdCacheEntryValue readCacheEntryValue() {
+ String cid = prefs.getString(getSharedPreferencesKey(CUSTOM_INSTALLATION_ID_KEY), null);
+ String iid = prefs.getString(getSharedPreferencesKey(INSTANCE_ID_KEY), null);
+ int status = prefs.getInt(getSharedPreferencesKey(CACHE_STATUS_KEY), -1);
- if (Strings.isEmptyOrWhitespace(cid) || Strings.isEmptyOrWhitespace(iid) || status == -1) {
+ if (cid == null || iid == null || status == -1) {
return null;
}
return CustomInstallationIdCacheEntryValue.create(cid, iid, CacheStatus.values()[status]);
}
- synchronized Task insertOrUpdateCacheEntry(
- FirebaseApp firebaseApp, CustomInstallationIdCacheEntryValue entryValue) {
+ public synchronized Task insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue entryValue) {
SharedPreferences.Editor editor = prefs.edit();
editor.putString(
- getSharedPreferencesKey(firebaseApp, CUSTOM_INSTALLATION_ID_KEY),
- entryValue.getCustomInstallationId());
- editor.putString(
- getSharedPreferencesKey(firebaseApp, INSTANCE_ID_KEY), entryValue.getFirebaseInstanceId());
- editor.putInt(
- getSharedPreferencesKey(firebaseApp, CACHE_STATUS_KEY),
- entryValue.getCacheStatus().ordinal());
- return commitSharedPreferencesEditAsync(editor);
- }
-
- synchronized Task clear(FirebaseApp firebaseApp) {
- SharedPreferences.Editor editor = prefs.edit();
- editor.remove(getSharedPreferencesKey(firebaseApp, CUSTOM_INSTALLATION_ID_KEY));
- editor.remove(getSharedPreferencesKey(firebaseApp, INSTANCE_ID_KEY));
- editor.remove(getSharedPreferencesKey(firebaseApp, CACHE_STATUS_KEY));
+ getSharedPreferencesKey(CUSTOM_INSTALLATION_ID_KEY), entryValue.getCustomInstallationId());
+ editor.putString(getSharedPreferencesKey(INSTANCE_ID_KEY), entryValue.getFirebaseInstanceId());
+ editor.putInt(getSharedPreferencesKey(CACHE_STATUS_KEY), entryValue.getCacheStatus().ordinal());
return commitSharedPreferencesEditAsync(editor);
}
- @RestrictTo(RestrictTo.Scope.TESTS)
- synchronized Task clearAll() {
+ public synchronized Task clear() {
SharedPreferences.Editor editor = prefs.edit();
- editor.clear();
+ editor.remove(getSharedPreferencesKey(CUSTOM_INSTALLATION_ID_KEY));
+ editor.remove(getSharedPreferencesKey(INSTANCE_ID_KEY));
+ editor.remove(getSharedPreferencesKey(CACHE_STATUS_KEY));
return commitSharedPreferencesEditAsync(editor);
}
- private static String getSharedPreferencesKey(FirebaseApp firebaseApp, String key) {
- return String.format("%s|%s", firebaseApp.getPersistenceKey(), key);
+ private String getSharedPreferencesKey(String key) {
+ return String.format("%s|%s", persistenceKey, key);
}
private Task commitSharedPreferencesEditAsync(SharedPreferences.Editor editor) {
- TaskCompletionSource result = new TaskCompletionSource();
- ioExecuter.execute(
- new Runnable() {
- @Override
- public void run() {
- result.setResult(editor.commit());
- }
- });
+ TaskCompletionSource result = new TaskCompletionSource<>();
+ ioExecuter.execute(() -> result.setResult(editor.commit()));
return result.getTask();
}
}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java
index 2d3b5f3c3a6..05528cd40f0 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java
@@ -22,14 +22,14 @@
* Firebase instance id, a custom installation id and the cache status of this entry.
*/
@AutoValue
-abstract class CustomInstallationIdCacheEntryValue {
- abstract String getCustomInstallationId();
+public abstract class CustomInstallationIdCacheEntryValue {
+ public abstract String getCustomInstallationId();
- abstract String getFirebaseInstanceId();
+ public abstract String getFirebaseInstanceId();
- abstract CacheStatus getCacheStatus();
+ public abstract CacheStatus getCacheStatus();
- static CustomInstallationIdCacheEntryValue create(
+ public static CustomInstallationIdCacheEntryValue create(
String customInstallationId, String firebaseInstanceId, CacheStatus cacheStatus) {
return new AutoValue_CustomInstallationIdCacheEntryValue(
customInstallationId, firebaseInstanceId, cacheStatus);
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
new file mode 100644
index 00000000000..59398b369d5
--- /dev/null
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
@@ -0,0 +1,56 @@
+// Copyright 2019 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.segmentation.remote;
+
+import com.google.android.gms.tasks.Task;
+import com.google.android.gms.tasks.Tasks;
+import com.squareup.okhttp.OkHttpClient;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+/** Http client that sends request to Firebase Segmentation backend API. To be implemented */
+public class SegmentationServiceClient {
+
+ private final OkHttpClient httpClient;
+ private final Executor httpRequestExecutor;
+
+ public enum Code {
+ OK,
+
+ SERVER_INTERNAL_ERROR,
+
+ ALREADY_EXISTS,
+
+ PERMISSION_DENIED
+ }
+
+ public SegmentationServiceClient() {
+ httpClient = new OkHttpClient();
+ httpRequestExecutor = Executors.newFixedThreadPool(4);
+ }
+
+ public Task updateCustomInstallationId(
+ long projectNumber,
+ String customInstallationId,
+ String firebaseInstanceId,
+ String firebaseInstanceIdToken) {
+ return Tasks.forResult(Code.OK);
+ }
+
+ public Task clearCustomInstallationId(
+ long projectNumber, String firebaseInstanceId, String firebaseInstanceIdToken) {
+ return Tasks.forResult(Code.OK);
+ }
+}
diff --git a/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrarTest.java b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrarTest.java
index 1f3c441808f..56b0d120eb0 100644
--- a/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrarTest.java
+++ b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrarTest.java
@@ -15,7 +15,6 @@
package com.google.firebase.segmentation;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import androidx.test.core.app.ApplicationProvider;
import com.google.firebase.FirebaseApp;
@@ -48,10 +47,8 @@ public void getFirebaseInstallationsInstance() {
FirebaseSegmentation defaultSegmentation = FirebaseSegmentation.getInstance();
assertNotNull(defaultSegmentation);
- assertNull(defaultSegmentation.setCustomInstallationId("12345").getResult());
FirebaseSegmentation anotherSegmentation = FirebaseSegmentation.getInstance(anotherApp);
assertNotNull(anotherSegmentation);
- assertNull(anotherSegmentation.setCustomInstallationId("ghdjaas").getResult());
}
}
From d07ec25295de8298f4e18f68b03e2758abf44b76 Mon Sep 17 00:00:00 2001
From: Di Wu <49409954+diwu-arete@users.noreply.github.com>
Date: Mon, 22 Jul 2019 12:39:55 -0700
Subject: [PATCH 06/17] Http client in Firebase Segmentation SDK to call
backend service. (#573)
* Implement Firebase segmentation SDK device local cache
* [Firebase Segmentation] Add custom installation id cache layer and tests for it.
* Add test for updating cache
* Switch to use SQLiteOpenHelper
* Switch to use SharedPreferences from SQLite.
* Change the cache class to be singleton
* Wrap shared pref commit in a async task.
* Address comments
* Google format fix
* Replace some deprecated code.
* Package refactor
* nit
* nit
* Add the state machine of updating custom installation id in the local
cache and update to Firebase Segmentation backend. CL also contains unit
tests.
(The http client is not implemented yet.)
* minor format fix
* Address comments #1
* Http client in Firebase Segmentation SDK to call backend service.
* Revert unintentional change
* Fix connected device test
* Fix connected device test
* 1. Add a few annotations to make java code Kotlin friendly
2. Some fixes for the http request format
* Fix java format
* Fix API version
* Change the segmentation API implementation to synchronous and put the
entire synchronous code block in async task.
* Fix a async getResult race issue.
* OkHttpClient -> HttpsUrlConnection
* Use gzip for compressing content and fix ourput stream memory leak risk.
* Addressed a few comments
---
.../firebase-segmentation.gradle | 52 +---
.../FirebaseSegmentationInstrumentedTest.java | 45 ++--
.../local/CustomInstallationIdCacheTest.java | 21 +-
.../segmentation/FirebaseSegmentation.java | 227 +++++++++---------
.../FirebaseSegmentationRegistrar.java | 2 +
.../local/CustomInstallationIdCache.java | 27 +--
.../CustomInstallationIdCacheEntryValue.java | 9 +-
.../remote/SegmentationServiceClient.java | 158 ++++++++++--
8 files changed, 309 insertions(+), 232 deletions(-)
diff --git a/firebase-segmentation/firebase-segmentation.gradle b/firebase-segmentation/firebase-segmentation.gradle
index 1f76b8b593a..c11085bd161 100644
--- a/firebase-segmentation/firebase-segmentation.gradle
+++ b/firebase-segmentation/firebase-segmentation.gradle
@@ -14,45 +14,12 @@
plugins {
id 'firebase-library'
- id 'com.google.protobuf'
}
firebaseLibrary {
testLab.enabled = true
}
-protobuf {
- // Configure the protoc executable
- protoc {
- // Download from repositories
- artifact = 'com.google.protobuf:protoc:3.4.0'
- }
- plugins {
- grpc {
- artifact = 'io.grpc:protoc-gen-grpc-java:1.12.0'
- }
- javalite {
- // The codegen for lite comes as a separate artifact
- artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
- }
- }
- generateProtoTasks {
- all().each { task ->
- task.builtins {
- // In most cases you don't need the full Java output
- // if you use the lite output.
- remove java
- }
- task.plugins {
- grpc {
- option 'lite'
- }
- javalite {}
- }
- }
- }
-}
-
android {
compileSdkVersion project.targetSdkVersion
@@ -63,13 +30,6 @@ android {
versionName version
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
- sourceSets {
- main {
- proto {
- srcDir 'src/main/proto'
- }
- }
- }
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
@@ -83,18 +43,14 @@ android {
dependencies {
implementation project(':firebase-common')
- implementation project(':protolite-well-known-types')
implementation('com.google.firebase:firebase-iid:17.0.3') {
exclude group: "com.google.firebase", module: "firebase-common"
}
- implementation 'io.grpc:grpc-stub:1.21.0'
- implementation 'io.grpc:grpc-protobuf-lite:1.21.0'
- implementation 'io.grpc:grpc-okhttp:1.21.0'
+
implementation 'androidx.appcompat:appcompat:1.0.2'
- implementation 'androidx.multidex:multidex:2.0.0'
- implementation 'com.google.android.gms:play-services-tasks:16.0.1'
- implementation 'com.squareup.okhttp:okhttp:2.7.5'
+ implementation 'androidx.multidex:multidex:2.0.1'
+ implementation 'com.google.android.gms:play-services-tasks:17.0.0'
compileOnly "com.google.auto.value:auto-value-annotations:1.6.5"
annotationProcessor "com.google.auto.value:auto-value:1.6.2"
@@ -103,7 +59,7 @@ dependencies {
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:$robolectricVersion"
- androidTestImplementation "androidx.annotation:annotation:1.1.0"
+ androidTestImplementation "androidx.annotation:annotation:1.0.0"
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test:runner:1.2.0'
diff --git a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java b/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java
index 8519e641cb7..19498782a78 100644
--- a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java
+++ b/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java
@@ -69,19 +69,24 @@ public void setUp() {
firebaseApp =
FirebaseApp.initializeApp(
ApplicationProvider.getApplicationContext(),
- new FirebaseOptions.Builder().setApplicationId("1:123456789:android:abcdef").build());
+ new FirebaseOptions.Builder()
+ .setApplicationId("1:123456789:android:abcdef")
+ .setApiKey("api_key")
+ .build());
actualCache = new CustomInstallationIdCache(firebaseApp);
when(backendClientReturnsOk.updateCustomInstallationId(
+ anyLong(), anyString(), anyString(), anyString(), anyString()))
+ .thenReturn(SegmentationServiceClient.Code.OK);
+ when(backendClientReturnsOk.clearCustomInstallationId(
anyLong(), anyString(), anyString(), anyString()))
- .thenReturn(Tasks.forResult(SegmentationServiceClient.Code.OK));
- when(backendClientReturnsOk.clearCustomInstallationId(anyLong(), anyString(), anyString()))
- .thenReturn(Tasks.forResult(SegmentationServiceClient.Code.OK));
+ .thenReturn(SegmentationServiceClient.Code.OK);
when(backendClientReturnsError.updateCustomInstallationId(
+ anyLong(), anyString(), anyString(), anyString(), anyString()))
+ .thenReturn(SegmentationServiceClient.Code.SERVER_ERROR);
+ when(backendClientReturnsError.clearCustomInstallationId(
anyLong(), anyString(), anyString(), anyString()))
- .thenReturn(Tasks.forResult(SegmentationServiceClient.Code.SERVER_INTERNAL_ERROR));
- when(backendClientReturnsError.clearCustomInstallationId(anyLong(), anyString(), anyString()))
- .thenReturn(Tasks.forResult(SegmentationServiceClient.Code.SERVER_INTERNAL_ERROR));
+ .thenReturn(SegmentationServiceClient.Code.SERVER_ERROR);
when(firebaseInstanceId.getInstanceId())
.thenReturn(
Tasks.forResult(
@@ -98,13 +103,13 @@ public String getToken() {
return "iid_token";
}
}));
- when(cacheReturnsError.insertOrUpdateCacheEntry(any())).thenReturn(Tasks.forResult(false));
+ when(cacheReturnsError.insertOrUpdateCacheEntry(any())).thenReturn(false);
when(cacheReturnsError.readCacheEntryValue()).thenReturn(null);
}
@After
public void cleanUp() throws Exception {
- Tasks.await(actualCache.clear());
+ actualCache.clear();
}
@Test
@@ -165,12 +170,11 @@ public void testUpdateCustomInstallationId_CacheError_BackendOk() throws Interru
@Test
public void testClearCustomInstallationId_CacheOk_BackendOk() throws Exception {
- Tasks.await(
- actualCache.insertOrUpdateCacheEntry(
- CustomInstallationIdCacheEntryValue.create(
- CUSTOM_INSTALLATION_ID,
- FIREBASE_INSTANCE_ID,
- CustomInstallationIdCache.CacheStatus.SYNCED)));
+ actualCache.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
+ CUSTOM_INSTALLATION_ID,
+ FIREBASE_INSTANCE_ID,
+ CustomInstallationIdCache.CacheStatus.SYNCED));
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsOk);
@@ -183,12 +187,11 @@ public void testClearCustomInstallationId_CacheOk_BackendOk() throws Exception {
@Test
public void testClearCustomInstallationId_CacheOk_BackendError() throws Exception {
- Tasks.await(
- actualCache.insertOrUpdateCacheEntry(
- CustomInstallationIdCacheEntryValue.create(
- CUSTOM_INSTALLATION_ID,
- FIREBASE_INSTANCE_ID,
- CustomInstallationIdCache.CacheStatus.SYNCED)));
+ actualCache.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
+ CUSTOM_INSTALLATION_ID,
+ FIREBASE_INSTANCE_ID,
+ CustomInstallationIdCache.CacheStatus.SYNCED));
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsError);
diff --git a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java b/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java
index 019a2b8ba08..9e22e522d2b 100644
--- a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java
+++ b/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java
@@ -20,7 +20,6 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import org.junit.After;
@@ -55,8 +54,8 @@ public void setUp() {
@After
public void cleanUp() throws Exception {
- Tasks.await(cache0.clear());
- Tasks.await(cache1.clear());
+ cache0.clear();
+ cache1.clear();
}
@Test
@@ -68,12 +67,9 @@ public void testReadCacheEntry_Null() {
@Test
public void testUpdateAndReadCacheEntry() throws Exception {
assertTrue(
- Tasks.await(
- cache0.insertOrUpdateCacheEntry(
- CustomInstallationIdCacheEntryValue.create(
- "123456",
- "cAAAAAAAAAA",
- CustomInstallationIdCache.CacheStatus.PENDING_UPDATE))));
+ cache0.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
+ "123456", "cAAAAAAAAAA", CustomInstallationIdCache.CacheStatus.PENDING_UPDATE)));
CustomInstallationIdCacheEntryValue entryValue = cache0.readCacheEntryValue();
assertThat(entryValue.getCustomInstallationId()).isEqualTo("123456");
assertThat(entryValue.getFirebaseInstanceId()).isEqualTo("cAAAAAAAAAA");
@@ -82,10 +78,9 @@ public void testUpdateAndReadCacheEntry() throws Exception {
assertNull(cache1.readCacheEntryValue());
assertTrue(
- Tasks.await(
- cache0.insertOrUpdateCacheEntry(
- CustomInstallationIdCacheEntryValue.create(
- "123456", "cAAAAAAAAAA", CustomInstallationIdCache.CacheStatus.SYNCED))));
+ cache0.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
+ "123456", "cAAAAAAAAAA", CustomInstallationIdCache.CacheStatus.SYNCED)));
entryValue = cache0.readCacheEntryValue();
assertThat(entryValue.getCustomInstallationId()).isEqualTo("123456");
assertThat(entryValue.getFirebaseInstanceId()).isEqualTo("cAAAAAAAAAA");
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
index 34de68597c1..2008ea09359 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
@@ -16,7 +16,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
+import androidx.annotation.WorkerThread;
import com.google.android.gms.common.internal.Preconditions;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
@@ -28,6 +28,9 @@
import com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue;
import com.google.firebase.segmentation.remote.SegmentationServiceClient;
import com.google.firebase.segmentation.remote.SegmentationServiceClient.Code;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
/** Entry point of Firebase Segmentation SDK. */
public class FirebaseSegmentation {
@@ -36,15 +39,16 @@ public class FirebaseSegmentation {
private final FirebaseInstanceId firebaseInstanceId;
private final CustomInstallationIdCache localCache;
private final SegmentationServiceClient backendServiceClient;
+ private final Executor executor;
FirebaseSegmentation(FirebaseApp firebaseApp) {
- this.firebaseApp = firebaseApp;
- this.firebaseInstanceId = FirebaseInstanceId.getInstance(firebaseApp);
- localCache = new CustomInstallationIdCache(firebaseApp);
- backendServiceClient = new SegmentationServiceClient();
+ this(
+ firebaseApp,
+ FirebaseInstanceId.getInstance(firebaseApp),
+ new CustomInstallationIdCache(firebaseApp),
+ new SegmentationServiceClient());
}
- @RestrictTo(RestrictTo.Scope.TESTS)
FirebaseSegmentation(
FirebaseApp firebaseApp,
FirebaseInstanceId firebaseInstanceId,
@@ -54,6 +58,7 @@ public class FirebaseSegmentation {
this.firebaseInstanceId = firebaseInstanceId;
this.localCache = localCache;
this.backendServiceClient = backendServiceClient;
+ this.executor = Executors.newFixedThreadPool(4);
}
/**
@@ -82,9 +87,9 @@ public static FirebaseSegmentation getInstance(@NonNull FirebaseApp app) {
@NonNull
public synchronized Task setCustomInstallationId(@Nullable String customInstallationId) {
if (customInstallationId == null) {
- return clearCustomInstallationId();
+ return Tasks.call(executor, () -> clearCustomInstallationId());
}
- return updateCustomInstallationId(customInstallationId);
+ return Tasks.call(executor, () -> updateCustomInstallationId(customInstallationId));
}
/**
@@ -106,71 +111,73 @@ public synchronized Task setCustomInstallationId(@Nullable String customIn
* return
*
*/
- private Task updateCustomInstallationId(String customInstallationId) {
+ @WorkerThread
+ private Void updateCustomInstallationId(String customInstallationId)
+ throws SetCustomInstallationIdException {
CustomInstallationIdCacheEntryValue cacheEntryValue = localCache.readCacheEntryValue();
if (cacheEntryValue != null
&& cacheEntryValue.getCustomInstallationId().equals(customInstallationId)
&& cacheEntryValue.getCacheStatus() == CustomInstallationIdCache.CacheStatus.SYNCED) {
// If the given custom installation id matches up the cached
// value, there's no need to update.
- return Tasks.forResult(null);
+ return null;
+ }
+
+ InstanceIdResult instanceIdResult;
+ try {
+ instanceIdResult = Tasks.await(firebaseInstanceId.getInstanceId());
+ } catch (ExecutionException | InterruptedException e) {
+ throw new SetCustomInstallationIdException(
+ "Failed to get Firebase instance id", Status.CLIENT_ERROR);
}
- Task instanceIdResultTask = firebaseInstanceId.getInstanceId();
- Task firstUpdateCacheResultTask =
- instanceIdResultTask.onSuccessTask(
- instanceIdResult ->
- localCache.insertOrUpdateCacheEntry(
- CustomInstallationIdCacheEntryValue.create(
- customInstallationId,
- instanceIdResult.getId(),
- CustomInstallationIdCache.CacheStatus.PENDING_UPDATE)));
-
- // Start requesting backend when first cache update is done.
- Task backendRequestResultTask =
- firstUpdateCacheResultTask.onSuccessTask(
- firstUpdateCacheResult -> {
- if (firstUpdateCacheResult) {
- String iid = instanceIdResultTask.getResult().getId();
- String iidToken = instanceIdResultTask.getResult().getToken();
- return backendServiceClient.updateCustomInstallationId(
- Utils.getProjectNumberFromAppId(firebaseApp.getOptions().getApplicationId()),
+ boolean firstUpdateCacheResult =
+ localCache.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
+ customInstallationId,
+ instanceIdResult.getId(),
+ CustomInstallationIdCache.CacheStatus.PENDING_UPDATE));
+
+ if (!firstUpdateCacheResult) {
+ throw new SetCustomInstallationIdException(
+ "Failed to update client side cache", Status.CLIENT_ERROR);
+ }
+
+ // Start requesting backend when first cache updae is done.
+ String iid = instanceIdResult.getId();
+ String iidToken = instanceIdResult.getToken();
+ Code backendRequestResult =
+ backendServiceClient.updateCustomInstallationId(
+ Utils.getProjectNumberFromAppId(firebaseApp.getOptions().getApplicationId()),
+ firebaseApp.getOptions().getApiKey(),
+ customInstallationId,
+ iid,
+ iidToken);
+
+ boolean finalUpdateCacheResult;
+ switch (backendRequestResult) {
+ case OK:
+ finalUpdateCacheResult =
+ localCache.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
customInstallationId,
- iid,
- iidToken);
- } else {
- throw new SetCustomInstallationIdException(
- "Failed to update client side cache", Status.CLIENT_ERROR);
- }
- });
-
- Task finalUpdateCacheResultTask =
- backendRequestResultTask.onSuccessTask(
- backendRequestResult -> {
- switch (backendRequestResult) {
- case OK:
- return localCache.insertOrUpdateCacheEntry(
- CustomInstallationIdCacheEntryValue.create(
- customInstallationId,
- instanceIdResultTask.getResult().getId(),
- CustomInstallationIdCache.CacheStatus.SYNCED));
- case ALREADY_EXISTS:
- throw new SetCustomInstallationIdException(
- Status.DUPLICATED_CUSTOM_INSTALLATION_ID);
- default:
- throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
- }
- });
-
- return finalUpdateCacheResultTask.onSuccessTask(
- finalUpdateCacheResult -> {
- if (finalUpdateCacheResult) {
- return Tasks.forResult(null);
- } else {
- throw new SetCustomInstallationIdException(
- "Failed to update client side cache", Status.CLIENT_ERROR);
- }
- });
+ instanceIdResult.getId(),
+ CustomInstallationIdCache.CacheStatus.SYNCED));
+ break;
+ case HTTP_CLIENT_ERROR:
+ throw new SetCustomInstallationIdException(Status.CLIENT_ERROR);
+ case CONFLICT:
+ throw new SetCustomInstallationIdException(Status.DUPLICATED_CUSTOM_INSTALLATION_ID);
+ default:
+ throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
+ }
+
+ if (finalUpdateCacheResult) {
+ return null;
+ } else {
+ throw new SetCustomInstallationIdException(
+ "Failed to update client side cache", Status.CLIENT_ERROR);
+ }
}
/**
@@ -190,51 +197,51 @@ private Task updateCustomInstallationId(String customInstallationId) {
* return
*
*/
- private Task clearCustomInstallationId() {
- Task instanceIdResultTask = firebaseInstanceId.getInstanceId();
- Task firstUpdateCacheResultTask =
- instanceIdResultTask.onSuccessTask(
- instanceIdResult ->
- localCache.insertOrUpdateCacheEntry(
- CustomInstallationIdCacheEntryValue.create(
- "",
- instanceIdResult.getId(),
- CustomInstallationIdCache.CacheStatus.PENDING_CLEAR)));
-
- Task backendRequestResultTask =
- firstUpdateCacheResultTask.onSuccessTask(
- firstUpdateCacheResult -> {
- if (firstUpdateCacheResult) {
- String iid = instanceIdResultTask.getResult().getId();
- String iidToken = instanceIdResultTask.getResult().getToken();
- return backendServiceClient.clearCustomInstallationId(
- Utils.getProjectNumberFromAppId(firebaseApp.getOptions().getApplicationId()),
- iid,
- iidToken);
- } else {
- throw new SetCustomInstallationIdException(
- "Failed to update client side cache", Status.CLIENT_ERROR);
- }
- });
-
- Task finalUpdateCacheResultTask =
- backendRequestResultTask.onSuccessTask(
- backendRequestResult -> {
- if (backendRequestResult == Code.OK) {
- return localCache.clear();
- } else {
- throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
- }
- });
-
- return finalUpdateCacheResultTask.onSuccessTask(
- finalUpdateCacheResult -> {
- if (finalUpdateCacheResult) {
- return Tasks.forResult(null);
- } else {
- throw new SetCustomInstallationIdException(
- "Failed to update client side cache", Status.CLIENT_ERROR);
- }
- });
+ @WorkerThread
+ private Void clearCustomInstallationId() throws SetCustomInstallationIdException {
+ InstanceIdResult instanceIdResult;
+ try {
+ instanceIdResult = Tasks.await(firebaseInstanceId.getInstanceId());
+ } catch (ExecutionException | InterruptedException e) {
+ throw new SetCustomInstallationIdException(
+ "Failed to get Firebase instance id", Status.CLIENT_ERROR);
+ }
+
+ boolean firstUpdateCacheResult =
+ localCache.insertOrUpdateCacheEntry(
+ CustomInstallationIdCacheEntryValue.create(
+ "", instanceIdResult.getId(), CustomInstallationIdCache.CacheStatus.PENDING_CLEAR));
+
+ if (!firstUpdateCacheResult) {
+ throw new SetCustomInstallationIdException(
+ "Failed to update client side cache", Status.CLIENT_ERROR);
+ }
+
+ String iid = instanceIdResult.getId();
+ String iidToken = instanceIdResult.getToken();
+ Code backendRequestResult =
+ backendServiceClient.clearCustomInstallationId(
+ Utils.getProjectNumberFromAppId(firebaseApp.getOptions().getApplicationId()),
+ firebaseApp.getOptions().getApiKey(),
+ iid,
+ iidToken);
+
+ boolean finalUpdateCacheResult;
+ switch (backendRequestResult) {
+ case OK:
+ finalUpdateCacheResult = localCache.clear();
+ break;
+ case HTTP_CLIENT_ERROR:
+ throw new SetCustomInstallationIdException(Status.CLIENT_ERROR);
+ default:
+ throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
+ }
+
+ if (finalUpdateCacheResult) {
+ return null;
+ } else {
+ throw new SetCustomInstallationIdException(
+ "Failed to update client side cache", Status.CLIENT_ERROR);
+ }
}
}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java
index 7d1d5fcfaab..ca7f688d60a 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java
@@ -14,6 +14,7 @@
package com.google.firebase.segmentation;
+import androidx.annotation.NonNull;
import com.google.firebase.FirebaseApp;
import com.google.firebase.components.Component;
import com.google.firebase.components.ComponentRegistrar;
@@ -25,6 +26,7 @@
public class FirebaseSegmentationRegistrar implements ComponentRegistrar {
@Override
+ @NonNull
public List> getComponents() {
return Arrays.asList(
Component.builder(FirebaseSegmentation.class)
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
index a1346062440..307d5d49923 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
@@ -16,12 +16,9 @@
import android.content.Context;
import android.content.SharedPreferences;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.google.android.gms.tasks.Task;
-import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.firebase.FirebaseApp;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
/**
* A layer that locally caches a few Firebase Segmentation attributes on top the Segmentation
@@ -49,11 +46,10 @@ public enum CacheStatus {
private static final String INSTANCE_ID_KEY = "Iid";
private static final String CACHE_STATUS_KEY = "Status";
- private final Executor ioExecuter;
private final SharedPreferences prefs;
private final String persistenceKey;
- public CustomInstallationIdCache(FirebaseApp firebaseApp) {
+ public CustomInstallationIdCache(@NonNull FirebaseApp firebaseApp) {
// Different FirebaseApp in the same Android application should have the same application
// context and same dir path
prefs =
@@ -61,7 +57,6 @@ public CustomInstallationIdCache(FirebaseApp firebaseApp) {
.getApplicationContext()
.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
persistenceKey = firebaseApp.getPersistenceKey();
- ioExecuter = Executors.newFixedThreadPool(2);
}
@Nullable
@@ -77,31 +72,27 @@ public synchronized CustomInstallationIdCacheEntryValue readCacheEntryValue() {
return CustomInstallationIdCacheEntryValue.create(cid, iid, CacheStatus.values()[status]);
}
- public synchronized Task insertOrUpdateCacheEntry(
- CustomInstallationIdCacheEntryValue entryValue) {
+ @NonNull
+ public synchronized boolean insertOrUpdateCacheEntry(
+ @NonNull CustomInstallationIdCacheEntryValue entryValue) {
SharedPreferences.Editor editor = prefs.edit();
editor.putString(
getSharedPreferencesKey(CUSTOM_INSTALLATION_ID_KEY), entryValue.getCustomInstallationId());
editor.putString(getSharedPreferencesKey(INSTANCE_ID_KEY), entryValue.getFirebaseInstanceId());
editor.putInt(getSharedPreferencesKey(CACHE_STATUS_KEY), entryValue.getCacheStatus().ordinal());
- return commitSharedPreferencesEditAsync(editor);
+ return editor.commit();
}
- public synchronized Task clear() {
+ @NonNull
+ public synchronized boolean clear() {
SharedPreferences.Editor editor = prefs.edit();
editor.remove(getSharedPreferencesKey(CUSTOM_INSTALLATION_ID_KEY));
editor.remove(getSharedPreferencesKey(INSTANCE_ID_KEY));
editor.remove(getSharedPreferencesKey(CACHE_STATUS_KEY));
- return commitSharedPreferencesEditAsync(editor);
+ return editor.commit();
}
private String getSharedPreferencesKey(String key) {
return String.format("%s|%s", persistenceKey, key);
}
-
- private Task commitSharedPreferencesEditAsync(SharedPreferences.Editor editor) {
- TaskCompletionSource result = new TaskCompletionSource<>();
- ioExecuter.execute(() -> result.setResult(editor.commit()));
- return result.getTask();
- }
}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java
index 05528cd40f0..5e2c1944278 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java
@@ -14,6 +14,7 @@
package com.google.firebase.segmentation.local;
+import androidx.annotation.NonNull;
import com.google.auto.value.AutoValue;
import com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus;
@@ -23,14 +24,20 @@
*/
@AutoValue
public abstract class CustomInstallationIdCacheEntryValue {
+ @NonNull
public abstract String getCustomInstallationId();
+ @NonNull
public abstract String getFirebaseInstanceId();
+ @NonNull
public abstract CacheStatus getCacheStatus();
+ @NonNull
public static CustomInstallationIdCacheEntryValue create(
- String customInstallationId, String firebaseInstanceId, CacheStatus cacheStatus) {
+ @NonNull String customInstallationId,
+ @NonNull String firebaseInstanceId,
+ @NonNull CacheStatus cacheStatus) {
return new AutoValue_CustomInstallationIdCacheEntryValue(
customInstallationId, firebaseInstanceId, cacheStatus);
}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
index 59398b369d5..86b5f945205 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
@@ -14,43 +14,159 @@
package com.google.firebase.segmentation.remote;
-import com.google.android.gms.tasks.Task;
-import com.google.android.gms.tasks.Tasks;
-import com.squareup.okhttp.OkHttpClient;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
+import androidx.annotation.NonNull;
+import java.io.IOException;
+import java.net.URL;
+import java.util.zip.GZIPOutputStream;
+import javax.net.ssl.HttpsURLConnection;
+import org.json.JSONException;
+import org.json.JSONObject;
/** Http client that sends request to Firebase Segmentation backend API. To be implemented */
public class SegmentationServiceClient {
- private final OkHttpClient httpClient;
- private final Executor httpRequestExecutor;
+ private static final String FIREBASE_SEGMENTATION_API_DOMAIN =
+ "firebasesegmentation.googleapis.com";
+ private static final String UPDATE_REQUEST_RESOURCE_NAME_FORMAT =
+ "projects/%s/installations/%s/customSegmentationData";
+ private static final String CLEAR_REQUEST_RESOURCE_NAME_FORMAT =
+ "projects/%s/installations/%s/customSegmentationData:clear";
+ private static final String FIREBASE_SEGMENTATION_API_VERSION = "v1alpha";
+
+ private static final String CONTENT_TYPE_HEADER_KEY = "Content-Type";
+ private static final String JSON_CONTENT_TYPE = "application/json";
+ private static final String CONTENT_ENCODING_HEADER_KEY = "Content-Encoding";
+ private static final String GZIP_CONTENT_ENCODING = "gzip";
public enum Code {
OK,
- SERVER_INTERNAL_ERROR,
+ HTTP_CLIENT_ERROR,
+
+ CONFLICT,
+
+ NETWORK_ERROR,
+
+ SERVER_ERROR,
+
+ UNAUTHORIZED,
+ }
+
+ @NonNull
+ public Code updateCustomInstallationId(
+ long projectNumber,
+ @NonNull String apiKey,
+ @NonNull String customInstallationId,
+ @NonNull String firebaseInstanceId,
+ @NonNull String firebaseInstanceIdToken) {
+ String resourceName =
+ String.format(UPDATE_REQUEST_RESOURCE_NAME_FORMAT, projectNumber, firebaseInstanceId);
+ try {
+ URL url =
+ new URL(
+ String.format(
+ "https://%s/%s/%s?key=%s",
+ FIREBASE_SEGMENTATION_API_DOMAIN,
+ FIREBASE_SEGMENTATION_API_VERSION,
+ resourceName,
+ apiKey));
- ALREADY_EXISTS,
+ HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
+ httpsURLConnection.setDoOutput(true);
+ httpsURLConnection.setRequestMethod("PATCH");
+ httpsURLConnection.addRequestProperty(
+ "Authorization", "FIREBASE_INSTALLATIONS_AUTH " + firebaseInstanceIdToken);
+ httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
+ httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);
+ GZIPOutputStream gzipOutputStream =
+ new GZIPOutputStream(httpsURLConnection.getOutputStream());
+ try {
+ gzipOutputStream.write(
+ buildUpdateCustomSegmentationDataRequestBody(resourceName, customInstallationId)
+ .toString()
+ .getBytes("UTF-8"));
+ } catch (JSONException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ gzipOutputStream.close();
+ }
- PERMISSION_DENIED
+ int httpResponseCode = httpsURLConnection.getResponseCode();
+ switch (httpResponseCode) {
+ case 200:
+ return Code.OK;
+ case 401:
+ return Code.UNAUTHORIZED;
+ case 409:
+ return Code.CONFLICT;
+ default:
+ return Code.SERVER_ERROR;
+ }
+ } catch (IOException e) {
+ return Code.NETWORK_ERROR;
+ }
}
- public SegmentationServiceClient() {
- httpClient = new OkHttpClient();
- httpRequestExecutor = Executors.newFixedThreadPool(4);
+ private static JSONObject buildUpdateCustomSegmentationDataRequestBody(
+ String resourceName, String customInstallationId) throws JSONException {
+ JSONObject customSegmentationData = new JSONObject();
+ customSegmentationData.put("name", resourceName);
+ customSegmentationData.put("custom_installation_id", customInstallationId);
+ return customSegmentationData;
}
- public Task updateCustomInstallationId(
+ @NonNull
+ public Code clearCustomInstallationId(
long projectNumber,
- String customInstallationId,
- String firebaseInstanceId,
- String firebaseInstanceIdToken) {
- return Tasks.forResult(Code.OK);
+ @NonNull String apiKey,
+ @NonNull String firebaseInstanceId,
+ @NonNull String firebaseInstanceIdToken) {
+ String resourceName =
+ String.format(CLEAR_REQUEST_RESOURCE_NAME_FORMAT, projectNumber, firebaseInstanceId);
+ try {
+ URL url =
+ new URL(
+ String.format(
+ "https://%s/%s/%s?key=%s",
+ FIREBASE_SEGMENTATION_API_DOMAIN,
+ FIREBASE_SEGMENTATION_API_VERSION,
+ resourceName,
+ apiKey));
+
+ HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
+ httpsURLConnection.setDoOutput(true);
+ httpsURLConnection.setRequestMethod("POST");
+ httpsURLConnection.addRequestProperty(
+ "Authorization", "FIREBASE_INSTALLATIONS_AUTH " + firebaseInstanceIdToken);
+ httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
+ httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);
+ GZIPOutputStream gzipOutputStream =
+ new GZIPOutputStream(httpsURLConnection.getOutputStream());
+ try {
+ gzipOutputStream.write(
+ buildClearCustomSegmentationDataRequestBody(resourceName).toString().getBytes("UTF-8"));
+ } catch (JSONException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ gzipOutputStream.close();
+ }
+
+ int httpResponseCode = httpsURLConnection.getResponseCode();
+ switch (httpResponseCode) {
+ case 200:
+ return Code.OK;
+ case 401:
+ return Code.UNAUTHORIZED;
+ default:
+ return Code.SERVER_ERROR;
+ }
+ } catch (IOException e) {
+ return Code.NETWORK_ERROR;
+ }
}
- public Task clearCustomInstallationId(
- long projectNumber, String firebaseInstanceId, String firebaseInstanceIdToken) {
- return Tasks.forResult(Code.OK);
+ private static JSONObject buildClearCustomSegmentationDataRequestBody(String resourceName)
+ throws JSONException {
+ return new JSONObject().put("name", resourceName);
}
}
From 52d259db3804bed609b5354c7502eddc68133dad Mon Sep 17 00:00:00 2001
From: Di Wu <49409954+diwu-arete@users.noreply.github.com>
Date: Tue, 6 Aug 2019 11:16:50 -0700
Subject: [PATCH 07/17] FirebaseSegmentation SDK changes (#673)
* Implement Firebase segmentation SDK device local cache
* [Firebase Segmentation] Add custom installation id cache layer and tests for it.
* Add test for updating cache
* Switch to use SQLiteOpenHelper
* Switch to use SharedPreferences from SQLite.
* Change the cache class to be singleton
* Wrap shared pref commit in a async task.
* Address comments
* Google format fix
* Replace some deprecated code.
* Package refactor
* nit
* nit
* Add the state machine of updating custom installation id in the local
cache and update to Firebase Segmentation backend. CL also contains unit
tests.
(The http client is not implemented yet.)
* minor format fix
* Address comments #1
* Http client in Firebase Segmentation SDK to call backend service.
* Revert unintentional change
* Fix connected device test
* Fix connected device test
* 1. Add a few annotations to make java code Kotlin friendly
2. Some fixes for the http request format
* Fix java format
* Fix API version
* Change the segmentation API implementation to synchronous and put the
entire synchronous code block in async task.
* Fix a async getResult race issue.
* OkHttpClient -> HttpsUrlConnection
* Use gzip for compressing content and fix ourput stream memory leak risk.
* Addressed a few comments
* FirebaseSegmentation SDK
1. Clean up http client response code.
2. When updateCustomInstallationId is called, on non-retryable server errors, the SDK should clean up the local cache. Instead, for retryable errors, SDK can keep the local cache for retrying update later.
---
.../FirebaseSegmentationInstrumentedTest.java | 28 +++++++++++++-
.../segmentation/FirebaseSegmentation.java | 37 ++++++++++++++-----
.../SetCustomInstallationIdException.java | 4 +-
.../remote/SegmentationServiceClient.java | 14 +++++--
4 files changed, 66 insertions(+), 17 deletions(-)
diff --git a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java b/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java
index 19498782a78..034ee1eb339 100644
--- a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java
+++ b/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java
@@ -127,7 +127,8 @@ public void testUpdateCustomInstallationId_CacheOk_BackendOk() throws Exception
}
@Test
- public void testUpdateCustomInstallationId_CacheOk_BackendError() throws InterruptedException {
+ public void testUpdateCustomInstallationId_CacheOk_BackendError_Retryable()
+ throws InterruptedException {
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsError);
@@ -150,6 +151,31 @@ public void testUpdateCustomInstallationId_CacheOk_BackendError() throws Interru
.isEqualTo(CustomInstallationIdCache.CacheStatus.PENDING_UPDATE);
}
+ @Test
+ public void testUpdateCustomInstallationId_CacheOk_BackendError_NotRetryable()
+ throws InterruptedException {
+ when(backendClientReturnsError.updateCustomInstallationId(
+ anyLong(), anyString(), anyString(), anyString(), anyString()))
+ .thenReturn(SegmentationServiceClient.Code.CONFLICT);
+ FirebaseSegmentation firebaseSegmentation =
+ new FirebaseSegmentation(
+ firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsError);
+
+ // Expect exception
+ try {
+ Tasks.await(firebaseSegmentation.setCustomInstallationId(CUSTOM_INSTALLATION_ID));
+ fail();
+ } catch (ExecutionException expected) {
+ Throwable cause = expected.getCause();
+ assertThat(cause).isInstanceOf(SetCustomInstallationIdException.class);
+ assertThat(((SetCustomInstallationIdException) cause).getStatus())
+ .isEqualTo(SetCustomInstallationIdException.Status.DUPLICATED_CUSTOM_INSTALLATION_ID);
+ }
+
+ CustomInstallationIdCacheEntryValue entryValue = actualCache.readCacheEntryValue();
+ assertThat(entryValue).isNull();
+ }
+
@Test
public void testUpdateCustomInstallationId_CacheError_BackendOk() throws InterruptedException {
FirebaseSegmentation firebaseSegmentation =
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
index 2008ea09359..85adf2fb708 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
@@ -128,7 +128,7 @@ private Void updateCustomInstallationId(String customInstallationId)
instanceIdResult = Tasks.await(firebaseInstanceId.getInstanceId());
} catch (ExecutionException | InterruptedException e) {
throw new SetCustomInstallationIdException(
- "Failed to get Firebase instance id", Status.CLIENT_ERROR);
+ Status.CLIENT_ERROR, "Failed to get Firebase instance id");
}
boolean firstUpdateCacheResult =
@@ -140,7 +140,7 @@ private Void updateCustomInstallationId(String customInstallationId)
if (!firstUpdateCacheResult) {
throw new SetCustomInstallationIdException(
- "Failed to update client side cache", Status.CLIENT_ERROR);
+ Status.CLIENT_ERROR, "Failed to update client side cache");
}
// Start requesting backend when first cache updae is done.
@@ -164,11 +164,22 @@ private Void updateCustomInstallationId(String customInstallationId)
instanceIdResult.getId(),
CustomInstallationIdCache.CacheStatus.SYNCED));
break;
- case HTTP_CLIENT_ERROR:
- throw new SetCustomInstallationIdException(Status.CLIENT_ERROR);
+ case UNAUTHORIZED:
+ localCache.clear();
+ throw new SetCustomInstallationIdException(
+ Status.CLIENT_ERROR, "Instance id token is invalid.");
case CONFLICT:
- throw new SetCustomInstallationIdException(Status.DUPLICATED_CUSTOM_INSTALLATION_ID);
+ localCache.clear();
+ throw new SetCustomInstallationIdException(
+ Status.DUPLICATED_CUSTOM_INSTALLATION_ID,
+ "The custom installation id is used by another Firebase installation in your project.");
+ case HTTP_CLIENT_ERROR:
+ localCache.clear();
+ throw new SetCustomInstallationIdException(Status.CLIENT_ERROR, "Http client error(4xx)");
+ case NETWORK_ERROR:
+ case SERVER_ERROR:
default:
+ // These are considered retryable errors, so not to clean up the cache.
throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
}
@@ -176,7 +187,7 @@ private Void updateCustomInstallationId(String customInstallationId)
return null;
} else {
throw new SetCustomInstallationIdException(
- "Failed to update client side cache", Status.CLIENT_ERROR);
+ Status.CLIENT_ERROR, "Failed to update client side cache");
}
}
@@ -204,7 +215,7 @@ private Void clearCustomInstallationId() throws SetCustomInstallationIdException
instanceIdResult = Tasks.await(firebaseInstanceId.getInstanceId());
} catch (ExecutionException | InterruptedException e) {
throw new SetCustomInstallationIdException(
- "Failed to get Firebase instance id", Status.CLIENT_ERROR);
+ Status.CLIENT_ERROR, "Failed to get Firebase instance id");
}
boolean firstUpdateCacheResult =
@@ -214,7 +225,7 @@ private Void clearCustomInstallationId() throws SetCustomInstallationIdException
if (!firstUpdateCacheResult) {
throw new SetCustomInstallationIdException(
- "Failed to update client side cache", Status.CLIENT_ERROR);
+ Status.CLIENT_ERROR, "Failed to update client side cache");
}
String iid = instanceIdResult.getId();
@@ -231,9 +242,15 @@ private Void clearCustomInstallationId() throws SetCustomInstallationIdException
case OK:
finalUpdateCacheResult = localCache.clear();
break;
+ case UNAUTHORIZED:
+ throw new SetCustomInstallationIdException(
+ Status.CLIENT_ERROR, "Instance id token is invalid.");
case HTTP_CLIENT_ERROR:
- throw new SetCustomInstallationIdException(Status.CLIENT_ERROR);
+ throw new SetCustomInstallationIdException(Status.CLIENT_ERROR, "Http client error(4xx)");
+ case NETWORK_ERROR:
+ case SERVER_ERROR:
default:
+ // These are considered retryable errors, so not to clean up the cache.
throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
}
@@ -241,7 +258,7 @@ private Void clearCustomInstallationId() throws SetCustomInstallationIdException
return null;
} else {
throw new SetCustomInstallationIdException(
- "Failed to update client side cache", Status.CLIENT_ERROR);
+ Status.CLIENT_ERROR, "Failed to update client side cache");
}
}
}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java
index 3c957ce3294..2291c078a03 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java
@@ -45,13 +45,13 @@ public enum Status {
this.status = status;
}
- SetCustomInstallationIdException(@NonNull String message, @NonNull Status status) {
+ SetCustomInstallationIdException(@NonNull Status status, @NonNull String message) {
super(message);
this.status = status;
}
SetCustomInstallationIdException(
- @NonNull String message, @NonNull Status status, Throwable cause) {
+ @NonNull Status status, @NonNull String message, Throwable cause) {
super(message, cause);
this.status = status;
}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
index 86b5f945205..6d0436d8b24 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
@@ -41,15 +41,15 @@ public class SegmentationServiceClient {
public enum Code {
OK,
- HTTP_CLIENT_ERROR,
-
CONFLICT,
+ UNAUTHORIZED,
+
NETWORK_ERROR,
- SERVER_ERROR,
+ HTTP_CLIENT_ERROR,
- UNAUTHORIZED,
+ SERVER_ERROR,
}
@NonNull
@@ -100,6 +100,9 @@ public Code updateCustomInstallationId(
case 409:
return Code.CONFLICT;
default:
+ if (httpResponseCode / 100 == 4) {
+ return Code.HTTP_CLIENT_ERROR;
+ }
return Code.SERVER_ERROR;
}
} catch (IOException e) {
@@ -158,6 +161,9 @@ public Code clearCustomInstallationId(
case 401:
return Code.UNAUTHORIZED;
default:
+ if (httpResponseCode / 100 == 4) {
+ return Code.HTTP_CLIENT_ERROR;
+ }
return Code.SERVER_ERROR;
}
} catch (IOException e) {
From 33665b2d47d00614e025d20860dcae68a7ce1b2f Mon Sep 17 00:00:00 2001
From: Di Wu <49409954+diwu-arete@users.noreply.github.com>
Date: Tue, 6 Aug 2019 11:57:00 -0700
Subject: [PATCH 08/17] Restrict Firebase API key to Android app package name.
(#690)
* Implement Firebase segmentation SDK device local cache
* [Firebase Segmentation] Add custom installation id cache layer and tests for it.
* Add test for updating cache
* Switch to use SQLiteOpenHelper
* Switch to use SharedPreferences from SQLite.
* Change the cache class to be singleton
* Wrap shared pref commit in a async task.
* Address comments
* Google format fix
* Replace some deprecated code.
* Package refactor
* nit
* nit
* Add the state machine of updating custom installation id in the local
cache and update to Firebase Segmentation backend. CL also contains unit
tests.
(The http client is not implemented yet.)
* minor format fix
* Address comments #1
* Http client in Firebase Segmentation SDK to call backend service.
* Revert unintentional change
* Fix connected device test
* Fix connected device test
* 1. Add a few annotations to make java code Kotlin friendly
2. Some fixes for the http request format
* Fix java format
* Fix API version
* Change the segmentation API implementation to synchronous and put the
entire synchronous code block in async task.
* Fix a async getResult race issue.
* OkHttpClient -> HttpsUrlConnection
* Use gzip for compressing content and fix ourput stream memory leak risk.
* Addressed a few comments
* FirebaseSegmentation SDK
1. Clean up http client response code.
2. When updateCustomInstallationId is called, on non-retryable server errors, the SDK should clean up the local cache. Instead, for retryable errors, SDK can keep the local cache for retrying update later.
* Restrict Firebase API key to Android app package name.
---
.../segmentation/FirebaseSegmentation.java | 4 +-
.../remote/SegmentationServiceClient.java | 41 +++++++++++++++++++
2 files changed, 44 insertions(+), 1 deletion(-)
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
index 85adf2fb708..b1d5e6e6742 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
@@ -35,6 +35,8 @@
/** Entry point of Firebase Segmentation SDK. */
public class FirebaseSegmentation {
+ public static final String TAG = "FirebaseSegmentation";
+
private final FirebaseApp firebaseApp;
private final FirebaseInstanceId firebaseInstanceId;
private final CustomInstallationIdCache localCache;
@@ -46,7 +48,7 @@ public class FirebaseSegmentation {
firebaseApp,
FirebaseInstanceId.getInstance(firebaseApp),
new CustomInstallationIdCache(firebaseApp),
- new SegmentationServiceClient());
+ new SegmentationServiceClient(firebaseApp.getApplicationContext()));
}
FirebaseSegmentation(
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
index 6d0436d8b24..65058bb97da 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
@@ -14,7 +14,14 @@
package com.google.firebase.segmentation.remote;
+import static com.google.firebase.segmentation.FirebaseSegmentation.TAG;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.Log;
import androidx.annotation.NonNull;
+import com.google.android.gms.common.util.AndroidUtilsLight;
+import com.google.android.gms.common.util.Hex;
import java.io.IOException;
import java.net.URL;
import java.util.zip.GZIPOutputStream;
@@ -37,6 +44,14 @@ public class SegmentationServiceClient {
private static final String JSON_CONTENT_TYPE = "application/json";
private static final String CONTENT_ENCODING_HEADER_KEY = "Content-Encoding";
private static final String GZIP_CONTENT_ENCODING = "gzip";
+ private static final String X_ANDROID_PACKAGE_HEADER_KEY = "X-Android-Package";
+ private static final String X_ANDROID_CERT_HEADER_KEY = "X-Android-Cert";
+
+ private final Context context;
+
+ public SegmentationServiceClient(@NonNull Context context) {
+ this.context = context;
+ }
public enum Code {
OK,
@@ -78,6 +93,9 @@ public Code updateCustomInstallationId(
"Authorization", "FIREBASE_INSTALLATIONS_AUTH " + firebaseInstanceIdToken);
httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);
+ httpsURLConnection.addRequestProperty(X_ANDROID_PACKAGE_HEADER_KEY, context.getPackageName());
+ httpsURLConnection.addRequestProperty(
+ X_ANDROID_CERT_HEADER_KEY, getFingerprintHashForPackage());
GZIPOutputStream gzipOutputStream =
new GZIPOutputStream(httpsURLConnection.getOutputStream());
try {
@@ -143,6 +161,9 @@ public Code clearCustomInstallationId(
"Authorization", "FIREBASE_INSTALLATIONS_AUTH " + firebaseInstanceIdToken);
httpsURLConnection.addRequestProperty(CONTENT_TYPE_HEADER_KEY, JSON_CONTENT_TYPE);
httpsURLConnection.addRequestProperty(CONTENT_ENCODING_HEADER_KEY, GZIP_CONTENT_ENCODING);
+ httpsURLConnection.addRequestProperty(X_ANDROID_PACKAGE_HEADER_KEY, context.getPackageName());
+ httpsURLConnection.addRequestProperty(
+ X_ANDROID_CERT_HEADER_KEY, getFingerprintHashForPackage());
GZIPOutputStream gzipOutputStream =
new GZIPOutputStream(httpsURLConnection.getOutputStream());
try {
@@ -175,4 +196,24 @@ private static JSONObject buildClearCustomSegmentationDataRequestBody(String res
throws JSONException {
return new JSONObject().put("name", resourceName);
}
+
+ /** Gets the Android package's SHA-1 fingerprint. */
+ private String getFingerprintHashForPackage() {
+ byte[] hash;
+
+ try {
+ hash = AndroidUtilsLight.getPackageCertificateHashBytes(context, context.getPackageName());
+
+ if (hash == null) {
+ Log.e(TAG, "Could not get fingerprint hash for package: " + context.getPackageName());
+ return null;
+ } else {
+ String cert = Hex.bytesToStringUppercase(hash, /* zeroTerminated= */ false);
+ return Hex.bytesToStringUppercase(hash, /* zeroTerminated= */ false);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "No such package: " + context.getPackageName(), e);
+ return null;
+ }
+ }
}
From f4ba63c6ae1988d43445b4fe21654890e5f210a2 Mon Sep 17 00:00:00 2001
From: Di Wu <49409954+diwu-arete@users.noreply.github.com>
Date: Tue, 6 Aug 2019 13:43:47 -0700
Subject: [PATCH 09/17] Arete floc (#691)
* Implement Firebase segmentation SDK device local cache
* [Firebase Segmentation] Add custom installation id cache layer and tests for it.
* Add test for updating cache
* Switch to use SQLiteOpenHelper
* Switch to use SharedPreferences from SQLite.
* Change the cache class to be singleton
* Wrap shared pref commit in a async task.
* Address comments
* Google format fix
* Replace some deprecated code.
* Package refactor
* nit
* nit
* Add the state machine of updating custom installation id in the local
cache and update to Firebase Segmentation backend. CL also contains unit
tests.
(The http client is not implemented yet.)
* minor format fix
* Address comments #1
* Http client in Firebase Segmentation SDK to call backend service.
* Revert unintentional change
* Fix connected device test
* Fix connected device test
* 1. Add a few annotations to make java code Kotlin friendly
2. Some fixes for the http request format
* Fix java format
* Fix API version
* Change the segmentation API implementation to synchronous and put the
entire synchronous code block in async task.
* Fix a async getResult race issue.
* OkHttpClient -> HttpsUrlConnection
* Use gzip for compressing content and fix ourput stream memory leak risk.
* Addressed a few comments
* FirebaseSegmentation SDK
1. Clean up http client response code.
2. When updateCustomInstallationId is called, on non-retryable server errors, the SDK should clean up the local cache. Instead, for retryable errors, SDK can keep the local cache for retrying update later.
* Restrict Firebase API key to Android app package name.
* Explicitly add internet permission
---
firebase-segmentation/src/main/AndroidManifest.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/firebase-segmentation/src/main/AndroidManifest.xml b/firebase-segmentation/src/main/AndroidManifest.xml
index 1502b31485f..9005c369f32 100644
--- a/firebase-segmentation/src/main/AndroidManifest.xml
+++ b/firebase-segmentation/src/main/AndroidManifest.xml
@@ -16,6 +16,7 @@
+
Date: Tue, 10 Dec 2019 11:26:26 -0800
Subject: [PATCH 10/17] Disable registrar test for FirebaseSegmentation.
(#1050)
* Implement Firebase segmentation SDK device local cache
* [Firebase Segmentation] Add custom installation id cache layer and tests for it.
* Add test for updating cache
* Switch to use SQLiteOpenHelper
* Switch to use SharedPreferences from SQLite.
* Change the cache class to be singleton
* Wrap shared pref commit in a async task.
* Address comments
* Google format fix
* Replace some deprecated code.
* Package refactor
* nit
* nit
* Add the state machine of updating custom installation id in the local
cache and update to Firebase Segmentation backend. CL also contains unit
tests.
(The http client is not implemented yet.)
* minor format fix
* Address comments #1
* Http client in Firebase Segmentation SDK to call backend service.
* Revert unintentional change
* Fix connected device test
* Fix connected device test
* 1. Add a few annotations to make java code Kotlin friendly
2. Some fixes for the http request format
* Fix java format
* Fix API version
* Change the segmentation API implementation to synchronous and put the
entire synchronous code block in async task.
* Fix a async getResult race issue.
* OkHttpClient -> HttpsUrlConnection
* Use gzip for compressing content and fix ourput stream memory leak risk.
* Addressed a few comments
* FirebaseSegmentation SDK
1. Clean up http client response code.
2. When updateCustomInstallationId is called, on non-retryable server errors, the SDK should clean up the local cache. Instead, for retryable errors, SDK can keep the local cache for retrying update later.
* Restrict Firebase API key to Android app package name.
* Explicitly add internet permission
* Disable registrar test for FirebaseSegmentation.
---
.../segmentation/FirebaseSegmentationRegistrarTest.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrarTest.java b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrarTest.java
index 56b0d120eb0..b45fd92b12c 100644
--- a/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrarTest.java
+++ b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrarTest.java
@@ -20,6 +20,7 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -32,6 +33,8 @@ public void setUp() {
FirebaseApp.clearInstancesForTest();
}
+ // TODO(rgowman:b/123870630): Enable test.
+ @Ignore
@Test
public void getFirebaseInstallationsInstance() {
FirebaseApp defaultApp =
From fd0a4faf9085a0f0d5fc598a360b9c433b0c91a1 Mon Sep 17 00:00:00 2001
From: Di Wu <49409954+diwu-arete@users.noreply.github.com>
Date: Fri, 13 Dec 2019 16:16:06 -0800
Subject: [PATCH 11/17] Replace the custom installation id cache SharedPref
implementation by file implementation. (#1056)
* Implement Firebase segmentation SDK device local cache
* [Firebase Segmentation] Add custom installation id cache layer and tests for it.
* Add test for updating cache
* Switch to use SQLiteOpenHelper
* Switch to use SharedPreferences from SQLite.
* Change the cache class to be singleton
* Wrap shared pref commit in a async task.
* Address comments
* Google format fix
* Replace some deprecated code.
* Package refactor
* nit
* nit
* Add the state machine of updating custom installation id in the local
cache and update to Firebase Segmentation backend. CL also contains unit
tests.
(The http client is not implemented yet.)
* minor format fix
* Address comments #1
* Http client in Firebase Segmentation SDK to call backend service.
* Revert unintentional change
* Fix connected device test
* Fix connected device test
* 1. Add a few annotations to make java code Kotlin friendly
2. Some fixes for the http request format
* Fix java format
* Fix API version
* Change the segmentation API implementation to synchronous and put the
entire synchronous code block in async task.
* Fix a async getResult race issue.
* OkHttpClient -> HttpsUrlConnection
* Use gzip for compressing content and fix ourput stream memory leak risk.
* Addressed a few comments
* FirebaseSegmentation SDK
1. Clean up http client response code.
2. When updateCustomInstallationId is called, on non-retryable server errors, the SDK should clean up the local cache. Instead, for retryable errors, SDK can keep the local cache for retrying update later.
* Restrict Firebase API key to Android app package name.
* Explicitly add internet permission
* Disable registrar test for FirebaseSegmentation.
* Disable test lab
* Add api info for segmentation API
* [FLoC] Replace the custom installation id cache SharedPref
implementation by file implementation.
---
firebase-segmentation/api.txt | 72 +++++++++++++
.../firebase-segmentation.gradle | 8 +-
.../local/CustomInstallationIdCache.java | 102 ++++++++++++------
3 files changed, 145 insertions(+), 37 deletions(-)
create mode 100644 firebase-segmentation/api.txt
diff --git a/firebase-segmentation/api.txt b/firebase-segmentation/api.txt
new file mode 100644
index 00000000000..e1469393eaf
--- /dev/null
+++ b/firebase-segmentation/api.txt
@@ -0,0 +1,72 @@
+// Signature format: 2.0
+package com.google.firebase.segmentation {
+
+ public class FirebaseSegmentation {
+ method @NonNull public static com.google.firebase.segmentation.FirebaseSegmentation getInstance();
+ method @NonNull public static com.google.firebase.segmentation.FirebaseSegmentation getInstance(@NonNull com.google.firebase.FirebaseApp);
+ method @NonNull public com.google.android.gms.tasks.Task setCustomInstallationId(@Nullable String);
+ field public static final String TAG = "FirebaseSegmentation";
+ }
+
+ public class FirebaseSegmentationRegistrar implements com.google.firebase.components.ComponentRegistrar {
+ ctor public FirebaseSegmentationRegistrar();
+ method @NonNull public java.util.List> getComponents();
+ }
+
+ public class SetCustomInstallationIdException extends com.google.firebase.FirebaseException {
+ method @NonNull public com.google.firebase.segmentation.SetCustomInstallationIdException.Status getStatus();
+ }
+
+ public enum SetCustomInstallationIdException.Status {
+ enum_constant public static final com.google.firebase.segmentation.SetCustomInstallationIdException.Status BACKEND_ERROR;
+ enum_constant public static final com.google.firebase.segmentation.SetCustomInstallationIdException.Status CLIENT_ERROR;
+ enum_constant public static final com.google.firebase.segmentation.SetCustomInstallationIdException.Status DUPLICATED_CUSTOM_INSTALLATION_ID;
+ enum_constant public static final com.google.firebase.segmentation.SetCustomInstallationIdException.Status UNKOWN;
+ }
+
+}
+
+package com.google.firebase.segmentation.local {
+
+ public class CustomInstallationIdCache {
+ ctor public CustomInstallationIdCache(@NonNull com.google.firebase.FirebaseApp);
+ method @NonNull public boolean clear();
+ method @NonNull public boolean insertOrUpdateCacheEntry(@NonNull com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue);
+ method @Nullable public com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue readCacheEntryValue();
+ }
+
+ public enum CustomInstallationIdCache.CacheStatus {
+ enum_constant public static final com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus PENDING_CLEAR;
+ enum_constant public static final com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus PENDING_UPDATE;
+ enum_constant public static final com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus SYNCED;
+ }
+
+ public abstract class CustomInstallationIdCacheEntryValue {
+ ctor public CustomInstallationIdCacheEntryValue();
+ method @NonNull public static com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue create(@NonNull String, @NonNull String, @NonNull com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus);
+ method @NonNull public abstract com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus getCacheStatus();
+ method @NonNull public abstract String getCustomInstallationId();
+ method @NonNull public abstract String getFirebaseInstanceId();
+ }
+
+}
+
+package com.google.firebase.segmentation.remote {
+
+ public class SegmentationServiceClient {
+ ctor public SegmentationServiceClient(@NonNull android.content.Context);
+ method @NonNull public com.google.firebase.segmentation.remote.SegmentationServiceClient.Code clearCustomInstallationId(long, @NonNull String, @NonNull String, @NonNull String);
+ method @NonNull public com.google.firebase.segmentation.remote.SegmentationServiceClient.Code updateCustomInstallationId(long, @NonNull String, @NonNull String, @NonNull String, @NonNull String);
+ }
+
+ public enum SegmentationServiceClient.Code {
+ enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code CONFLICT;
+ enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code HTTP_CLIENT_ERROR;
+ enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code NETWORK_ERROR;
+ enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code OK;
+ enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code SERVER_ERROR;
+ enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code UNAUTHORIZED;
+ }
+
+}
+
diff --git a/firebase-segmentation/firebase-segmentation.gradle b/firebase-segmentation/firebase-segmentation.gradle
index c11085bd161..c07201f2a77 100644
--- a/firebase-segmentation/firebase-segmentation.gradle
+++ b/firebase-segmentation/firebase-segmentation.gradle
@@ -16,10 +16,6 @@ plugins {
id 'firebase-library'
}
-firebaseLibrary {
- testLab.enabled = true
-}
-
android {
compileSdkVersion project.targetSdkVersion
@@ -48,7 +44,7 @@ dependencies {
exclude group: "com.google.firebase", module: "firebase-common"
}
- implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.google.android.gms:play-services-tasks:17.0.0'
@@ -59,7 +55,7 @@ dependencies {
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:$robolectricVersion"
- androidTestImplementation "androidx.annotation:annotation:1.0.0"
+ androidTestImplementation "androidx.annotation:annotation:1.1.0"
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test:runner:1.2.0'
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
index 307d5d49923..eb77445bf70 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
@@ -14,11 +14,16 @@
package com.google.firebase.segmentation.local;
-import android.content.Context;
-import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.firebase.FirebaseApp;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import org.json.JSONException;
+import org.json.JSONObject;
/**
* A layer that locally caches a few Firebase Segmentation attributes on top the Segmentation
@@ -40,59 +45,94 @@ public enum CacheStatus {
PENDING_CLEAR
}
- private static final String SHARED_PREFS_NAME = "CustomInstallationIdCache";
+ private static final String DATA_FILE_NAME_PREFIX = "PersistedCustomInstallationId";
private static final String CUSTOM_INSTALLATION_ID_KEY = "Cid";
private static final String INSTANCE_ID_KEY = "Iid";
private static final String CACHE_STATUS_KEY = "Status";
- private final SharedPreferences prefs;
- private final String persistenceKey;
+ private final File dataFile;
+ private final FirebaseApp firebaseApp;
public CustomInstallationIdCache(@NonNull FirebaseApp firebaseApp) {
- // Different FirebaseApp in the same Android application should have the same application
- // context and same dir path
- prefs =
- firebaseApp
- .getApplicationContext()
- .getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
- persistenceKey = firebaseApp.getPersistenceKey();
+ this.firebaseApp = firebaseApp;
+ // Store custom installation id in different file for different FirebaseApp.
+ dataFile =
+ new File(
+ firebaseApp.getApplicationContext().getFilesDir(),
+ String.format("%s.%s.json", DATA_FILE_NAME_PREFIX, firebaseApp.getPersistenceKey()));
}
@Nullable
public synchronized CustomInstallationIdCacheEntryValue readCacheEntryValue() {
- String cid = prefs.getString(getSharedPreferencesKey(CUSTOM_INSTALLATION_ID_KEY), null);
- String iid = prefs.getString(getSharedPreferencesKey(INSTANCE_ID_KEY), null);
- int status = prefs.getInt(getSharedPreferencesKey(CACHE_STATUS_KEY), -1);
+ JSONObject cidInfo = readCidInfoFromFile();
+ String cid = cidInfo.optString(CUSTOM_INSTALLATION_ID_KEY, null);
+ String iid = cidInfo.optString(INSTANCE_ID_KEY, null);
+ int status = cidInfo.optInt(CACHE_STATUS_KEY, -1);
if (cid == null || iid == null || status == -1) {
return null;
}
-
return CustomInstallationIdCacheEntryValue.create(cid, iid, CacheStatus.values()[status]);
}
+ private JSONObject readCidInfoFromFile() {
+ final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ final byte[] tmpBuf = new byte[16 * 1024];
+ try (FileInputStream fis = new FileInputStream(dataFile)) {
+ while (true) {
+ int numRead = fis.read(tmpBuf, 0, tmpBuf.length);
+ if (numRead < 0) {
+ break;
+ }
+ baos.write(tmpBuf, 0, numRead);
+ }
+ return new JSONObject(baos.toString());
+ } catch (IOException | JSONException e) {
+ return new JSONObject();
+ }
+ }
+
+ /**
+ * Write the prefs to a JSON object, serialize them into a JSON string and write the bytes to a
+ * temp file. After writing and closing the temp file, rename it over to the actual
+ * DATA_FILE_NAME.
+ */
@NonNull
public synchronized boolean insertOrUpdateCacheEntry(
@NonNull CustomInstallationIdCacheEntryValue entryValue) {
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(
- getSharedPreferencesKey(CUSTOM_INSTALLATION_ID_KEY), entryValue.getCustomInstallationId());
- editor.putString(getSharedPreferencesKey(INSTANCE_ID_KEY), entryValue.getFirebaseInstanceId());
- editor.putInt(getSharedPreferencesKey(CACHE_STATUS_KEY), entryValue.getCacheStatus().ordinal());
- return editor.commit();
+ try {
+ // Write the prefs into a JSON object
+ JSONObject json = new JSONObject();
+ json.put(CUSTOM_INSTALLATION_ID_KEY, entryValue.getCustomInstallationId());
+ json.put(INSTANCE_ID_KEY, entryValue.getFirebaseInstanceId());
+ json.put(CACHE_STATUS_KEY, entryValue.getCacheStatus().ordinal());
+ File tmpFile =
+ File.createTempFile(
+ String.format("%s.%s", DATA_FILE_NAME_PREFIX, firebaseApp.getPersistenceKey()),
+ "tmp",
+ firebaseApp.getApplicationContext().getFilesDir());
+
+ // Serialize the JSON object into a string and write the bytes to a temp file
+ FileOutputStream fos = new FileOutputStream(tmpFile);
+ fos.write(json.toString().getBytes("UTF-8"));
+ fos.close();
+
+ // Snapshot the temp file to the actual file
+ if (!tmpFile.renameTo(dataFile)) {
+ throw new IOException("unable to rename the tmpfile to " + dataFile.getPath());
+ }
+ } catch (JSONException | IOException e) {
+ return false;
+ }
+ return true;
}
@NonNull
public synchronized boolean clear() {
- SharedPreferences.Editor editor = prefs.edit();
- editor.remove(getSharedPreferencesKey(CUSTOM_INSTALLATION_ID_KEY));
- editor.remove(getSharedPreferencesKey(INSTANCE_ID_KEY));
- editor.remove(getSharedPreferencesKey(CACHE_STATUS_KEY));
- return editor.commit();
- }
-
- private String getSharedPreferencesKey(String key) {
- return String.format("%s|%s", persistenceKey, key);
+ if (!dataFile.exists()) {
+ return true;
+ }
+ return dataFile.delete();
}
}
From 6e37fb99155f6f844281859edbd786989d7549b5 Mon Sep 17 00:00:00 2001
From: Di Wu <49409954+diwu-arete@users.noreply.github.com>
Date: Fri, 13 Dec 2019 23:10:22 -0800
Subject: [PATCH 12/17] [FLoC] Change FLoC instrumental tests to be unit tests.
(#1058)
* [FLoC] Change FLoC instrumental tests to be unit tests.
---
.../firebase-segmentation.gradle | 12 +---
.../src/androidTest/AndroidManifest.xml | 26 -------
.../FirebaseSegmentationTest.java} | 65 ++++++++++++-----
.../segmentation/TestOnCompleteListener.java | 70 +++++++++++++++++++
.../local/CustomInstallationIdCacheTest.java | 4 +-
5 files changed, 123 insertions(+), 54 deletions(-)
delete mode 100644 firebase-segmentation/src/androidTest/AndroidManifest.xml
rename firebase-segmentation/src/{androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java => test/java/com/google/firebase/segmentation/FirebaseSegmentationTest.java} (80%)
create mode 100644 firebase-segmentation/src/test/java/com/google/firebase/segmentation/TestOnCompleteListener.java
rename firebase-segmentation/src/{androidTest => test}/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java (97%)
diff --git a/firebase-segmentation/firebase-segmentation.gradle b/firebase-segmentation/firebase-segmentation.gradle
index c07201f2a77..14b4deb8018 100644
--- a/firebase-segmentation/firebase-segmentation.gradle
+++ b/firebase-segmentation/firebase-segmentation.gradle
@@ -54,13 +54,7 @@ dependencies {
testImplementation 'androidx.test:core:1.2.0'
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:$robolectricVersion"
-
- androidTestImplementation "androidx.annotation:annotation:1.1.0"
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test:rules:1.2.0'
- androidTestImplementation 'androidx.test:runner:1.2.0'
- androidTestImplementation "com.google.truth:truth:$googleTruthVersion"
- androidTestImplementation 'junit:junit:4.12'
- androidTestImplementation 'org.mockito:mockito-core:2.25.0'
- androidTestImplementation 'org.mockito:mockito-android:2.25.0'
+ testImplementation "com.google.truth:truth:$googleTruthVersion"
+ testImplementation 'org.mockito:mockito-core:2.25.0'
+ testImplementation 'org.mockito:mockito-inline:2.25.0'
}
diff --git a/firebase-segmentation/src/androidTest/AndroidManifest.xml b/firebase-segmentation/src/androidTest/AndroidManifest.xml
deleted file mode 100644
index f3ec53d62a2..00000000000
--- a/firebase-segmentation/src/androidTest/AndroidManifest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationTest.java
similarity index 80%
rename from firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java
rename to firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationTest.java
index 034ee1eb339..701bfcab06f 100644
--- a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/FirebaseSegmentationInstrumentedTest.java
+++ b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationTest.java
@@ -17,14 +17,13 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.any;
import static org.mockito.Mockito.when;
import androidx.annotation.NonNull;
import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
@@ -34,6 +33,10 @@
import com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue;
import com.google.firebase.segmentation.remote.SegmentationServiceClient;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
@@ -42,16 +45,11 @@
import org.junit.runners.MethodSorters;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class FirebaseSegmentationInstrumentedTest {
-
+public class FirebaseSegmentationTest {
private static final String CUSTOM_INSTALLATION_ID = "123";
private static final String FIREBASE_INSTANCE_ID = "cAAAAAAAAAA";
@@ -59,9 +57,12 @@ public class FirebaseSegmentationInstrumentedTest {
@Mock private FirebaseInstanceId firebaseInstanceId;
@Mock private SegmentationServiceClient backendClientReturnsOk;
@Mock private SegmentationServiceClient backendClientReturnsError;
+
private CustomInstallationIdCache actualCache;
@Mock private CustomInstallationIdCache cacheReturnsError;
+ private ExecutorService taskExecutor;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -105,6 +106,8 @@ public String getToken() {
}));
when(cacheReturnsError.insertOrUpdateCacheEntry(any())).thenReturn(false);
when(cacheReturnsError.readCacheEntryValue()).thenReturn(null);
+
+ taskExecutor = new ThreadPoolExecutor(0, 1, 30L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
}
@After
@@ -119,7 +122,11 @@ public void testUpdateCustomInstallationId_CacheOk_BackendOk() throws Exception
firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsOk);
// No exception, means success.
- assertNull(Tasks.await(firebaseSegmentation.setCustomInstallationId(CUSTOM_INSTALLATION_ID)));
+ TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
+ firebaseSegmentation
+ .setCustomInstallationId(CUSTOM_INSTALLATION_ID)
+ .addOnCompleteListener(taskExecutor, onCompleteListener);
+ assertNull(onCompleteListener.await());
CustomInstallationIdCacheEntryValue entryValue = actualCache.readCacheEntryValue();
assertThat(entryValue.getCustomInstallationId()).isEqualTo(CUSTOM_INSTALLATION_ID);
assertThat(entryValue.getFirebaseInstanceId()).isEqualTo(FIREBASE_INSTANCE_ID);
@@ -135,7 +142,11 @@ public void testUpdateCustomInstallationId_CacheOk_BackendError_Retryable()
// Expect exception
try {
- Tasks.await(firebaseSegmentation.setCustomInstallationId(CUSTOM_INSTALLATION_ID));
+ TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
+ firebaseSegmentation
+ .setCustomInstallationId(CUSTOM_INSTALLATION_ID)
+ .addOnCompleteListener(taskExecutor, onCompleteListener);
+ onCompleteListener.await();
fail();
} catch (ExecutionException expected) {
Throwable cause = expected.getCause();
@@ -163,7 +174,11 @@ public void testUpdateCustomInstallationId_CacheOk_BackendError_NotRetryable()
// Expect exception
try {
- Tasks.await(firebaseSegmentation.setCustomInstallationId(CUSTOM_INSTALLATION_ID));
+ TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
+ firebaseSegmentation
+ .setCustomInstallationId(CUSTOM_INSTALLATION_ID)
+ .addOnCompleteListener(taskExecutor, onCompleteListener);
+ onCompleteListener.await();
fail();
} catch (ExecutionException expected) {
Throwable cause = expected.getCause();
@@ -184,7 +199,11 @@ public void testUpdateCustomInstallationId_CacheError_BackendOk() throws Interru
// Expect exception
try {
- Tasks.await(firebaseSegmentation.setCustomInstallationId(CUSTOM_INSTALLATION_ID));
+ TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
+ firebaseSegmentation
+ .setCustomInstallationId(CUSTOM_INSTALLATION_ID)
+ .addOnCompleteListener(taskExecutor, onCompleteListener);
+ onCompleteListener.await();
fail();
} catch (ExecutionException expected) {
Throwable cause = expected.getCause();
@@ -206,7 +225,11 @@ public void testClearCustomInstallationId_CacheOk_BackendOk() throws Exception {
firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsOk);
// No exception, means success.
- assertNull(Tasks.await(firebaseSegmentation.setCustomInstallationId(null)));
+ TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
+ firebaseSegmentation
+ .setCustomInstallationId(null)
+ .addOnCompleteListener(taskExecutor, onCompleteListener);
+ assertNull(onCompleteListener.await());
CustomInstallationIdCacheEntryValue entryValue = actualCache.readCacheEntryValue();
assertNull(entryValue);
}
@@ -224,7 +247,11 @@ public void testClearCustomInstallationId_CacheOk_BackendError() throws Exceptio
// Expect exception
try {
- Tasks.await(firebaseSegmentation.setCustomInstallationId(null));
+ TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
+ firebaseSegmentation
+ .setCustomInstallationId(null)
+ .addOnCompleteListener(taskExecutor, onCompleteListener);
+ onCompleteListener.await();
fail();
} catch (ExecutionException expected) {
Throwable cause = expected.getCause();
@@ -248,7 +275,11 @@ public void testClearCustomInstallationId_CacheError_BackendOk() throws Interrup
// Expect exception
try {
- Tasks.await(firebaseSegmentation.setCustomInstallationId(CUSTOM_INSTALLATION_ID));
+ TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
+ firebaseSegmentation
+ .setCustomInstallationId(null)
+ .addOnCompleteListener(taskExecutor, onCompleteListener);
+ onCompleteListener.await();
fail();
} catch (ExecutionException expected) {
Throwable cause = expected.getCause();
diff --git a/firebase-segmentation/src/test/java/com/google/firebase/segmentation/TestOnCompleteListener.java b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/TestOnCompleteListener.java
new file mode 100644
index 00000000000..b81cbb8e485
--- /dev/null
+++ b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/TestOnCompleteListener.java
@@ -0,0 +1,70 @@
+// Copyright 2019 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.segmentation;
+
+import androidx.annotation.NonNull;
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.Task;
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper listener that works around a limitation of the Tasks API where await() cannot be called on
+ * the main thread. This listener works around it by running itself on a different thread, thus
+ * allowing the main thread to be woken up when the Tasks complete.
+ */
+public class TestOnCompleteListener implements OnCompleteListener {
+ private static final long TIMEOUT_MS = 5000;
+ private final CountDownLatch latch = new CountDownLatch(1);
+ private Task task;
+ private volatile TResult result;
+ private volatile Exception exception;
+ private volatile boolean successful;
+
+ @Override
+ public void onComplete(@NonNull Task task) {
+ this.task = task;
+ successful = task.isSuccessful();
+ if (successful) {
+ result = task.getResult();
+ } else {
+ exception = task.getException();
+ }
+ latch.countDown();
+ }
+
+ /** Blocks until the {@link #onComplete} is called. */
+ public TResult await() throws InterruptedException, ExecutionException {
+ if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ throw new InterruptedException("timed out waiting for result");
+ }
+ if (successful) {
+ return result;
+ } else {
+ if (exception instanceof InterruptedException) {
+ throw (InterruptedException) exception;
+ }
+ if (exception instanceof SetCustomInstallationIdException) {
+ throw new ExecutionException(exception);
+ }
+ if (exception instanceof IOException) {
+ throw new ExecutionException(exception);
+ }
+ throw new IllegalStateException("got an unexpected exception type", exception);
+ }
+ }
+}
diff --git a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java
similarity index 97%
rename from firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java
rename to firebase-segmentation/src/test/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java
index 9e22e522d2b..66c2e6f3928 100644
--- a/firebase-segmentation/src/androidTest/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java
+++ b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheTest.java
@@ -19,16 +19,16 @@
import static org.junit.Assert.assertTrue;
import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
/** Instrumented tests for {@link CustomInstallationIdCache} */
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
public class CustomInstallationIdCacheTest {
private FirebaseApp firebaseApp0;
From e65c3b0b05900315b5f5b8f30a099d776481073e Mon Sep 17 00:00:00 2001
From: Di Wu
Date: Thu, 23 Jan 2020 11:06:37 -0800
Subject: [PATCH 13/17] Hide non-public classes.
---
firebase-segmentation/api.txt | 51 +------------------
.../FirebaseSegmentationRegistrar.java | 1 +
.../SetCustomInstallationIdException.java | 2 +-
.../google/firebase/segmentation/Utils.java | 6 ++-
.../local/CustomInstallationIdCache.java | 2 +
.../CustomInstallationIdCacheEntryValue.java | 2 +
.../remote/SegmentationServiceClient.java | 6 ++-
subprojects.cfg | 2 +-
8 files changed, 18 insertions(+), 54 deletions(-)
diff --git a/firebase-segmentation/api.txt b/firebase-segmentation/api.txt
index e1469393eaf..e5feed1804b 100644
--- a/firebase-segmentation/api.txt
+++ b/firebase-segmentation/api.txt
@@ -8,11 +8,6 @@ package com.google.firebase.segmentation {
field public static final String TAG = "FirebaseSegmentation";
}
- public class FirebaseSegmentationRegistrar implements com.google.firebase.components.ComponentRegistrar {
- ctor public FirebaseSegmentationRegistrar();
- method @NonNull public java.util.List> getComponents();
- }
-
public class SetCustomInstallationIdException extends com.google.firebase.FirebaseException {
method @NonNull public com.google.firebase.segmentation.SetCustomInstallationIdException.Status getStatus();
}
@@ -21,51 +16,7 @@ package com.google.firebase.segmentation {
enum_constant public static final com.google.firebase.segmentation.SetCustomInstallationIdException.Status BACKEND_ERROR;
enum_constant public static final com.google.firebase.segmentation.SetCustomInstallationIdException.Status CLIENT_ERROR;
enum_constant public static final com.google.firebase.segmentation.SetCustomInstallationIdException.Status DUPLICATED_CUSTOM_INSTALLATION_ID;
- enum_constant public static final com.google.firebase.segmentation.SetCustomInstallationIdException.Status UNKOWN;
- }
-
-}
-
-package com.google.firebase.segmentation.local {
-
- public class CustomInstallationIdCache {
- ctor public CustomInstallationIdCache(@NonNull com.google.firebase.FirebaseApp);
- method @NonNull public boolean clear();
- method @NonNull public boolean insertOrUpdateCacheEntry(@NonNull com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue);
- method @Nullable public com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue readCacheEntryValue();
- }
-
- public enum CustomInstallationIdCache.CacheStatus {
- enum_constant public static final com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus PENDING_CLEAR;
- enum_constant public static final com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus PENDING_UPDATE;
- enum_constant public static final com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus SYNCED;
- }
-
- public abstract class CustomInstallationIdCacheEntryValue {
- ctor public CustomInstallationIdCacheEntryValue();
- method @NonNull public static com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue create(@NonNull String, @NonNull String, @NonNull com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus);
- method @NonNull public abstract com.google.firebase.segmentation.local.CustomInstallationIdCache.CacheStatus getCacheStatus();
- method @NonNull public abstract String getCustomInstallationId();
- method @NonNull public abstract String getFirebaseInstanceId();
- }
-
-}
-
-package com.google.firebase.segmentation.remote {
-
- public class SegmentationServiceClient {
- ctor public SegmentationServiceClient(@NonNull android.content.Context);
- method @NonNull public com.google.firebase.segmentation.remote.SegmentationServiceClient.Code clearCustomInstallationId(long, @NonNull String, @NonNull String, @NonNull String);
- method @NonNull public com.google.firebase.segmentation.remote.SegmentationServiceClient.Code updateCustomInstallationId(long, @NonNull String, @NonNull String, @NonNull String, @NonNull String);
- }
-
- public enum SegmentationServiceClient.Code {
- enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code CONFLICT;
- enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code HTTP_CLIENT_ERROR;
- enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code NETWORK_ERROR;
- enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code OK;
- enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code SERVER_ERROR;
- enum_constant public static final com.google.firebase.segmentation.remote.SegmentationServiceClient.Code UNAUTHORIZED;
+ enum_constant public static final com.google.firebase.segmentation.SetCustomInstallationIdException.Status UNKNOWN;
}
}
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java
index ca7f688d60a..bc30ab3bba6 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java
@@ -23,6 +23,7 @@
import java.util.Arrays;
import java.util.List;
+/** @hide */
public class FirebaseSegmentationRegistrar implements ComponentRegistrar {
@Override
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java
index 2291c078a03..1d0826d39d9 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/SetCustomInstallationIdException.java
@@ -21,7 +21,7 @@
public class SetCustomInstallationIdException extends FirebaseException {
public enum Status {
- UNKOWN(0),
+ UNKNOWN(0),
/** Error in Firebase SDK. */
CLIENT_ERROR(1),
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/Utils.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/Utils.java
index ca231a89cb5..e6d03fabb2e 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/Utils.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/Utils.java
@@ -17,7 +17,11 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-/** Util methods used for {@link FirebaseSegmentation} */
+/**
+ * Util methods used for {@link FirebaseSegmentation}
+ *
+ * @hide
+ */
class Utils {
private static final Pattern APP_ID_PATTERN =
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
index eb77445bf70..675bf1d0472 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCache.java
@@ -28,6 +28,8 @@
/**
* A layer that locally caches a few Firebase Segmentation attributes on top the Segmentation
* backend API.
+ *
+ * @hide
*/
public class CustomInstallationIdCache {
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java
index 5e2c1944278..5d67df1332b 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/local/CustomInstallationIdCacheEntryValue.java
@@ -21,6 +21,8 @@
/**
* This class represents a cache entry value in {@link CustomInstallationIdCache}, which contains a
* Firebase instance id, a custom installation id and the cache status of this entry.
+ *
+ * @hide
*/
@AutoValue
public abstract class CustomInstallationIdCacheEntryValue {
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
index 65058bb97da..9e550b6daa6 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/remote/SegmentationServiceClient.java
@@ -29,7 +29,11 @@
import org.json.JSONException;
import org.json.JSONObject;
-/** Http client that sends request to Firebase Segmentation backend API. To be implemented */
+/**
+ * Http client that sends request to Firebase Segmentation backend API. To be implemented
+ *
+ * @hide
+ */
public class SegmentationServiceClient {
private static final String FIREBASE_SEGMENTATION_API_DOMAIN =
diff --git a/subprojects.cfg b/subprojects.cfg
index d025734d440..531ddce7f68 100644
--- a/subprojects.cfg
+++ b/subprojects.cfg
@@ -25,10 +25,10 @@ firebase-inappmessaging-display
firebase-inappmessaging-display:ktx
firebase-installations-interop
firebase-installations
+firebase-segmentation
firebase-storage
firebase-storage:ktx
firebase-storage:test-app
-firebase-segmentation
protolite-well-known-types
encoders
From fd3a20f9839429a332a67b3d3fc9246cdb4480a9 Mon Sep 17 00:00:00 2001
From: Di Wu
Date: Mon, 30 Mar 2020 12:37:06 -0700
Subject: [PATCH 14/17] Firebase Segmentation SDK switches to depend on FIS.
---
.../firebase-segmentation.gradle | 4 +-
firebase-segmentation/gradle.properties | 2 +-
.../segmentation/FirebaseSegmentation.java | 62 +++++++++----------
.../FirebaseSegmentationRegistrar.java | 7 ++-
.../FirebaseSegmentationTest.java | 59 +++++++++++-------
5 files changed, 77 insertions(+), 57 deletions(-)
diff --git a/firebase-segmentation/firebase-segmentation.gradle b/firebase-segmentation/firebase-segmentation.gradle
index 1f8dfda7426..bad46c716b1 100644
--- a/firebase-segmentation/firebase-segmentation.gradle
+++ b/firebase-segmentation/firebase-segmentation.gradle
@@ -40,8 +40,8 @@ android {
dependencies {
implementation project(':firebase-common')
implementation project(':firebase-components')
-
- implementation('com.google.firebase:firebase-iid:17.0.3') {
+
+ implementation('com.google.firebase:firebase-installations-interop:16.0.0') {
exclude group: "com.google.firebase", module: "firebase-common"
}
diff --git a/firebase-segmentation/gradle.properties b/firebase-segmentation/gradle.properties
index 752913a3eb5..ba39c95ac29 100644
--- a/firebase-segmentation/gradle.properties
+++ b/firebase-segmentation/gradle.properties
@@ -1 +1 @@
-version=17.1.1
+version=17.2.0
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
index b1d5e6e6742..d9d00e67e16 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
@@ -21,8 +21,8 @@
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
-import com.google.firebase.iid.FirebaseInstanceId;
-import com.google.firebase.iid.InstanceIdResult;
+import com.google.firebase.installations.FirebaseInstallationsApi;
+import com.google.firebase.installations.InstallationTokenResult;
import com.google.firebase.segmentation.SetCustomInstallationIdException.Status;
import com.google.firebase.segmentation.local.CustomInstallationIdCache;
import com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue;
@@ -38,26 +38,26 @@ public class FirebaseSegmentation {
public static final String TAG = "FirebaseSegmentation";
private final FirebaseApp firebaseApp;
- private final FirebaseInstanceId firebaseInstanceId;
+ private final FirebaseInstallationsApi firebaseInstallationsApi;
private final CustomInstallationIdCache localCache;
private final SegmentationServiceClient backendServiceClient;
private final Executor executor;
- FirebaseSegmentation(FirebaseApp firebaseApp) {
+ FirebaseSegmentation(FirebaseApp firebaseApp, FirebaseInstallationsApi firebaseInstallationsApi) {
this(
firebaseApp,
- FirebaseInstanceId.getInstance(firebaseApp),
+ firebaseInstallationsApi,
new CustomInstallationIdCache(firebaseApp),
new SegmentationServiceClient(firebaseApp.getApplicationContext()));
}
FirebaseSegmentation(
FirebaseApp firebaseApp,
- FirebaseInstanceId firebaseInstanceId,
+ FirebaseInstallationsApi firebaseInstallationsApi,
CustomInstallationIdCache localCache,
SegmentationServiceClient backendServiceClient) {
this.firebaseApp = firebaseApp;
- this.firebaseInstanceId = firebaseInstanceId;
+ this.firebaseInstallationsApi = firebaseInstallationsApi;
this.localCache = localCache;
this.backendServiceClient = backendServiceClient;
this.executor = Executors.newFixedThreadPool(4);
@@ -82,7 +82,7 @@ public static FirebaseSegmentation getInstance() {
*/
@NonNull
public static FirebaseSegmentation getInstance(@NonNull FirebaseApp app) {
- Preconditions.checkArgument(app != null, "Null is not a valid value of FirebaseApp.");
+ Preconditions.checkArgument(app != null, "Null is not a valid value " + "of FirebaseApp.");
return app.get(FirebaseSegmentation.class);
}
@@ -125,20 +125,21 @@ private Void updateCustomInstallationId(String customInstallationId)
return null;
}
- InstanceIdResult instanceIdResult;
+ String fid;
+ InstallationTokenResult installationTokenResult;
try {
- instanceIdResult = Tasks.await(firebaseInstanceId.getInstanceId());
+ fid = Tasks.await(firebaseInstallationsApi.getId());
+ // No need to force refresh token.
+ installationTokenResult = Tasks.await(firebaseInstallationsApi.getToken(false));
} catch (ExecutionException | InterruptedException e) {
throw new SetCustomInstallationIdException(
- Status.CLIENT_ERROR, "Failed to get Firebase instance id");
+ Status.CLIENT_ERROR, "Failed to get Firebase installation ID and token");
}
boolean firstUpdateCacheResult =
localCache.insertOrUpdateCacheEntry(
CustomInstallationIdCacheEntryValue.create(
- customInstallationId,
- instanceIdResult.getId(),
- CustomInstallationIdCache.CacheStatus.PENDING_UPDATE));
+ customInstallationId, fid, CustomInstallationIdCache.CacheStatus.PENDING_UPDATE));
if (!firstUpdateCacheResult) {
throw new SetCustomInstallationIdException(
@@ -146,15 +147,13 @@ private Void updateCustomInstallationId(String customInstallationId)
}
// Start requesting backend when first cache updae is done.
- String iid = instanceIdResult.getId();
- String iidToken = instanceIdResult.getToken();
Code backendRequestResult =
backendServiceClient.updateCustomInstallationId(
Utils.getProjectNumberFromAppId(firebaseApp.getOptions().getApplicationId()),
firebaseApp.getOptions().getApiKey(),
customInstallationId,
- iid,
- iidToken);
+ fid,
+ installationTokenResult.getToken());
boolean finalUpdateCacheResult;
switch (backendRequestResult) {
@@ -162,9 +161,7 @@ private Void updateCustomInstallationId(String customInstallationId)
finalUpdateCacheResult =
localCache.insertOrUpdateCacheEntry(
CustomInstallationIdCacheEntryValue.create(
- customInstallationId,
- instanceIdResult.getId(),
- CustomInstallationIdCache.CacheStatus.SYNCED));
+ customInstallationId, fid, CustomInstallationIdCache.CacheStatus.SYNCED));
break;
case UNAUTHORIZED:
localCache.clear();
@@ -174,14 +171,16 @@ private Void updateCustomInstallationId(String customInstallationId)
localCache.clear();
throw new SetCustomInstallationIdException(
Status.DUPLICATED_CUSTOM_INSTALLATION_ID,
- "The custom installation id is used by another Firebase installation in your project.");
+ "The custom installation id is used by another "
+ + "Firebase installation in your project.");
case HTTP_CLIENT_ERROR:
localCache.clear();
throw new SetCustomInstallationIdException(Status.CLIENT_ERROR, "Http client error(4xx)");
case NETWORK_ERROR:
case SERVER_ERROR:
default:
- // These are considered retryable errors, so not to clean up the cache.
+ // These are considered retryable errors, so not to clean up
+ // the cache.
throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
}
@@ -212,32 +211,33 @@ private Void updateCustomInstallationId(String customInstallationId)
*/
@WorkerThread
private Void clearCustomInstallationId() throws SetCustomInstallationIdException {
- InstanceIdResult instanceIdResult;
+ String fid;
+ InstallationTokenResult installationTokenResult;
try {
- instanceIdResult = Tasks.await(firebaseInstanceId.getInstanceId());
+ fid = Tasks.await(firebaseInstallationsApi.getId());
+ // No need to force refresh token.
+ installationTokenResult = Tasks.await(firebaseInstallationsApi.getToken(false));
} catch (ExecutionException | InterruptedException e) {
throw new SetCustomInstallationIdException(
- Status.CLIENT_ERROR, "Failed to get Firebase instance id");
+ Status.CLIENT_ERROR, "Failed to get Firebase installation ID and token");
}
boolean firstUpdateCacheResult =
localCache.insertOrUpdateCacheEntry(
CustomInstallationIdCacheEntryValue.create(
- "", instanceIdResult.getId(), CustomInstallationIdCache.CacheStatus.PENDING_CLEAR));
+ "", fid, CustomInstallationIdCache.CacheStatus.PENDING_CLEAR));
if (!firstUpdateCacheResult) {
throw new SetCustomInstallationIdException(
Status.CLIENT_ERROR, "Failed to update client side cache");
}
- String iid = instanceIdResult.getId();
- String iidToken = instanceIdResult.getToken();
Code backendRequestResult =
backendServiceClient.clearCustomInstallationId(
Utils.getProjectNumberFromAppId(firebaseApp.getOptions().getApplicationId()),
firebaseApp.getOptions().getApiKey(),
- iid,
- iidToken);
+ fid,
+ installationTokenResult.getToken());
boolean finalUpdateCacheResult;
switch (backendRequestResult) {
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java
index bc30ab3bba6..c69385a9cba 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentationRegistrar.java
@@ -19,6 +19,7 @@
import com.google.firebase.components.Component;
import com.google.firebase.components.ComponentRegistrar;
import com.google.firebase.components.Dependency;
+import com.google.firebase.installations.FirebaseInstallationsApi;
import com.google.firebase.platforminfo.LibraryVersionComponent;
import java.util.Arrays;
import java.util.List;
@@ -32,7 +33,11 @@ public List> getComponents() {
return Arrays.asList(
Component.builder(FirebaseSegmentation.class)
.add(Dependency.required(FirebaseApp.class))
- .factory(c -> new FirebaseSegmentation(c.get(FirebaseApp.class)))
+ .add(Dependency.required(FirebaseInstallationsApi.class))
+ .factory(
+ c ->
+ new FirebaseSegmentation(
+ c.get(FirebaseApp.class), c.get(FirebaseInstallationsApi.class)))
.build(),
LibraryVersionComponent.create("fire-segmentation", BuildConfig.VERSION_NAME));
}
diff --git a/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationTest.java b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationTest.java
index 701bfcab06f..6a8a37485c2 100644
--- a/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationTest.java
+++ b/firebase-segmentation/src/test/java/com/google/firebase/segmentation/FirebaseSegmentationTest.java
@@ -27,8 +27,8 @@
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
-import com.google.firebase.iid.FirebaseInstanceId;
-import com.google.firebase.iid.InstanceIdResult;
+import com.google.firebase.installations.FirebaseInstallationsApi;
+import com.google.firebase.installations.InstallationTokenResult;
import com.google.firebase.segmentation.local.CustomInstallationIdCache;
import com.google.firebase.segmentation.local.CustomInstallationIdCacheEntryValue;
import com.google.firebase.segmentation.remote.SegmentationServiceClient;
@@ -44,6 +44,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -51,10 +52,11 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class FirebaseSegmentationTest {
private static final String CUSTOM_INSTALLATION_ID = "123";
- private static final String FIREBASE_INSTANCE_ID = "cAAAAAAAAAA";
+ private static final String FIREBASE_INSTALLATION_ID = "fid_is_better_than_iid";
+ private static final String FIREBASE_INSTALLATION_ID_TOKEN = "fis_token";
private FirebaseApp firebaseApp;
- @Mock private FirebaseInstanceId firebaseInstanceId;
+ @Mock private FirebaseInstallationsApi firebaseInstallationsApi;
@Mock private SegmentationServiceClient backendClientReturnsOk;
@Mock private SegmentationServiceClient backendClientReturnsError;
@@ -88,20 +90,33 @@ public void setUp() {
when(backendClientReturnsError.clearCustomInstallationId(
anyLong(), anyString(), anyString(), anyString()))
.thenReturn(SegmentationServiceClient.Code.SERVER_ERROR);
- when(firebaseInstanceId.getInstanceId())
+ when(firebaseInstallationsApi.getId()).thenReturn(Tasks.forResult(FIREBASE_INSTALLATION_ID));
+ when(firebaseInstallationsApi.getToken(Mockito.anyBoolean()))
.thenReturn(
Tasks.forResult(
- new InstanceIdResult() {
+ new InstallationTokenResult() {
@NonNull
@Override
- public String getId() {
- return FIREBASE_INSTANCE_ID;
+ public String getToken() {
+ return FIREBASE_INSTALLATION_ID_TOKEN;
}
@NonNull
@Override
- public String getToken() {
- return "iid_token";
+ public long getTokenExpirationTimestamp() {
+ return 0;
+ }
+
+ @NonNull
+ @Override
+ public long getTokenCreationTimestamp() {
+ return 0;
+ }
+
+ @NonNull
+ @Override
+ public Builder toBuilder() {
+ return null;
}
}));
when(cacheReturnsError.insertOrUpdateCacheEntry(any())).thenReturn(false);
@@ -119,7 +134,7 @@ public void cleanUp() throws Exception {
public void testUpdateCustomInstallationId_CacheOk_BackendOk() throws Exception {
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
- firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsOk);
+ firebaseApp, firebaseInstallationsApi, actualCache, backendClientReturnsOk);
// No exception, means success.
TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
@@ -129,7 +144,7 @@ public void testUpdateCustomInstallationId_CacheOk_BackendOk() throws Exception
assertNull(onCompleteListener.await());
CustomInstallationIdCacheEntryValue entryValue = actualCache.readCacheEntryValue();
assertThat(entryValue.getCustomInstallationId()).isEqualTo(CUSTOM_INSTALLATION_ID);
- assertThat(entryValue.getFirebaseInstanceId()).isEqualTo(FIREBASE_INSTANCE_ID);
+ assertThat(entryValue.getFirebaseInstanceId()).isEqualTo(FIREBASE_INSTALLATION_ID);
assertThat(entryValue.getCacheStatus()).isEqualTo(CustomInstallationIdCache.CacheStatus.SYNCED);
}
@@ -138,7 +153,7 @@ public void testUpdateCustomInstallationId_CacheOk_BackendError_Retryable()
throws InterruptedException {
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
- firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsError);
+ firebaseApp, firebaseInstallationsApi, actualCache, backendClientReturnsError);
// Expect exception
try {
@@ -157,7 +172,7 @@ public void testUpdateCustomInstallationId_CacheOk_BackendError_Retryable()
CustomInstallationIdCacheEntryValue entryValue = actualCache.readCacheEntryValue();
assertThat(entryValue.getCustomInstallationId()).isEqualTo(CUSTOM_INSTALLATION_ID);
- assertThat(entryValue.getFirebaseInstanceId()).isEqualTo(FIREBASE_INSTANCE_ID);
+ assertThat(entryValue.getFirebaseInstanceId()).isEqualTo(FIREBASE_INSTALLATION_ID);
assertThat(entryValue.getCacheStatus())
.isEqualTo(CustomInstallationIdCache.CacheStatus.PENDING_UPDATE);
}
@@ -170,7 +185,7 @@ public void testUpdateCustomInstallationId_CacheOk_BackendError_NotRetryable()
.thenReturn(SegmentationServiceClient.Code.CONFLICT);
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
- firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsError);
+ firebaseApp, firebaseInstallationsApi, actualCache, backendClientReturnsError);
// Expect exception
try {
@@ -195,7 +210,7 @@ public void testUpdateCustomInstallationId_CacheOk_BackendError_NotRetryable()
public void testUpdateCustomInstallationId_CacheError_BackendOk() throws InterruptedException {
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
- firebaseApp, firebaseInstanceId, cacheReturnsError, backendClientReturnsOk);
+ firebaseApp, firebaseInstallationsApi, cacheReturnsError, backendClientReturnsOk);
// Expect exception
try {
@@ -218,11 +233,11 @@ public void testClearCustomInstallationId_CacheOk_BackendOk() throws Exception {
actualCache.insertOrUpdateCacheEntry(
CustomInstallationIdCacheEntryValue.create(
CUSTOM_INSTALLATION_ID,
- FIREBASE_INSTANCE_ID,
+ FIREBASE_INSTALLATION_ID,
CustomInstallationIdCache.CacheStatus.SYNCED));
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
- firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsOk);
+ firebaseApp, firebaseInstallationsApi, actualCache, backendClientReturnsOk);
// No exception, means success.
TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
@@ -239,11 +254,11 @@ public void testClearCustomInstallationId_CacheOk_BackendError() throws Exceptio
actualCache.insertOrUpdateCacheEntry(
CustomInstallationIdCacheEntryValue.create(
CUSTOM_INSTALLATION_ID,
- FIREBASE_INSTANCE_ID,
+ FIREBASE_INSTALLATION_ID,
CustomInstallationIdCache.CacheStatus.SYNCED));
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
- firebaseApp, firebaseInstanceId, actualCache, backendClientReturnsError);
+ firebaseApp, firebaseInstallationsApi, actualCache, backendClientReturnsError);
// Expect exception
try {
@@ -262,7 +277,7 @@ public void testClearCustomInstallationId_CacheOk_BackendError() throws Exceptio
CustomInstallationIdCacheEntryValue entryValue = actualCache.readCacheEntryValue();
assertThat(entryValue.getCustomInstallationId().isEmpty()).isTrue();
- assertThat(entryValue.getFirebaseInstanceId()).isEqualTo(FIREBASE_INSTANCE_ID);
+ assertThat(entryValue.getFirebaseInstanceId()).isEqualTo(FIREBASE_INSTALLATION_ID);
assertThat(entryValue.getCacheStatus())
.isEqualTo(CustomInstallationIdCache.CacheStatus.PENDING_CLEAR);
}
@@ -271,7 +286,7 @@ public void testClearCustomInstallationId_CacheOk_BackendError() throws Exceptio
public void testClearCustomInstallationId_CacheError_BackendOk() throws InterruptedException {
FirebaseSegmentation firebaseSegmentation =
new FirebaseSegmentation(
- firebaseApp, firebaseInstanceId, cacheReturnsError, backendClientReturnsOk);
+ firebaseApp, firebaseInstallationsApi, cacheReturnsError, backendClientReturnsOk);
// Expect exception
try {
From aab0d3fcdb93071101e1106bd87ae2925d790332 Mon Sep 17 00:00:00 2001
From: Di Wu
Date: Mon, 30 Mar 2020 12:57:27 -0700
Subject: [PATCH 15/17] Add FIS as a runtime dependency
---
firebase-segmentation/firebase-segmentation.gradle | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/firebase-segmentation/firebase-segmentation.gradle b/firebase-segmentation/firebase-segmentation.gradle
index bad46c716b1..89ff1f4c21b 100644
--- a/firebase-segmentation/firebase-segmentation.gradle
+++ b/firebase-segmentation/firebase-segmentation.gradle
@@ -40,14 +40,12 @@ android {
dependencies {
implementation project(':firebase-common')
implementation project(':firebase-components')
-
- implementation('com.google.firebase:firebase-installations-interop:16.0.0') {
- exclude group: "com.google.firebase", module: "firebase-common"
- }
+ implementation project(':firebase-installations-interop')
+ runtimeOnly project(':firebase-installations')
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.multidex:multidex:2.0.1'
- implementation 'com.google.android.gms:play-services-tasks:17.0.0'
+ implementation 'com.google.android.gms:play-services-tasks:17.0.2'
compileOnly "com.google.auto.value:auto-value-annotations:1.6.5"
annotationProcessor "com.google.auto.value:auto-value:1.6.2"
From 70de2e113c745dc7a1f19fc820906b0615c11ea6 Mon Sep 17 00:00:00 2001
From: Di Wu
Date: Mon, 30 Mar 2020 13:01:54 -0700
Subject: [PATCH 16/17] minor formatting
---
.../google/firebase/segmentation/FirebaseSegmentation.java | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
index d9d00e67e16..b7e70bfef69 100644
--- a/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
+++ b/firebase-segmentation/src/main/java/com/google/firebase/segmentation/FirebaseSegmentation.java
@@ -171,16 +171,14 @@ private Void updateCustomInstallationId(String customInstallationId)
localCache.clear();
throw new SetCustomInstallationIdException(
Status.DUPLICATED_CUSTOM_INSTALLATION_ID,
- "The custom installation id is used by another "
- + "Firebase installation in your project.");
+ "The custom installation id is used by another Firebase installation in your project.");
case HTTP_CLIENT_ERROR:
localCache.clear();
throw new SetCustomInstallationIdException(Status.CLIENT_ERROR, "Http client error(4xx)");
case NETWORK_ERROR:
case SERVER_ERROR:
default:
- // These are considered retryable errors, so not to clean up
- // the cache.
+ // These are considered retryable errors, so not to clean up the cache.
throw new SetCustomInstallationIdException(Status.BACKEND_ERROR);
}
From 413a194b37eb0923469fd1a098ac3729c137e87e Mon Sep 17 00:00:00 2001
From: Di Wu
Date: Thu, 24 Sep 2020 10:46:20 -0700
Subject: [PATCH 17/17] Change version code of firebase segmentation SDK to be
a beta version. Also remove some unneeded build dependencies.
---
firebase-segmentation/firebase-segmentation.gradle | 2 --
firebase-segmentation/gradle.properties | 2 +-
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/firebase-segmentation/firebase-segmentation.gradle b/firebase-segmentation/firebase-segmentation.gradle
index 89ff1f4c21b..5941b9f8130 100644
--- a/firebase-segmentation/firebase-segmentation.gradle
+++ b/firebase-segmentation/firebase-segmentation.gradle
@@ -43,8 +43,6 @@ dependencies {
implementation project(':firebase-installations-interop')
runtimeOnly project(':firebase-installations')
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.google.android.gms:play-services-tasks:17.0.2'
compileOnly "com.google.auto.value:auto-value-annotations:1.6.5"
diff --git a/firebase-segmentation/gradle.properties b/firebase-segmentation/gradle.properties
index ba39c95ac29..29ae9e151c9 100644
--- a/firebase-segmentation/gradle.properties
+++ b/firebase-segmentation/gradle.properties
@@ -1 +1 @@
-version=17.2.0
+version=16.0.0-beta01