Skip to content

Commit 48342ab

Browse files
committed
Merge branch 'master' into wuandy/SquashBasedOnSebastianDraft
# Conflicts: # firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/DeleteMutation.java # firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/Mutation.java # firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/SetMutation.java # firebase-firestore/src/test/java/com/google/firebase/firestore/model/mutation/MutationTest.java
2 parents 58c6b77 + cf7680d commit 48342ab

File tree

59 files changed

+1691
-366
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1691
-366
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Whenever a new Firebase Android SDK is released, this workflow triggers
2+
# *another* workflow on the Firebase C++ SDK, which will check for the Android
3+
# version update and create a PR updating its dependencies if any version
4+
# numbers changed.
5+
name: update-cpp-sdk-on-release
6+
on:
7+
push:
8+
branches:
9+
# Change the below line if the main branch is renamed.
10+
- 'master'
11+
paths:
12+
# Only run this if a gradle.properties file is touched.
13+
- '**/gradle.properties'
14+
15+
jobs:
16+
check_if_version_changed:
17+
name: Check if released version changed
18+
# This step checks several things, and sets released_version_changed=1 only if all are true:
19+
# - The push must target the main branch.
20+
# - The push must modify a gradle.properties file.
21+
# - The push must change a "latestReleasedVersion=" line in a gradle.properties file.
22+
runs-on: ubuntu-latest
23+
outputs:
24+
released_version_changed: ${{ steps.check_version.outputs.released_version_changed }}
25+
steps:
26+
- uses: actions/[email protected]
27+
with:
28+
# Check out the actual head commit, not any merge commit.
29+
ref: ${{ github.sha }}
30+
# Specify fetch-depth so we can query the log, the default is a shallow clone.
31+
fetch-depth: 0
32+
- name: Check if version was updated in git history
33+
id: check_version
34+
run: |
35+
# Query the git history for all gradle.properties files changed by this push.
36+
# Then, check the diff to see if any "latestReleasedVersion=" lines changed.
37+
if (git diff '${{ github.event.before }}' -- '**/gradle.properties' | grep -q '^[-+]latestReleasedVersion='); then
38+
echo "::set-output name=released_version_changed::1"
39+
else
40+
echo "No change to latestReleasedVersion detected since ${{ github.event.before }}"
41+
fi
42+
43+
trigger_cpp_sdk_update:
44+
name: Trigger C++ SDK update
45+
# If the previous step set the released_version_changed output param to 1, then
46+
# we should trigger the C++ SDK to update its Android dependencies.
47+
needs: check_if_version_changed
48+
if: ${{ needs.check_if_version_changed.outputs.released_version_changed }}
49+
# Fetch an authentication token for firebase-workflow-trigger, then use that
50+
# token to trigger the update-dependencies workflow in firebase-cpp-sdk.
51+
runs-on: ubuntu-latest
52+
steps:
53+
- name: Setup python
54+
uses: actions/setup-python@v2
55+
with:
56+
python-version: 3.7
57+
58+
- name: Check out firebase-cpp-sdk
59+
uses: actions/[email protected]
60+
with:
61+
repository: firebase/firebase-cpp-sdk
62+
ref: main
63+
64+
- name: Get firebase-workflow-trigger token
65+
uses: tibdex/github-app-token@v1
66+
id: generate-token
67+
with:
68+
app_id: ${{ secrets.CPP_WORKFLOW_TRIGGER_APP_ID }}
69+
private_key: ${{ secrets.CPP_WORKFLOW_TRIGGER_APP_PRIVATE_KEY }}
70+
repository: firebase/firebase-cpp-sdk
71+
72+
- name: Trigger firebase-cpp-sdk update
73+
run: |
74+
python scripts/gha/trigger_workflow.py -t ${{ steps.generate-token.outputs.token }} -w update-dependencies.yml -p updateAndroid 1 -p updateiOS 0 -p comment "[Triggered]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID) by [firebase-android-sdk $(date '+%b %d') release]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/commit/${{ github.sha }})." -s 10 -A

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@
1616

