diff --git a/firebase-installations/api.txt b/firebase-installations/api.txt
index 4e5eb23d352..2024c519fd0 100644
--- a/firebase-installations/api.txt
+++ b/firebase-installations/api.txt
@@ -93,7 +93,7 @@ package com.google.firebase.installations.remote {
public class FirebaseInstallationServiceClient {
ctor public FirebaseInstallationServiceClient(@NonNull android.content.Context, @Nullable com.google.firebase.platforminfo.UserAgentPublisher, @Nullable com.google.firebase.heartbeatinfo.HeartBeatInfo);
- method @NonNull public com.google.firebase.installations.remote.InstallationResponse createFirebaseInstallation(@NonNull String, @NonNull String, @NonNull String, @NonNull String) throws java.io.IOException;
+ method @NonNull public com.google.firebase.installations.remote.InstallationResponse createFirebaseInstallation(@NonNull String, @NonNull String, @NonNull String, @NonNull String, @Nullable String) throws java.io.IOException;
method @NonNull public void deleteFirebaseInstallation(@NonNull String, @NonNull String, @NonNull String, @NonNull String) throws com.google.firebase.FirebaseException, java.io.IOException;
method @NonNull public com.google.firebase.installations.remote.TokenResult generateAuthToken(@NonNull String, @NonNull String, @NonNull String, @NonNull String) throws java.io.IOException;
}
diff --git a/firebase-installations/src/main/java/com/google/firebase/installations/FirebaseInstallations.java b/firebase-installations/src/main/java/com/google/firebase/installations/FirebaseInstallations.java
index 7abd5351ed7..8288d2c205f 100644
--- a/firebase-installations/src/main/java/com/google/firebase/installations/FirebaseInstallations.java
+++ b/firebase-installations/src/main/java/com/google/firebase/installations/FirebaseInstallations.java
@@ -307,8 +307,8 @@ private final void doRegistrationInternal(boolean forceRefresh) {
* been persisted.
*/
private PersistedInstallationEntry getPrefsWithGeneratedIdMultiProcessSafe() {
- CrossProcessLock lock = CrossProcessLock
- .acquire(firebaseApp.getApplicationContext(), LOCKFILE_NAME_GENERATE_FID);
+ CrossProcessLock lock =
+ CrossProcessLock.acquire(firebaseApp.getApplicationContext(), LOCKFILE_NAME_GENERATE_FID);
try {
synchronized (lockGenerateFid) {
PersistedInstallationEntry prefs =
@@ -354,7 +354,8 @@ private PersistedInstallationEntry registerFidWithServer(PersistedInstallationEn
/*apiKey= */ firebaseApp.getOptions().getApiKey(),
/*fid= */ prefs.getFirebaseInstallationId(),
/*projectID= */ firebaseApp.getOptions().getProjectId(),
- /*appId= */ getApplicationId());
+ /*appId= */ getApplicationId(),
+ /* migration-header= */ null);
switch (response.getResponseCode()) {
case OK:
diff --git a/firebase-installations/src/main/java/com/google/firebase/installations/remote/FirebaseInstallationServiceClient.java b/firebase-installations/src/main/java/com/google/firebase/installations/remote/FirebaseInstallationServiceClient.java
index bb392f9472e..a92dd0ba922 100644
--- a/firebase-installations/src/main/java/com/google/firebase/installations/remote/FirebaseInstallationServiceClient.java
+++ b/firebase-installations/src/main/java/com/google/firebase/installations/remote/FirebaseInstallationServiceClient.java
@@ -70,6 +70,8 @@ public class FirebaseInstallationServiceClient {
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 static final String X_ANDROID_IID_MIGRATION_KEY = "x-goog-fis-android-iid-migration-auth";
+
private static final int NETWORK_TIMEOUT_MILLIS = 10000;
private static final Pattern EXPIRATION_TIMESTAMP_PATTERN = Pattern.compile("[0-9]+s");
@@ -87,10 +89,10 @@ public class FirebaseInstallationServiceClient {
public FirebaseInstallationServiceClient(
@NonNull Context context,
@Nullable UserAgentPublisher publisher,
- @Nullable HeartBeatInfo heartBeatInfo) {
+ @Nullable HeartBeatInfo heartbeatInfo) {
this.context = context;
this.userAgentPublisher = publisher;
- this.heartbeatInfo = heartBeatInfo;
+ this.heartbeatInfo = heartbeatInfo;
}
/**
@@ -100,6 +102,8 @@ public FirebaseInstallationServiceClient(
* @param fid Firebase Installation Identifier
* @param projectID Project Id
* @param appId the identifier of a Firebase application
+ * @param iidToken the identifier token of a Firebase application with instance id. It is set to
+ * null for a FID.
* @return {@link InstallationResponse} generated from the response body
*
* - 400: return response with status BAD_CONFIG
@@ -111,7 +115,11 @@ public FirebaseInstallationServiceClient(
*/
@NonNull
public InstallationResponse createFirebaseInstallation(
- @NonNull String apiKey, @NonNull String fid, @NonNull String projectID, @NonNull String appId)
+ @NonNull String apiKey,
+ @NonNull String fid,
+ @NonNull String projectID,
+ @NonNull String appId,
+ @Nullable String iidToken)
throws IOException {
String resourceName = String.format(CREATE_REQUEST_RESOURCE_NAME_FORMAT, projectID);
int retryCount = 0;
@@ -128,6 +136,11 @@ public InstallationResponse createFirebaseInstallation(
httpsURLConnection.setRequestMethod("POST");
httpsURLConnection.setDoOutput(true);
+ // Note: Set the iid token header for authenticating the Instance-ID migrating to FIS.
+ if (iidToken != null) {
+ httpsURLConnection.addRequestProperty(X_ANDROID_IID_MIGRATION_KEY, iidToken);
+ }
+
GZIPOutputStream gzipOutputStream =
new GZIPOutputStream(httpsURLConnection.getOutputStream());
try {
diff --git a/firebase-installations/src/test/java/com/google/firebase/installations/FirebaseInstallationsTest.java b/firebase-installations/src/test/java/com/google/firebase/installations/FirebaseInstallationsTest.java
index 5dc10b77fda..9cabfa99494 100644
--- a/firebase-installations/src/test/java/com/google/firebase/installations/FirebaseInstallationsTest.java
+++ b/firebase-installations/src/test/java/com/google/firebase/installations/FirebaseInstallationsTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.matches;
import static org.mockito.Mockito.doAnswer;
@@ -178,7 +179,8 @@ public void cleanUp() {
*/
@Test
public void testGetId_noNetwork_noIid() throws Exception {
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenThrow(new IOException());
when(mockBackend.generateAuthToken(anyString(), anyString(), anyString(), anyString()))
.thenThrow(new IOException());
@@ -207,7 +209,8 @@ public void testGetId_noNetwork_noIid() throws Exception {
@Test
public void testGetId_noNetwork_iidPresent() throws Exception {
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenThrow(new IOException());
when(mockBackend.generateAuthToken(anyString(), anyString(), anyString(), anyString()))
.thenThrow(new IOException());
@@ -236,7 +239,8 @@ public void testGetId_noNetwork_iidPresent() throws Exception {
@Test
public void testGetId_noNetwork_fidAlreadyGenerated() throws Exception {
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenThrow(new IOException());
when(mockBackend.generateAuthToken(anyString(), anyString(), anyString(), anyString()))
.thenThrow(new IOException());
@@ -303,7 +307,7 @@ public void testGetId_ValidIdAndToken_NoBackendCalls() throws Exception {
@Test
public void testGetId_UnRegisteredId_IssueCreateIdCall() throws Exception {
when(mockBackend.createFirebaseInstallation(
- anyString(), matches(TEST_FID_1), anyString(), anyString()))
+ anyString(), matches(TEST_FID_1), anyString(), anyString(), any()))
.thenReturn(TEST_INSTALLATION_RESPONSE);
persistedInstallation.insertOrUpdatePersistedInstallationEntry(
PersistedInstallationEntry.INSTANCE.withUnregisteredFid(TEST_FID_1));
@@ -322,7 +326,8 @@ public void testGetId_UnRegisteredId_IssueCreateIdCall() throws Exception {
// check that the mockClient didn't get invoked at all, since the fid is already registered
// and the authtoken is present and not expired
verify(mockBackend)
- .createFirebaseInstallation(anyString(), matches(TEST_FID_1), anyString(), anyString());
+ .createFirebaseInstallation(
+ anyString(), matches(TEST_FID_1), anyString(), anyString(), any());
verify(mockBackend, never())
.generateAuthToken(anyString(), anyString(), anyString(), anyString());
@@ -335,7 +340,8 @@ public void testGetId_UnRegisteredId_IssueCreateIdCall() throws Exception {
@Test
public void testGetId_migrateIid_successful() throws Exception {
when(mockIidStore.readIid()).thenReturn(TEST_INSTANCE_ID_1);
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenReturn(TEST_INSTALLATION_RESPONSE_WITH_IID);
// Do the actual getId() call under test.
@@ -361,7 +367,8 @@ public void testGetId_migrateIid_successful() throws Exception {
@Test
public void testGetId_multipleCalls_sameFIDReturned() throws Exception {
when(mockIidStore.readIid()).thenReturn(null);
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenReturn(TEST_INSTALLATION_RESPONSE);
// Call getId multiple times
@@ -384,7 +391,7 @@ public void testGetId_multipleCalls_sameFIDReturned() throws Exception {
.that(task2.getResult())
.isEqualTo(TEST_FID_1);
verify(mockBackend, times(1))
- .createFirebaseInstallation(TEST_API_KEY, TEST_FID_1, TEST_PROJECT_ID, TEST_APP_ID_1);
+ .createFirebaseInstallation(TEST_API_KEY, TEST_FID_1, TEST_PROJECT_ID, TEST_APP_ID_1, null);
PersistedInstallationEntry entry = persistedInstallation.readPersistedInstallationEntryValue();
assertThat(entry.getFirebaseInstallationId(), equalTo(TEST_FID_1));
assertTrue("the entry isn't doesn't have a registered fid: " + entry, entry.isRegistered());
@@ -399,7 +406,8 @@ public void testGetId_unregistered_replacesFidWithResponse() throws Exception {
// Update local storage with installation entry that has invalid fid.
persistedInstallation.insertOrUpdatePersistedInstallationEntry(
PersistedInstallationEntry.INSTANCE.withUnregisteredFid("tobereplaced"));
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenReturn(TEST_INSTALLATION_RESPONSE);
// The first call will return the existing FID, "tobereplaced"
@@ -435,7 +443,8 @@ public void testGetId_ServerError_UnregisteredFID() throws Exception {
PersistedInstallationEntry.INSTANCE.withUnregisteredFid(TEST_FID_1));
// have the server return a server error for the registration
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenReturn(
InstallationResponse.builder().setResponseCode(ResponseCode.BAD_CONFIG).build());
@@ -469,7 +478,8 @@ public void testGetId_fidRegistrationUncheckedException_statusUpdated() throws E
PersistedInstallationEntry.INSTANCE.withUnregisteredFid(TEST_FID_1));
// Mocking unchecked exception on FIS createFirebaseInstallation
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenThrow(new IOException());
TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();
@@ -569,14 +579,15 @@ public void testGetId_expiredAuthToken_refreshesAuthToken() throws Exception {
.isEqualTo(TEST_AUTH_TOKEN_2);
verify(mockBackend, never())
- .createFirebaseInstallation(TEST_API_KEY, TEST_FID_1, TEST_PROJECT_ID, TEST_APP_ID_1);
+ .createFirebaseInstallation(TEST_API_KEY, TEST_FID_1, TEST_PROJECT_ID, TEST_APP_ID_1, null);
verify(mockBackend, times(1))
.generateAuthToken(TEST_API_KEY, TEST_FID_1, TEST_PROJECT_ID, TEST_REFRESH_TOKEN);
}
@Test
public void testGetAuthToken_fidDoesNotExist_successful() throws Exception {
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenReturn(TEST_INSTALLATION_RESPONSE);
TestOnCompleteListener onCompleteListener =
@@ -650,7 +661,8 @@ public void testGetToken_unregisteredFid_fetchedNewTokenFromFIS() throws Excepti
// calls getId to ensure FID registration and returns a valid auth token.
persistedInstallation.insertOrUpdatePersistedInstallationEntry(
PersistedInstallationEntry.INSTANCE.withUnregisteredFid(TEST_FID_1));
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenReturn(TEST_INSTALLATION_RESPONSE);
TestOnCompleteListener onCompleteListener =
@@ -859,7 +871,8 @@ public void testDelete_registeredFID_successful() throws Exception {
utils.currentTimeInSecs(),
TEST_AUTH_TOKEN,
TEST_TOKEN_EXPIRATION_TIMESTAMP));
- when(mockBackend.createFirebaseInstallation(anyString(), anyString(), anyString(), anyString()))
+ when(mockBackend.createFirebaseInstallation(
+ anyString(), anyString(), anyString(), anyString(), any()))
.thenReturn(TEST_INSTALLATION_RESPONSE);
TestOnCompleteListener onCompleteListener = new TestOnCompleteListener<>();