Skip to content

Commit ca4ca38

Browse files
authored
Integrate V2 heartbeat with app check. (#3472)
* app check * add platform logging to app check * fix app check * update
1 parent e46adf7 commit ca4ca38

File tree

6 files changed

+57
-127
lines changed

6 files changed

+57
-127
lines changed

appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckRegistrar.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@
2121
import com.google.firebase.components.Component;
2222
import com.google.firebase.components.ComponentRegistrar;
2323
import com.google.firebase.components.Dependency;
24-
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
24+
import com.google.firebase.heartbeatinfo.HeartBeatController;
2525
import com.google.firebase.platforminfo.LibraryVersionComponent;
26-
import com.google.firebase.platforminfo.UserAgentPublisher;
2726
import java.util.Arrays;
2827
import java.util.List;
2928

@@ -41,14 +40,12 @@ public List<Component<?>> getComponents() {
4140
return Arrays.asList(
4241
Component.builder(FirebaseAppCheck.class, (InternalAppCheckTokenProvider.class))
4342
.add(Dependency.required(FirebaseApp.class))
44-
.add(Dependency.optionalProvider(UserAgentPublisher.class))
45-
.add(Dependency.optionalProvider(HeartBeatInfo.class))
43+
.add(Dependency.optionalProvider(HeartBeatController.class))
4644
.factory(
4745
(container) ->
4846
new DefaultFirebaseAppCheck(
4947
container.get(FirebaseApp.class),
50-
container.getProvider(UserAgentPublisher.class),
51-
container.getProvider(HeartBeatInfo.class)))
48+
container.getProvider(HeartBeatController.class)))
5249
.alwaysEager()
5350
.build(),
5451
LibraryVersionComponent.create("fire-app-check", BuildConfig.VERSION_NAME));

appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/DefaultFirebaseAppCheck.java

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@
3131
import com.google.firebase.appcheck.FirebaseAppCheck;
3232
import com.google.firebase.appcheck.internal.util.Clock;
3333
import com.google.firebase.appcheck.interop.AppCheckTokenListener;
34-
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
34+
import com.google.firebase.heartbeatinfo.HeartBeatController;
3535
import com.google.firebase.inject.Provider;
36-
import com.google.firebase.platforminfo.UserAgentPublisher;
3736
import java.util.ArrayList;
3837
import java.util.List;
3938

@@ -42,8 +41,7 @@ public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
4241
private static final long BUFFER_TIME_MILLIS = 5 * 60 * 1000; // 5 minutes in milliseconds
4342

4443
private final FirebaseApp firebaseApp;
45-
private final Provider<UserAgentPublisher> userAgentPublisherProvider;
46-
private final Provider<HeartBeatInfo> heartBeatInfoProvider;
44+
private final Provider<HeartBeatController> heartbeatControllerProvider;
4745
private final List<AppCheckTokenListener> appCheckTokenListenerList;
4846
private final List<AppCheckListener> appCheckListenerList;
4947
private final StorageHelper storageHelper;
@@ -56,14 +54,11 @@ public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
5654

