Skip to content

Commit 3633bc8

Browse files
Merge branch 'master' into fad_audit
2 parents d30d7c2 + dd1c336 commit 3633bc8

File tree

111 files changed

+2368
-1676
lines changed

Some content is hidden

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

111 files changed

+2368
-1676
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@ AppDistributionReleaseInternal getNewReleaseFromClient(
119119
firebaseAppDistributionTesterApiClient.fetchNewRelease(
120120
fid, appId, apiKey, authToken, firebaseApp.getApplicationContext());
121121

122+
if (retrievedNewRelease == null) {
123+
LogWrapper.getInstance().v(TAG + "Tester does not have access to any releases");
124+
return null;
125+
}
126+
122127
if (!canInstall(retrievedNewRelease)) {
123128
LogWrapper.getInstance().v(TAG + "New release has lower version code than current release");
124129
return null;

firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistribution.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -346,19 +346,11 @@ private UpdateTaskImpl showUpdateAlertDialog(AppDistributionRelease newRelease)
346346
updateDialog.setButton(
347347
AlertDialog.BUTTON_NEGATIVE,
348348
context.getString(R.string.update_no_button),
349-
(dialogInterface, i) -> {
350-
dialogInterface.dismiss();
351-
synchronized (updateIfNewReleaseTaskLock) {
352-
postProgressToCachedUpdateIfNewReleaseTask(
353-
UpdateProgress.builder()
354-
.setApkFileTotalBytes(UNKNOWN_RELEASE_FILE_SIZE)
355-
.setApkBytesDownloaded(UNKNOWN_RELEASE_FILE_SIZE)
356-
.setUpdateStatus(UpdateStatus.UPDATE_CANCELED)
357-
.build());
358-
setCachedUpdateIfNewReleaseCompletionError(
359-
new FirebaseAppDistributionException(
360-
ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED));
361-
}
349+
(dialogInterface, i) -> dismissUpdateDialogCallback());
350+
351+
updateDialog.setOnCancelListener(
352+
dialogInterface -> {
353+
dismissUpdateDialogCallback();
362354
});
363355

364356
updateDialog.show();
@@ -368,6 +360,20 @@ private UpdateTaskImpl showUpdateAlertDialog(AppDistributionRelease newRelease)
368360
}
369361
}
370362

363+
private void dismissUpdateDialogCallback() {
364+
synchronized (updateIfNewReleaseTaskLock) {
365+
postProgressToCachedUpdateIfNewReleaseTask(
366+
UpdateProgress.builder()
367+
.setApkFileTotalBytes(UNKNOWN_RELEASE_FILE_SIZE)
368+
.setApkBytesDownloaded(UNKNOWN_RELEASE_FILE_SIZE)
369+
.setUpdateStatus(UpdateStatus.UPDATE_CANCELED)
370+
.build());
371+
setCachedUpdateIfNewReleaseCompletionError(
372+
new FirebaseAppDistributionException(
373+
ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED));
374+
}
375+
}
376+
371377
private void setCachedUpdateIfNewReleaseCompletionError(FirebaseAppDistributionException e) {
372378
synchronized (updateIfNewReleaseTaskLock) {
373379
safeSetTaskException(cachedUpdateIfNewReleaseTask, e);

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

Lines changed: 86 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,23 @@
1414

1515
package com.google.firebase.app.distribution;
1616

17-
import static com.google.firebase.app.distribution.FirebaseAppDistributionException.Status.AUTHENTICATION_FAILURE;
18-
import static com.google.firebase.app.distribution.FirebaseAppDistributionException.Status.NETWORK_FAILURE;
19-
2017
import android.content.Context;
2118
import android.content.pm.PackageManager;
2219
import androidx.annotation.NonNull;
20+
import androidx.annotation.Nullable;
2321
import com.google.android.gms.common.util.AndroidUtilsLight;
2422
import com.google.android.gms.common.util.Hex;
23+
import com.google.firebase.app.distribution.Constants.ErrorMessages;
24+
import com.google.firebase.app.distribution.FirebaseAppDistributionException.Status;
2525
import java.io.BufferedInputStream;
2626
import java.io.ByteArrayOutputStream;
2727
import java.io.IOException;
2828
import java.io.InputStream;
2929
import java.net.MalformedURLException;
30+
import java.net.ProtocolException;
3031
import java.net.URL;
3132
import javax.net.ssl.HttpsURLConnection;
33+
import org.json.JSONArray;
3234
import org.json.JSONException;
3335
import org.json.JSONObject;
3436

@@ -55,26 +57,42 @@ class FirebaseAppDistributionTesterApiClient {
5557

5658
public static final int DEFAULT_BUFFER_SIZE = 8192;
5759

58-
public @NonNull AppDistributionReleaseInternal fetchNewRelease(
60+
/**
61+
* Fetches and returns the lastest release for the app that the tester has access to, or null if
62+
* the tester doesn't have access to any releases.
63+
*/
64+
@Nullable
65+
public AppDistributionReleaseInternal fetchNewRelease(
5966
@NonNull String fid,
6067
@NonNull String appId,
6168
@NonNull String apiKey,
6269
@NonNull String authToken,
6370
@NonNull Context context)
6471
throws FirebaseAppDistributionException {
72+
HttpsURLConnection connection = openHttpsUrlConnection(appId, fid, apiKey, authToken, context);
73+
String responseBody;
74+
try (BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream())) {
75+
responseBody = convertInputStreamToString(inputStream);
76+
} catch (IOException e) {
77+
throw getExceptionForHttpResponse(connection, e);
78+
} finally {
79+
connection.disconnect();
80+
}
81+
return parseNewRelease(responseBody);
82+
}
6583

66-
AppDistributionReleaseInternal newRelease;
67-
HttpsURLConnection connection = openHttpsUrlConnection(appId, fid);
84+
AppDistributionReleaseInternal parseNewRelease(String responseBody)
85+
throws FirebaseAppDistributionException {
6886
try {
69-
connection.setRequestMethod(REQUEST_METHOD);
70-
connection.setRequestProperty(API_KEY_HEADER, apiKey);
71-
connection.setRequestProperty(INSTALLATION_AUTH_HEADER, authToken);
72-
connection.addRequestProperty(X_ANDROID_PACKAGE_HEADER_KEY, context.getPackageName());
73-
connection.addRequestProperty(
74-
X_ANDROID_CERT_HEADER_KEY, getFingerprintHashForPackage(context));
75-
76-
InputStream inputStream = connection.getInputStream();
77-
JSONObject newReleaseJson = readFetchReleaseInputStream(inputStream);
87+
JSONObject responseJson = new JSONObject(responseBody);
88+
if (!responseJson.has("releases")) {
89+
return null;
90+
}
91+
JSONArray releasesJson = responseJson.getJSONArray("releases");
92+
if (releasesJson.length() == 0) {
93+
return null;
94+
}
95+
JSONObject newReleaseJson = releasesJson.getJSONObject(0);
7896
final String displayVersion = newReleaseJson.getString(DISPLAY_VERSION_JSON_KEY);
7997
final String buildVersion = newReleaseJson.getString(BUILD_VERSION_JSON_KEY);
8098
String releaseNotes = tryGetValue(newReleaseJson, RELEASE_NOTES_JSON_KEY);
@@ -88,7 +106,7 @@ class FirebaseAppDistributionTesterApiClient {
88106
? BinaryType.APK
89107
: BinaryType.AAB;
90108

91-
newRelease =
109+
AppDistributionReleaseInternal newRelease =
92110
AppDistributionReleaseInternal.builder()
93111
.setDisplayVersion(displayVersion)
94112
.setBuildVersion(buildVersion)
@@ -99,48 +117,50 @@ class FirebaseAppDistributionTesterApiClient {
99117
.setApkHash(apkHash)
100118
.setDownloadUrl(downloadUrl)
101119
.build();
102-
inputStream.close();
103120

104-
} catch (IOException | JSONException e) {
105-
if (e instanceof JSONException) {
106-
LogWrapper.getInstance().e(TAG + "Error parsing the new release.", e);
107-
throw new FirebaseAppDistributionException(
108-
Constants.ErrorMessages.JSON_PARSING_ERROR, NETWORK_FAILURE, e);
109-
}
110-
throw getExceptionForHttpResponse(connection);
111-
} finally {
112-
connection.disconnect();
121+
LogWrapper.getInstance().v("Zip hash for the new release " + newRelease.getApkHash());
122+
return newRelease;
123+
} catch (JSONException e) {
124+
LogWrapper.getInstance().e(TAG + "Error parsing the new release.", e);
125+
throw new FirebaseAppDistributionException(
126+
ErrorMessages.JSON_PARSING_ERROR, Status.UNKNOWN, e);
113127
}
114-
LogWrapper.getInstance().v("Zip hash for the new release " + newRelease.getApkHash());
115-
return newRelease;
116128
}
117129

118130
private FirebaseAppDistributionException getExceptionForHttpResponse(
119-
HttpsURLConnection connection) {
131+
HttpsURLConnection connection, Exception cause) {
132+
// TODO(lkellogg): this try-catch should be unnecessary because it will only throw an
133+
// IOException here if we couldn't connect to the server, in which case getInputStream() would
134+
// have already failed with the same exception. We also weirdly have to choose one of the two
135+
// thrown exceptions to set as the cause. We can avoid this by checking the response code
136+
// first, and then catching any unexpected exceptions when reading the input stream, essentially
137+
// combining the "default" case below with this try-catch.
138+
int responseCode;
120139
try {
121-
LogWrapper.getInstance().e(TAG + "Failed due to " + connection.getResponseCode());
122-
switch (connection.getResponseCode()) {
123-
case 401:
124-
return new FirebaseAppDistributionException(
125-
Constants.ErrorMessages.AUTHENTICATION_ERROR, AUTHENTICATION_FAILURE);
126-
case 403:
127-
case 400:
128-
return new FirebaseAppDistributionException(
129-
Constants.ErrorMessages.AUTHORIZATION_ERROR, AUTHENTICATION_FAILURE);
130-
case 404:
131-
return new FirebaseAppDistributionException(
132-
Constants.ErrorMessages.NOT_FOUND_ERROR, AUTHENTICATION_FAILURE);
133-
case 408:
134-
case 504:
135-
return new FirebaseAppDistributionException(
136-
Constants.ErrorMessages.TIMEOUT_ERROR, NETWORK_FAILURE);
137-
default:
138-
return new FirebaseAppDistributionException(
139-
Constants.ErrorMessages.NETWORK_ERROR, NETWORK_FAILURE);
140-
}
141-
} catch (IOException ex) {
140+
responseCode = connection.getResponseCode();
141+
} catch (IOException e) {
142142
return new FirebaseAppDistributionException(
143-
Constants.ErrorMessages.NETWORK_ERROR, NETWORK_FAILURE, ex);
143+
ErrorMessages.NETWORK_ERROR, Status.NETWORK_FAILURE, e);
144+
}
145+
LogWrapper.getInstance().e(TAG + "Failed due to " + responseCode);
146+
switch (responseCode) {
147+
case 401:
148+
return new FirebaseAppDistributionException(
149+
ErrorMessages.AUTHENTICATION_ERROR, Status.AUTHENTICATION_FAILURE, cause);
150+
case 403:
151+
case 400:
152+
return new FirebaseAppDistributionException(
153+
ErrorMessages.AUTHORIZATION_ERROR, Status.AUTHENTICATION_FAILURE, cause);
154+
case 404:
155+
return new FirebaseAppDistributionException(
156+
ErrorMessages.NOT_FOUND_ERROR, Status.AUTHENTICATION_FAILURE, cause);
157+
case 408:
158+
case 504:
159+
return new FirebaseAppDistributionException(
160+
ErrorMessages.TIMEOUT_ERROR, Status.NETWORK_FAILURE, cause);
161+
default:
162+
return new FirebaseAppDistributionException(
163+
ErrorMessages.UNKNOWN_ERROR, Status.UNKNOWN, cause);
144164
}
145165
}
146166

@@ -152,33 +172,27 @@ private String tryGetValue(JSONObject jsonObject, String key) {
152172
}
153173
}
154174

155-
private JSONObject readFetchReleaseInputStream(InputStream in)
156-
throws FirebaseAppDistributionException, IOException {
157-
JSONObject newRelease;
158-
InputStream jsonIn = new BufferedInputStream(in);
159-
String result = convertInputStreamToString(jsonIn);
160-
try {
161-
JSONObject json = new JSONObject(result);
162-
newRelease = json.getJSONArray("releases").getJSONObject(0);
163-
} catch (JSONException e) {
164-
throw new FirebaseAppDistributionException(
165-
Constants.ErrorMessages.JSON_PARSING_ERROR,
166-
FirebaseAppDistributionException.Status.UNKNOWN,
167-
e);
168-
}
169-
return newRelease;
170-
}
171-
172-
HttpsURLConnection openHttpsUrlConnection(String appId, String fid)
175+
HttpsURLConnection openHttpsUrlConnection(
176+
String appId, String fid, String apiKey, String authToken, Context context)
173177
throws FirebaseAppDistributionException {
174178
HttpsURLConnection httpsURLConnection;
175179
URL url = getReleasesEndpointUrl(appId, fid);
176180
try {
177181
httpsURLConnection = (HttpsURLConnection) url.openConnection();
178182
} catch (IOException e) {
179183
throw new FirebaseAppDistributionException(
180-
Constants.ErrorMessages.NETWORK_ERROR, NETWORK_FAILURE, e);
184+
ErrorMessages.NETWORK_ERROR, Status.NETWORK_FAILURE, e);
181185
}
186+
try {
187+
httpsURLConnection.setRequestMethod(REQUEST_METHOD);
188+
} catch (ProtocolException e) {
189+
throw new FirebaseAppDistributionException(ErrorMessages.UNKNOWN_ERROR, Status.UNKNOWN, e);
190+
}
191+
httpsURLConnection.setRequestProperty(API_KEY_HEADER, apiKey);
192+
httpsURLConnection.setRequestProperty(INSTALLATION_AUTH_HEADER, authToken);
193+
httpsURLConnection.addRequestProperty(X_ANDROID_PACKAGE_HEADER_KEY, context.getPackageName());
194+
httpsURLConnection.addRequestProperty(
195+
X_ANDROID_CERT_HEADER_KEY, getFingerprintHashForPackage(context));
182196
return httpsURLConnection;
183197
}
184198

@@ -188,9 +202,7 @@ private URL getReleasesEndpointUrl(String appId, String fid)
188202
return new URL(String.format(RELEASE_ENDPOINT_URL_FORMAT, appId, fid));
189203
} catch (MalformedURLException e) {
190204
throw new FirebaseAppDistributionException(
191-
Constants.ErrorMessages.UNKNOWN_ERROR,
192-
FirebaseAppDistributionException.Status.UNKNOWN,
193-
e);
205+
ErrorMessages.UNKNOWN_ERROR, FirebaseAppDistributionException.Status.UNKNOWN, e);
194206
}
195207
}
196208