1717
import static com.google.android.gms.common.internal.Preconditions.checkNotNull;
1818

19+
import android.content.Context;
20+
import android.content.pm.PackageManager.NameNotFoundException;
21+
import android.util.Log;
1922
import androidx.annotation.IntDef;
2023
import androidx.annotation.NonNull;
2124
import androidx.annotation.VisibleForTesting;
25+
import com.google.android.gms.common.util.AndroidUtilsLight;
26+
import com.google.android.gms.common.util.Hex;
2227
import com.google.firebase.FirebaseApp;
2328
import com.google.firebase.FirebaseException;
2429
import com.google.firebase.appcheck.FirebaseAppCheck;
@@ -40,6 +45,8 @@
4045
*/
4146
public class NetworkClient {
4247

48+
private static final String TAG = NetworkClient.class.getName();
49+
4350
private static final String SAFETY_NET_EXCHANGE_URL_TEMPLATE =
4451
"https://firebaseappcheck.googleapis.com/v1beta/projects/%s/apps/%s:exchangeSafetyNetToken?key=%s";
4552
private static final String DEBUG_EXCHANGE_URL_TEMPLATE =
@@ -49,7 +56,10 @@ public class NetworkClient {
4956
private static final String UTF_8 = "UTF-8";
5057
@VisibleForTesting static final String X_FIREBASE_CLIENT = "X-Firebase-Client";
5158
@VisibleForTesting static final String X_FIREBASE_CLIENT_LOG_TYPE = "X-Firebase-Client-Log-Type";
59+
@VisibleForTesting static final String X_ANDROID_PACKAGE = "X-Android-Package";
60+
@VisibleForTesting static final String X_ANDROID_CERT = "X-Android-Cert";
5261

62+
private final Context context;
5363
private final DefaultFirebaseAppCheck firebaseAppCheck;
5464
private final String apiKey;
5565
private final String appId;
@@ -65,6 +75,7 @@ public class NetworkClient {
6575

6676
public NetworkClient(@NonNull FirebaseApp firebaseApp) {
6777
checkNotNull(firebaseApp);
78+
this.context = firebaseApp.getApplicationContext();
6879
this.firebaseAppCheck = (DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance(firebaseApp);
6980
this.apiKey = firebaseApp.getOptions().getApiKey();
7081
this.appId = firebaseApp.getOptions().getApplicationId();
@@ -97,6 +108,10 @@ public AppCheckTokenResponse exchangeAttestationForAppCheckToken(
97108
X_FIREBASE_CLIENT_LOG_TYPE, firebaseAppCheck.getHeartbeatCode());
98109
}
99110

111+
// Headers for Android API key restrictions.
112+
urlConnection.setRequestProperty(X_ANDROID_PACKAGE, context.getPackageName());
113+
urlConnection.setRequestProperty(X_ANDROID_CERT, getFingerprintHashForPackage());
114+
100115
try (OutputStream out =
101116
new BufferedOutputStream(urlConnection.getOutputStream(), requestBytes.length)) {
102117
out.write(requestBytes, /* off= */ 0, requestBytes.length);
@@ -130,6 +145,23 @@ public AppCheckTokenResponse exchangeAttestationForAppCheckToken(
130145
}
131146
}
132147

148+
/** Gets the Android package's SHA-1 fingerprint. */
149+
private String getFingerprintHashForPackage() {
150+
byte[] hash;
151+
152+
try {
153+
hash = AndroidUtilsLight.getPackageCertificateHashBytes(context, context.getPackageName());
154+
if (hash == null) {
155+
Log.e(TAG, "Could not get fingerprint hash for package: " + context.getPackageName());
156+
return null;
157+
}
158+
return Hex.bytesToStringUppercase(hash, /* zeroTerminated= */ false);
159+
} catch (NameNotFoundException e) {
160+
Log.e(TAG, "No such package: " + context.getPackageName(), e);
161+
return null;
162+
}
163+
}
164+
133165
private static String getUrlTemplate(@AttestationTokenType int tokenType) {
134166
switch (tokenType) {
135167
case SAFETY_NET:

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

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
import static com.google.firebase.appcheck.internal.HttpErrorResponse.CODE_KEY;
2121
import static com.google.firebase.appcheck.internal.HttpErrorResponse.ERROR_KEY;
2222
import static com.google.firebase.appcheck.internal.HttpErrorResponse.MESSAGE_KEY;
23+
import static com.google.firebase.appcheck.internal.NetworkClient.X_ANDROID_CERT;
24+
import static com.google.firebase.appcheck.internal.NetworkClient.X_ANDROID_PACKAGE;
2325
import static com.google.firebase.appcheck.internal.NetworkClient.X_FIREBASE_CLIENT;
2426
import static com.google.firebase.appcheck.internal.NetworkClient.X_FIREBASE_CLIENT_LOG_TYPE;
2527
import static org.junit.Assert.assertThrows;
2628
import static org.mockito.ArgumentMatchers.any;
29+
import static org.mockito.ArgumentMatchers.eq;
2730
import static org.mockito.Mockito.doReturn;
2831
import static org.mockito.Mockito.spy;
2932
import static org.mockito.Mockito.verify;
@@ -116,13 +119,7 @@ public void exchangeSafetyNetToken_successResponse_returnsAppCheckTokenResponse(
116119
verify(networkClient).createHttpUrlConnection(expectedUrl);
117120
verify(outputStream)
118121
.write(JSON_REQUEST.getBytes(), /* off= */ 0, JSON_REQUEST.getBytes().length);
119-
120-
String userAgent = ((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getUserAgent();
121-
String heartBeatCode =
122-
((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getHeartbeatCode();
123-
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT, userAgent);
124-
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT_LOG_TYPE, heartBeatCode);
125-
assertThat(userAgent).contains(SDK_NAME);
122+
verifyRequestHeaders();
126123
}
127124

128125
@Test
@@ -146,13 +143,7 @@ public void exchangeSafetyNetToken_errorResponse_throwsException() throws Except
146143
verify(networkClient).createHttpUrlConnection(expectedUrl);
147144
verify(outputStream)
148145
.write(JSON_REQUEST.getBytes(), /* off= */ 0, JSON_REQUEST.getBytes().length);
149-
150-
String userAgent = ((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getUserAgent();
151-
String heartBeatCode =
152-
((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getHeartbeatCode();
153-
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT, userAgent);
154-
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT_LOG_TYPE, heartBeatCode);
155-
assertThat(userAgent).contains(SDK_NAME);
146+
verifyRequestHeaders();
156147
}
157148

158149
@Test
@@ -174,13 +165,7 @@ public void exchangeDebugToken_successResponse_returnsAppCheckTokenResponse() th
174165
verify(networkClient).createHttpUrlConnection(expectedUrl);
175166
verify(outputStream)
176167
.write(JSON_REQUEST.getBytes(), /* off= */ 0, JSON_REQUEST.getBytes().length);
177-
178-
String userAgent = ((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getUserAgent();
179-
String heartBeatCode =
180-
((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getHeartbeatCode();
181-
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT, userAgent);
182-
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT_LOG_TYPE, heartBeatCode);
183-
assertThat(userAgent).contains(SDK_NAME);
168+
verifyRequestHeaders();
184169
}
185170

186171
@Test
@@ -204,13 +189,7 @@ public void exchangeDebugToken_errorResponse_throwsException() throws Exception
204189
verify(networkClient).createHttpUrlConnection(expectedUrl);
205190
verify(outputStream)
206191
.write(JSON_REQUEST.getBytes(), /* off= */ 0, JSON_REQUEST.getBytes().length);
207-
208-
String userAgent = ((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getUserAgent();
209-
String heartBeatCode =
210-
((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getHeartbeatCode();
211-
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT, userAgent);
212-
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT_LOG_TYPE, heartBeatCode);
213-
assertThat(userAgent).contains(SDK_NAME);
192+
verifyRequestHeaders();
214193
}
215194

216195
@Test
@@ -222,6 +201,20 @@ public void exchangeUnknownAttestation_throwsException() {
222201
JSON_REQUEST.getBytes(), NetworkClient.UNKNOWN));
223202
}
224203

204+
private void verifyRequestHeaders() {
205+
String userAgent = ((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getUserAgent();
206+
String heartBeatCode =
207+
((DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance()).getHeartbeatCode();
208+
209+
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT, userAgent);
210+
verify(mockHttpUrlConnection).setRequestProperty(X_FIREBASE_CLIENT_LOG_TYPE, heartBeatCode);
211+
verify(mockHttpUrlConnection)
212+
.setRequestProperty(
213+
X_ANDROID_PACKAGE, ApplicationProvider.getApplicationContext().getPackageName());
214+
verify(mockHttpUrlConnection).setRequestProperty(eq(X_ANDROID_CERT), any());
215+
assertThat(userAgent).contains(SDK_NAME);
216+
}
217+
225218
private static JSONObject createAttestationResponse() throws Exception {
226219
JSONObject responseBodyJson = new JSONObject();
227220
responseBodyJson.put(ATTESTATION_TOKEN_KEY, ATTESTATION_TOKEN);

firebase-app-distribution/src/main/AndroidManifest.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@
4343
</activity>
4444

4545
<provider
46-
android:name="androidx.core.content.FileProvider"
47-
android:authorities="${applicationId}.appdistro.fileprovider"
46+
android:name="com.google.firebase.appdistribution.FirebaseAppDistributionFileProvider"
47+
android:authorities="${applicationId}.FirebaseAppDistributionFileProvider"
4848
android:exported="false"
4949
android:grantUriPermissions="true">
5050
<meta-data
5151
android:name="android.support.FILE_PROVIDER_PATHS"
52-
android:resource="@xml/appdistro_provider_paths" />
52+
android:resource="@xml/firebase_app_distribution_provider_paths" />
5353
</provider>
5454
</application>
5555
</manifest>

firebase-app-distribution/src/main/java/com/google/firebase/appdistribution/CheckForNewReleaseClient.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ AppDistributionReleaseInternal getNewReleaseFromClient(
125125
throws FirebaseAppDistributionException {
126126
try {
127127
AppDistributionReleaseInternal retrievedNewRelease =
128-
firebaseAppDistributionTesterApiClient.fetchNewRelease(fid, appId, apiKey, authToken);
128+
firebaseAppDistributionTesterApiClient.fetchNewRelease(
129+
fid, appId, apiKey, authToken, firebaseApp.getApplicationContext());
129130

130131
if (isNewerBuildVersion(retrievedNewRelease) || !isInstalledRelease(retrievedNewRelease)) {
131132
return retrievedNewRelease;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.appdistribution;
16+
17+
import androidx.core.content.FileProvider;
18+
19+
public class FirebaseAppDistributionFileProvider extends FileProvider {}

firebase-app-distribution/src/main/java/com/google/firebase/appdistribution/FirebaseAppDistributionTesterApiClient.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@
1414

1515
package com.google.firebase.appdistribution;
1616

17+
import static android.content.ContentValues.TAG;
1718
import static com.google.firebase.appdistribution.FirebaseAppDistributionException.Status.AUTHENTICATION_FAILURE;
1819
import static com.google.firebase.appdistribution.FirebaseAppDistributionException.Status.NETWORK_FAILURE;
1920

21+
import android.content.Context;
22+
import android.content.pm.PackageManager;
23+
import android.util.Log;
2024
import androidx.annotation.NonNull;
25+
import com.google.android.gms.common.util.AndroidUtilsLight;
26+
import com.google.android.gms.common.util.Hex;
2127
import com.google.firebase.appdistribution.internal.AppDistributionReleaseInternal;
2228
import java.io.BufferedInputStream;
2329
import java.io.ByteArrayOutputStream;
@@ -36,18 +42,27 @@ class FirebaseAppDistributionTesterApiClient {
3642
private static final String REQUEST_METHOD = "GET";
3743
private static final String API_KEY_HEADER = "x-goog-api-key";
3844
private static final String INSTALLATION_AUTH_HEADER = "X-Goog-Firebase-Installations-Auth";
45+
private static final String X_ANDROID_PACKAGE_HEADER_KEY = "X-Android-Package";
46+
private static final String X_ANDROID_CERT_HEADER_KEY = "X-Android-Cert";
47+
3948
private static final String BUILD_VERSION_JSON_KEY = "buildVersion";
4049
private static final String DISPLAY_VERSION_JSON_KEY = "displayVersion";
4150
private static final String RELEASE_NOTES_JSON_KEY = "releaseNotes";
4251
private static final String BINARY_TYPE_JSON_KEY = "binaryType";
4352
private static final String CODE_HASH_KEY = "codeHash";
4453
private static final String IAS_ARTIFACT_ID_KEY = "iasArtifactId";
4554
private static final String DOWNLOAD_URL_KEY = "downloadUrl";
55+
4656
private static final String TAG = "TesterApiClient:";
57+
4758
public static final int DEFAULT_BUFFER_SIZE = 8192;
4859

4960
public @NonNull AppDistributionReleaseInternal fetchNewRelease(
50-
@NonNull String fid, @NonNull String appId, @NonNull String apiKey, @NonNull String authToken)
61+
@NonNull String fid,
62+
@NonNull String appId,
63+
@NonNull String apiKey,
64+
@NonNull String authToken,
65+
@NonNull Context context)
5166
throws FirebaseAppDistributionException {
5267

5368
AppDistributionReleaseInternal newRelease;
@@ -56,6 +71,9 @@ class FirebaseAppDistributionTesterApiClient {
5671
connection.setRequestMethod(REQUEST_METHOD);
5772
connection.setRequestProperty(API_KEY_HEADER, apiKey);
5873
connection.setRequestProperty(INSTALLATION_AUTH_HEADER, authToken);
74+
connection.addRequestProperty(X_ANDROID_PACKAGE_HEADER_KEY, context.getPackageName());
75+
connection.addRequestProperty(
76+
X_ANDROID_CERT_HEADER_KEY, getFingerprintHashForPackage(context));
5977

6078
InputStream inputStream = connection.getInputStream();
6179
JSONObject newReleaseJson = readFetchReleaseInputStream(inputStream);
@@ -185,4 +203,27 @@ private static String convertInputStreamToString(InputStream is) throws IOExcept
185203
}
186204
return result.toString();
187205
}
206+
207+
/**
208+
* Gets the Android package's SHA-1 fingerprint.
209+
*
210+
* @param context
211+
*/
212+
private String getFingerprintHashForPackage(Context context) {
213+
byte[] hash;
214+
215+
try {
216+
hash = AndroidUtilsLight.getPackageCertificateHashBytes(context, context.getPackageName());
217+
218+
if (hash == null) {
219+
Log.e(TAG, "Could not get fingerprint hash for package: " + context.getPackageName());
220+
return null;
221+
} else {
222+
return Hex.bytesToStringUppercase(hash, /* zeroTerminated= */ false);
223+
}
224+
} catch (PackageManager.NameNotFoundException e) {
225+
Log.e(TAG, "No such package: " + context.getPackageName(), e);
226+
return null;
227+
}
228+
}
188229
}

0 commit comments

Comments
 (0)