5755
public DefaultFirebaseAppCheck(
5856
@NonNull FirebaseApp firebaseApp,
59-
@NonNull Provider<UserAgentPublisher> userAgentPublisherProvider,
60-
@NonNull Provider<HeartBeatInfo> heartBeatInfoProvider) {
57+
@NonNull Provider<HeartBeatController> heartBeatController) {
6158
checkNotNull(firebaseApp);
62-
checkNotNull(userAgentPublisherProvider);
63-
checkNotNull(heartBeatInfoProvider);
59+
checkNotNull(heartBeatController);
6460
this.firebaseApp = firebaseApp;
65-
this.userAgentPublisherProvider = userAgentPublisherProvider;
66-
this.heartBeatInfoProvider = heartBeatInfoProvider;
61+
this.heartbeatControllerProvider = heartBeatController;
6762
this.appCheckTokenListenerList = new ArrayList<>();
6863
this.appCheckListenerList = new ArrayList<>();
6964
this.storageHelper =
@@ -217,13 +212,8 @@ public Task<AppCheckToken> then(@NonNull Task<AppCheckToken> task) {
217212
}
218213

219214
@NonNull
220-
Provider<UserAgentPublisher> getUserAgentPublisherProvider() {
221-
return userAgentPublisherProvider;
222-
}
223-
224-
@NonNull
225-
Provider<HeartBeatInfo> getHeartBeatInfoProvider() {
226-
return heartBeatInfoProvider;
215+
Provider<HeartBeatController> getHeartbeatControllerProvider() {
216+
return heartbeatControllerProvider;
227217
}
228218

229219
/** Sets the in-memory cached {@link AppCheckToken}. */

appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,13 @@
2424
import androidx.annotation.VisibleForTesting;
2525
import com.google.android.gms.common.util.AndroidUtilsLight;
2626
import com.google.android.gms.common.util.Hex;
27+
import com.google.android.gms.tasks.Tasks;
2728
import com.google.firebase.FirebaseApp;
2829
import com.google.firebase.FirebaseException;
2930
import com.google.firebase.FirebaseOptions;
3031
import com.google.firebase.appcheck.FirebaseAppCheck;
31-
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
32-
import com.google.firebase.heartbeatinfo.HeartBeatInfo.HeartBeat;
32+
import com.google.firebase.heartbeatinfo.HeartBeatController;
3333
import com.google.firebase.inject.Provider;
34-
import com.google.firebase.platforminfo.UserAgentPublisher;
3534
import java.io.BufferedOutputStream;
3635
import java.io.BufferedReader;
3736
import java.io.IOException;
@@ -60,17 +59,14 @@ public class NetworkClient {
6059
private static final String APPLICATION_JSON = "application/json";
6160
private static final String UTF_8 = "UTF-8";
6261
@VisibleForTesting static final String X_FIREBASE_CLIENT = "X-Firebase-Client";
63-
@VisibleForTesting static final String X_FIREBASE_CLIENT_LOG_TYPE = "X-Firebase-Client-Log-Type";
6462
@VisibleForTesting static final String X_ANDROID_PACKAGE = "X-Android-Package";
6563
@VisibleForTesting static final String X_ANDROID_CERT = "X-Android-Cert";
66-
private static final String HEART_BEAT_STORAGE_TAG = "fire-app-check";
6764

6865
private final Context context;
6966
private final String apiKey;
7067
private final String appId;
7168
private final String projectId;
72-
private final Provider<UserAgentPublisher> userAgentPublisherProvider;
73-
private final Provider<HeartBeatInfo> heartBeatInfoProvider;
69+
private final Provider<HeartBeatController> heartBeatControllerProvider;
7470

7571
@Retention(RetentionPolicy.SOURCE)
7672
@IntDef({UNKNOWN, SAFETY_NET, DEBUG})
@@ -85,30 +81,25 @@ public NetworkClient(@NonNull FirebaseApp firebaseApp) {
8581
firebaseApp.getApplicationContext(),
8682
firebaseApp.getOptions(),
8783
((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance(firebaseApp))
88-
.getUserAgentPublisherProvider(),
89-
((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance(firebaseApp))
90-
.getHeartBeatInfoProvider());
84+
.getHeartbeatControllerProvider());
9185
}
9286

9387
@VisibleForTesting
9488
NetworkClient(
9589
@NonNull Context context,
9690
@NonNull FirebaseOptions firebaseOptions,
97-
@NonNull Provider<UserAgentPublisher> userAgentPublisherProvider,
98-
@NonNull Provider<HeartBeatInfo> heartBeatInfoProvider) {
91+
@NonNull Provider<HeartBeatController> heartBeatControllerProvider) {
9992
checkNotNull(context);
10093
checkNotNull(firebaseOptions);
101-
checkNotNull(userAgentPublisherProvider);
102-
checkNotNull(heartBeatInfoProvider);
94+
checkNotNull(heartBeatControllerProvider);
10395
this.context = context;
10496
this.apiKey = firebaseOptions.getApiKey();
10597
this.appId = firebaseOptions.getApplicationId();
10698
this.projectId = firebaseOptions.getProjectId();
10799
if (projectId == null) {
108100
throw new IllegalArgumentException("FirebaseOptions#getProjectId cannot be null.");
109101
}
110-
this.userAgentPublisherProvider = userAgentPublisherProvider;
111-
this.heartBeatInfoProvider = heartBeatInfoProvider;
102+
this.heartBeatControllerProvider = heartBeatControllerProvider;
112103
}
113104

114105
/**
@@ -131,17 +122,9 @@ public AppCheckTokenResponse exchangeAttestationForAppCheckToken(
131122
urlConnection.setDoOutput(true);
132123
urlConnection.setFixedLengthStreamingMode(requestBytes.length);
133124
urlConnection.setRequestProperty(CONTENT_TYPE, APPLICATION_JSON);
134-
String userAgent = getUserAgent();
135-
if (userAgent != null) {
136-
urlConnection.setRequestProperty(X_FIREBASE_CLIENT, userAgent);
137-
}
138-
139-
// getHeartbeatCode should not be called multiple times, as subsequent calls will return
140-
// HeartBeat.NONE until the heartbeat is reset.
141-
HeartBeat heartBeat = getHeartbeatCode();
142-
if (heartBeat != HeartBeat.NONE) {
143-
urlConnection.setRequestProperty(
144-
X_FIREBASE_CLIENT_LOG_TYPE, Integer.toString(heartBeat.getCode()));
125+
String heartBeatHeader = getHeartBeat();
126+
if (heartBeatHeader != null) {
127+
urlConnection.setRequestProperty(X_FIREBASE_CLIENT, heartBeatHeader);
145128
}
146129

147130
// Headers for Android API key restrictions.
@@ -183,18 +166,20 @@ public AppCheckTokenResponse exchangeAttestationForAppCheckToken(
183166
}
184167
}
185168

186-
private String getUserAgent() {
187-
return userAgentPublisherProvider.get() != null
188-
? userAgentPublisherProvider.get().getUserAgent()
189-
: null;
190-
}
191-
192-
private HeartBeat getHeartbeatCode() {
193-
return heartBeatInfoProvider.get() != null
194-
? heartBeatInfoProvider.get().getHeartBeatCode(HEART_BEAT_STORAGE_TAG)
195-
: HeartBeat.NONE;
169+
@VisibleForTesting
170+
String getHeartBeat() {
171+
HeartBeatController controller = heartBeatControllerProvider.get();
172+
if (controller != null) {
173+
try {
174+
return Tasks.await(controller.getHeartBeatsHeader());
175+
} catch (Exception e) {
176+
Log.w(TAG, "Unable to get heartbeats!");
177+
return null;
178+
}
179+
} else {
180+
return null;
181+
}
196182
}
197-
198183
/** Gets the Android package's SHA-1 fingerprint. */
199184
private String getFingerprintHashForPackage() {
200185
byte[] hash;

appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/FirebaseAppCheckRegistrarTest.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
import com.google.firebase.FirebaseApp;
2020
import com.google.firebase.components.Component;
2121
import com.google.firebase.components.Dependency;
22-
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
23-
import com.google.firebase.platforminfo.UserAgentPublisher;
22+
import com.google.firebase.heartbeatinfo.HeartBeatController;
2423
import java.util.List;
2524
import org.junit.Test;
2625
import org.junit.runner.RunWith;
@@ -40,8 +39,7 @@ public void testGetComponents() {
4039
assertThat(firebaseAppCheckComponent.getDependencies())
4140
.containsExactly(
4241
Dependency.required(FirebaseApp.class),
43-
Dependency.optionalProvider(UserAgentPublisher.class),
44-
Dependency.optionalProvider(HeartBeatInfo.class));
42+
Dependency.optionalProvider(HeartBeatController.class));
4543
assertThat(firebaseAppCheckComponent.isAlwaysEager()).isTrue();
4644
}
4745
}

appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultFirebaseAppCheckTest.java

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131
import com.google.firebase.appcheck.AppCheckTokenResult;
3232
import com.google.firebase.appcheck.FirebaseAppCheck.AppCheckListener;
3333
import com.google.firebase.appcheck.interop.AppCheckTokenListener;
34-
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
35-
import com.google.firebase.platforminfo.UserAgentPublisher;
34+
import com.google.firebase.heartbeatinfo.HeartBeatController;
3635
import org.junit.Before;
3736
import org.junit.Test;
3837
import org.junit.runner.RunWith;
@@ -58,8 +57,7 @@ public class DefaultFirebaseAppCheckTest {
5857
@Mock private AppCheckProvider mockAppCheckProvider;
5958
@Mock private AppCheckTokenListener mockAppCheckTokenListener;
6059
@Mock private AppCheckListener mockAppCheckListener;
61-
@Mock private UserAgentPublisher mockUserAgentPublisher;
62-
@Mock private HeartBeatInfo mockHeartBeatInfo;
60+
@Mock private HeartBeatController mockHeartBeatController;
6361

6462
private DefaultAppCheckToken validDefaultAppCheckToken;
6563
private DefaultFirebaseAppCheck defaultFirebaseAppCheck;
@@ -76,34 +74,24 @@ public void setup() {
7674
when(mockAppCheckProvider.getToken()).thenReturn(Tasks.forResult(validDefaultAppCheckToken));
7775

7876
defaultFirebaseAppCheck =
79-
new DefaultFirebaseAppCheck(
80-
mockFirebaseApp, () -> mockUserAgentPublisher, () -> mockHeartBeatInfo);
77+
new DefaultFirebaseAppCheck(mockFirebaseApp, () -> mockHeartBeatController);
8178
}
8279

8380
@Test
8481
public void testConstructor_nullFirebaseApp_expectThrows() {
8582
assertThrows(
8683
NullPointerException.class,
8784
() -> {
88-
new DefaultFirebaseAppCheck(null, () -> mockUserAgentPublisher, () -> mockHeartBeatInfo);
85+
new DefaultFirebaseAppCheck(null, () -> mockHeartBeatController);
8986
});
9087
}
9188

9289
@Test
93-
public void testConstructor_nullUserAgentPublisherProvider_expectThrows() {
90+
public void testConstructor_nullHeartBeatControllerProvider_expectThrows() {
9491
assertThrows(
9592
NullPointerException.class,
9693
() -> {
97-
new DefaultFirebaseAppCheck(mockFirebaseApp, null, () -> mockHeartBeatInfo);
98-
});
99-
}
100-
101-
@Test
102-
public void testConstructor_nullHeartBeatInfoProvider_expectThrows() {
103-
assertThrows(
104-
NullPointerException.class,
105-
() -> {
106-
new DefaultFirebaseAppCheck(mockFirebaseApp, () -> mockUserAgentPublisher, null);
94+
new DefaultFirebaseAppCheck(mockFirebaseApp, null);
10795
});
10896
}
10997

0 commit comments

Comments
 (0)