@@ -204,11 +216,7 @@ private static String convertInputStreamToString(InputStream is) throws IOExcept
204216
return result.toString();
205217
}
206218

207-
/**
208-
* Gets the Android package's SHA-1 fingerprint.
209-
*
210-
* @param context
211-
*/
219+
/** Gets the Android package's SHA-1 fingerprint. */
212220
private String getFingerprintHashForPackage(Context context) {
213221
byte[] hash;
214222

firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/InstallActivity.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,17 @@ private void showUnknownSourcesUi() {
9595
alertDialog.setButton(
9696
AlertDialog.BUTTON_NEGATIVE,
9797
getString(R.string.update_no_button),
98-
(dialogInterface, i) -> {
99-
LogWrapper.getInstance().v(TAG + "Unknown sources dialog cancelled");
100-
dialogInterface.dismiss();
101-
finish();
102-
});
98+
(dialogInterface, i) -> dismissUnknownSourcesDialogCallback());
99+
alertDialog.setOnCancelListener(dialogInterface -> dismissUnknownSourcesDialogCallback());
103100

104101
alertDialog.show();
105102
}
106103

104+
private void dismissUnknownSourcesDialogCallback() {
105+
LogWrapper.getInstance().v(TAG + "Unknown sources dialog cancelled");
106+
finish();
107+
}
108+
107109
private Intent getUnknownSourcesIntent() {
108110
Intent intent;
109111
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

0 commit comments

Comments
 (0)