diff --git a/firebase-installations/api.txt b/firebase-installations/api.txt index e2120cdf91f..2024c519fd0 100644 --- a/firebase-installations/api.txt +++ b/firebase-installations/api.txt @@ -32,6 +32,7 @@ package com.google.firebase.installations.local { public class IidStore { ctor public IidStore(); method @Nullable public String readIid(); + method @Nullable public String readToken(); } public class PersistedInstallation { @@ -92,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/local/IidStore.java b/firebase-installations/src/main/java/com/google/firebase/installations/local/IidStore.java index 4e8366df7bf..704ef19f3b6 100644 --- a/firebase-installations/src/main/java/com/google/firebase/installations/local/IidStore.java +++ b/firebase-installations/src/main/java/com/google/firebase/installations/local/IidStore.java @@ -30,6 +30,8 @@ import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; +import org.json.JSONException; +import org.json.JSONObject; /** * Read existing iid only for default (first initialized) instance of this firebase application.* @@ -38,10 +40,16 @@ public class IidStore { private static final String IID_SHARED_PREFS_NAME = "com.google.android.gms.appid"; private static final String STORE_KEY_PUB = "|S||P|"; private static final String STORE_KEY_ID = "|S|id"; + private static final String STORE_KEY_TOKEN = "|T|"; + private static final String DEFAULT_SCOPE = "|*"; + private static final String JSON_TOKEN_KEY = "token"; + private static final String JSON_ENCODED_PREFIX = "{"; @GuardedBy("iidPrefs") private final SharedPreferences iidPrefs; + private final String defaultSenderId; + public IidStore() { // Different FirebaseApp in the same Android application should have the same application // context and same dir path. We only read existing Iids for the default firebase application. @@ -49,6 +57,62 @@ public IidStore() { FirebaseApp.getInstance() .getApplicationContext() .getSharedPreferences(IID_SHARED_PREFS_NAME, Context.MODE_PRIVATE); + + defaultSenderId = getDefaultSenderId(FirebaseApp.getInstance()); + } + + private static String getDefaultSenderId(FirebaseApp app) { + // Check for an explicit sender id + String senderId = app.getOptions().getGcmSenderId(); + if (senderId != null) { + return senderId; + } + String appId = app.getOptions().getApplicationId(); + if (!appId.startsWith("1:") && !appId.startsWith("2:")) { + // If applicationId does not contain a (GMP-)App-ID, it contains a Sender identifier + return appId; + } + // For v1 app IDs, fall back to parsing the project number out + @SuppressWarnings("StringSplitter") + String[] parts = appId.split(":"); + if (parts.length != 4) { + return null; // Invalid format + } + String projectNumber = parts[1]; + if (projectNumber.isEmpty()) { + return null; // No project number + } + return projectNumber; + } + + private String createTokenKey(@NonNull String senderId) { + return STORE_KEY_TOKEN + senderId + DEFAULT_SCOPE; + } + + @Nullable + public String readToken() { + synchronized (iidPrefs) { + String token = iidPrefs.getString(createTokenKey(defaultSenderId), null); + if (token.isEmpty()) { + return null; + } + + if (token.startsWith(JSON_ENCODED_PREFIX)) { + return parseIidTokenFromJson(token); + } + // Legacy value, token is whole string + return token; + } + } + + private String parseIidTokenFromJson(String token) { + // Encoded as JSON + try { + JSONObject json = new JSONObject(token); + return json.getString(JSON_TOKEN_KEY); + } catch (JSONException e) { + return null; + } } @Nullable 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 *