Skip to content

Commit 5a1ae17

Browse files
FAD reintroduce terminal states to UpdateStatus (#2892)
* Add terminal states in UpdateStatus, update APK tests * Remove Installed terminal state, remove use of class variables for totalbytes and bytesdownloaded * Remove unnecessary addOnProgressListener from test * Add setCachedUpdateTask for testing
1 parent 5533fca commit 5a1ae17

File tree

3 files changed

+80
-63
lines changed

3 files changed

+80
-63
lines changed

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

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414

1515
package com.google.firebase.appdistribution;
1616

17+
import static com.google.firebase.appdistribution.FirebaseAppDistributionException.Status;
1718
import static com.google.firebase.appdistribution.FirebaseAppDistributionException.Status.NETWORK_FAILURE;
1819

1920
import android.app.Activity;
2021
import android.content.Context;
2122
import android.content.Intent;
2223
import android.util.Log;
2324
import androidx.annotation.NonNull;
25+
import androidx.annotation.RestrictTo;
2426
import androidx.annotation.VisibleForTesting;
2527
import com.google.android.gms.tasks.CancellationTokenSource;
2628
import com.google.android.gms.tasks.Task;
@@ -46,6 +48,7 @@ class UpdateApkClient {
4648
private final Executor downloadExecutor;
4749
private TaskCompletionSource<Void> installTaskCompletionSource;
4850
private final FirebaseApp firebaseApp;
51+
private UpdateTaskImpl cachedUpdateTask;
4952

5053
public UpdateApkClient(@NonNull FirebaseApp firebaseApp) {
5154
this.downloadExecutor = Executors.newSingleThreadExecutor();
@@ -57,51 +60,47 @@ public void updateApk(
5760
@NonNull String downloadUrl,
5861
@NonNull Activity currentActivity) {
5962

60-
downloadApk(downloadUrl, updateTask)
63+
this.cachedUpdateTask = updateTask;
64+
downloadApk(downloadUrl)
6165
.addOnSuccessListener(
6266
downloadExecutor,
6367
file ->
6468
install(file.getPath(), currentActivity)
65-
.addOnSuccessListener(
66-
unused -> {
67-
postUpdateProgress(
68-
updateTask, file.length(), file.length(), UpdateStatus.DOWNLOADED);
69-
updateTask.setResult();
70-
})
7169
.addOnFailureListener(
72-
e ->
73-
setTaskCompletionErrorWithDefault(
74-
updateTask,
75-
e,
76-
new FirebaseAppDistributionException(
77-
Constants.ErrorMessages.NETWORK_ERROR,
78-
FirebaseAppDistributionException.Status.INSTALLATION_FAILURE))))
70+
e -> {
71+
postInstallationFailure(e, file.length());
72+
setTaskCompletionErrorWithDefault(
73+
e,
74+
new FirebaseAppDistributionException(
75+
Constants.ErrorMessages.NETWORK_ERROR,
76+
FirebaseAppDistributionException.Status.INSTALLATION_FAILURE));
77+
}))
7978
.addOnFailureListener(
8079
downloadExecutor,
81-
e ->
82-
setTaskCompletionErrorWithDefault(
83-
updateTask,
84-
e,
85-
new FirebaseAppDistributionException(
86-
Constants.ErrorMessages.NETWORK_ERROR,
87-
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE)));
80+
e -> {
81+
setTaskCompletionErrorWithDefault(
82+
e,
83+
new FirebaseAppDistributionException(
84+
Constants.ErrorMessages.NETWORK_ERROR,
85+
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
86+
});
8887
}
8988

9089
@VisibleForTesting
9190
@NonNull
92-
Task<File> downloadApk(@NonNull String downloadUrl, UpdateTaskImpl updateTask) {
91+
Task<File> downloadApk(@NonNull String downloadUrl) {
9392
if (downloadTaskCompletionSource != null
9493
&& !downloadTaskCompletionSource.getTask().isComplete()) {
9594
return downloadTaskCompletionSource.getTask();
9695
}
9796

9897
downloadTaskCompletionSource = new TaskCompletionSource<>();
9998

100-
makeApkDownloadRequest(downloadUrl, updateTask);
99+
makeApkDownloadRequest(downloadUrl);
101100
return downloadTaskCompletionSource.getTask();
102101
}
103102

104-
private void makeApkDownloadRequest(@NonNull String downloadUrl, UpdateTaskImpl updateTask) {
103+
private void makeApkDownloadRequest(@NonNull String downloadUrl) {
105104
downloadExecutor.execute(
106105
() -> {
107106
try {
@@ -114,9 +113,9 @@ private void makeApkDownloadRequest(@NonNull String downloadUrl, UpdateTaskImpl
114113
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
115114
} else {
116115
long responseLength = connection.getContentLength();
117-
postUpdateProgress(updateTask, responseLength, 0, UpdateStatus.PENDING);
116+
postUpdateProgress(responseLength, 0, UpdateStatus.PENDING);
118117
String fileName = getApplicationName() + ".apk";
119-
downloadToDisk(connection.getInputStream(), responseLength, fileName, updateTask);
118+
downloadToDisk(connection.getInputStream(), responseLength, fileName);
120119
}
121120
} catch (IOException | FirebaseAppDistributionException e) {
122121
setDownloadTaskCompletionErrorWithDefault(
@@ -128,36 +127,34 @@ private void makeApkDownloadRequest(@NonNull String downloadUrl, UpdateTaskImpl
128127
});
129128
}
130129

131-
private void downloadToDisk(
132-
InputStream input, long totalSize, String fileName, UpdateTaskImpl updateTask) {
130+
private void downloadToDisk(InputStream input, long totalSize, String fileName) {
133131

134132
File apkFile = getApkFileForApp(fileName);
135133
apkFile.delete();
134+
long bytesDownloaded = 0;
136135
try (BufferedOutputStream outputStream =
137136
new BufferedOutputStream(
138137
firebaseApp.getApplicationContext().openFileOutput(fileName, Context.MODE_PRIVATE))) {
139138

140139
byte[] data = new byte[8 * 1024];
141140
int readSize = input.read(data);
142-
long downloadedSize = 0;
143141
long lastMsUpdated = 0;
144142

145143
while (readSize != -1) {
146144
outputStream.write(data, 0, readSize);
147-
downloadedSize += readSize;
145+
bytesDownloaded += readSize;
148146
readSize = input.read(data);
149147

150148
// update progress logic for onProgressListener
151149
long currentTimeMs = System.currentTimeMillis();
152150
if (currentTimeMs - lastMsUpdated > UPDATE_INTERVAL_MS) {
153151
lastMsUpdated = currentTimeMs;
154-
postUpdateProgress(updateTask, totalSize, downloadedSize, UpdateStatus.DOWNLOADING);
152+
postUpdateProgress(totalSize, bytesDownloaded, UpdateStatus.DOWNLOADING);
155153
}
156154
}
157-
// completion
158-
postUpdateProgress(updateTask, totalSize, downloadedSize, UpdateStatus.DOWNLOADED);
159155

160156
} catch (IOException e) {
157+
postUpdateProgress(totalSize, bytesDownloaded, UpdateStatus.DOWNLOAD_FAILED);
161158
setDownloadTaskCompletionError(
162159
new FirebaseAppDistributionException(
163160
Constants.ErrorMessages.NETWORK_ERROR,
@@ -168,12 +165,16 @@ private void downloadToDisk(
168165
// check that file is actual JAR
169166
new JarFile(apkFile);
170167
} catch (Exception e) {
168+
postUpdateProgress(totalSize, bytesDownloaded, UpdateStatus.DOWNLOAD_FAILED);
171169
setDownloadTaskCompletionError(
172170
new FirebaseAppDistributionException(
173171
Constants.ErrorMessages.NETWORK_ERROR,
174172
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
175173
}
176174

175+
// completion
176+
postUpdateProgress(totalSize, totalSize, UpdateStatus.DOWNLOADED);
177+
177178
downloadTaskCompletionSource.setResult(apkFile);
178179
}
179180

@@ -235,6 +236,7 @@ private Task<Void> install(String path, Activity currentActivity) {
235236
void setInstallationResult(int resultCode) {
236237
if (resultCode == Activity.RESULT_OK) {
237238
installTaskCompletionSource.setResult(null);
239+
cachedUpdateTask.setResult();
238240
} else if (resultCode == Activity.RESULT_CANCELED) {
239241
installTaskCompletionSource.setException(
240242
new FirebaseAppDistributionException(
@@ -248,31 +250,41 @@ void setInstallationResult(int resultCode) {
248250
}
249251
}
250252

251-
private void setTaskCompletionError(
252-
UpdateTaskImpl updateTask, FirebaseAppDistributionException e) {
253-
if (updateTask != null && !updateTask.isComplete()) {
254-
updateTask.setException(e);
253+
private void setTaskCompletionError(FirebaseAppDistributionException e) {
254+
if (cachedUpdateTask != null && !cachedUpdateTask.isComplete()) {
255+
cachedUpdateTask.setException(e);
255256
}
256257
}
257258

258259
private void setTaskCompletionErrorWithDefault(
259-
UpdateTaskImpl updateTask,
260-
Exception e,
261-
FirebaseAppDistributionException defaultFirebaseException) {
260+
Exception e, FirebaseAppDistributionException defaultFirebaseException) {
262261
if (e instanceof FirebaseAppDistributionException) {
263-
setTaskCompletionError(updateTask, (FirebaseAppDistributionException) e);
262+
setTaskCompletionError((FirebaseAppDistributionException) e);
264263
} else {
265-
setTaskCompletionError(updateTask, defaultFirebaseException);
264+
setTaskCompletionError(defaultFirebaseException);
266265
}
267266
}
268267

269-
private void postUpdateProgress(
270-
UpdateTaskImpl updateTask, long totalBytes, long downloadedBytes, UpdateStatus status) {
271-
updateTask.updateProgress(
268+
private void postUpdateProgress(long totalBytes, long downloadedBytes, UpdateStatus status) {
269+
cachedUpdateTask.updateProgress(
272270
UpdateProgress.builder()
273271
.setApkFileTotalBytes(totalBytes)
274272
.setApkBytesDownloaded(downloadedBytes)
275273
.setUpdateStatus(status)
276274
.build());
277275
}
276+
277+
private void postInstallationFailure(Exception e, long fileLength) {
278+
if (e instanceof FirebaseAppDistributionException
279+
&& ((FirebaseAppDistributionException) e).getErrorCode() == Status.INSTALLATION_CANCELED) {
280+
postUpdateProgress(fileLength, fileLength, UpdateStatus.INSTALL_CANCELED);
281+
} else {
282+
postUpdateProgress(fileLength, fileLength, UpdateStatus.INSTALL_FAILED);
283+
}
284+
}
285+
286+
@RestrictTo(RestrictTo.Scope.TESTS)
287+
void setCachedUpdateTask(UpdateTaskImpl updateTask) {
288+
this.cachedUpdateTask = updateTask;
289+
}
278290
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ public enum UpdateStatus {
2525
/** Download completed */
2626
DOWNLOADED,
2727

28+
/** Download failed */
29+
DOWNLOAD_FAILED,
30+
31+
/** Installation canceled */
32+
INSTALL_CANCELED,
33+
34+
/** Installation failed */
35+
INSTALL_FAILED,
36+
2837
/** AAB flow (directed to Play) */
2938
REDIRECTED_TO_PLAY,
3039
}

firebase-app-distribution/src/test/java/com/google/firebase/appdistribution/UpdateApkClientTest.java

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import java.io.File;
3131
import java.util.ArrayList;
3232
import java.util.List;
33-
import java.util.concurrent.CountDownLatch;
3433
import javax.net.ssl.HttpsURLConnection;
3534
import org.junit.Before;
3635
import org.junit.Test;
@@ -57,7 +56,6 @@ public class UpdateApkClientTest {
5756
private UpdateApkClient updateApkClient;
5857
private TestActivity activity;
5958
private ShadowActivity shadowActivity;
60-
private CountDownLatch latch;
6159
@Mock private File mockFile;
6260
@Mock private HttpsURLConnection mockHttpsUrlConnection;
6361

@@ -97,6 +95,7 @@ public void updateApk_whenDownloadFails_setsNetworkError() throws Exception {
9795
updateApkClient.updateApk(updateTask, TEST_URL, activity);
9896
// wait for error to be caught and set
9997
Thread.sleep(1000);
98+
10099
assertFalse(updateTask.isSuccessful());
101100
assertTrue(updateTask.getException() instanceof FirebaseAppDistributionException);
102101
FirebaseAppDistributionException e =
@@ -107,36 +106,30 @@ public void updateApk_whenDownloadFails_setsNetworkError() throws Exception {
107106

108107
@Test
109108
public void updateApk_whenInstallSuccessful_setsResult() throws Exception {
110-
List<UpdateProgress> progressEvents = new ArrayList<>();
111109
UpdateTaskImpl updateTask = new UpdateTaskImpl();
112-
doReturn(Tasks.forResult(mockFile)).when(updateApkClient).downloadApk(TEST_URL, updateTask);
113-
updateTask.addOnProgressListener(progressEvents::add);
110+
doReturn(Tasks.forResult(mockFile)).when(updateApkClient).downloadApk(TEST_URL);
114111

115112
updateApkClient.updateApk(updateTask, TEST_URL, activity);
116113
// sleep to wait for installTaskCompletionSource to be set
117114
Thread.sleep(1000);
118115
updateApkClient.setInstallationResult(RESULT_OK);
119-
assertEquals(1, progressEvents.size());
120-
assertEquals(
121-
UpdateProgress.builder()
122-
.setApkBytesDownloaded(TEST_FILE_LENGTH)
123-
.setApkFileTotalBytes(TEST_FILE_LENGTH)
124-
.setUpdateStatus(UpdateStatus.DOWNLOADED)
125-
.build(),
126-
progressEvents.get(0));
127116
assertTrue(updateTask.isSuccessful());
128117
}
129118

130119
@Test
131120
public void updateApk_whenInstallCancelled_setsError() throws Exception {
121+
List<UpdateProgress> progressEvents = new ArrayList<>();
132122
UpdateTaskImpl updateTask = new UpdateTaskImpl();
133-
doReturn(Tasks.forResult(mockFile)).when(updateApkClient).downloadApk(TEST_URL, updateTask);
123+
updateTask.addOnProgressListener(progressEvents::add);
124+
doReturn(Tasks.forResult(mockFile)).when(updateApkClient).downloadApk(TEST_URL);
134125

135126
updateApkClient.updateApk(updateTask, TEST_URL, activity);
136127
// sleep to wait for installTaskCompletionSource to be set
137128
Thread.sleep(1000);
138129
updateApkClient.setInstallationResult(RESULT_CANCELED);
139130

131+
assertEquals(1, progressEvents.size());
132+
assertEquals(UpdateStatus.INSTALL_CANCELED, progressEvents.get(0).getUpdateStatus());
140133
assertFalse(updateTask.isSuccessful());
141134
assertTrue(updateTask.getException() instanceof FirebaseAppDistributionException);
142135
FirebaseAppDistributionException e =
@@ -147,14 +140,18 @@ public void updateApk_whenInstallCancelled_setsError() throws Exception {
147140

148141
@Test
149142
public void updateApk_whenInstallFailed_setsError() throws Exception {
143+
List<UpdateProgress> progressEvents = new ArrayList<>();
150144
UpdateTaskImpl updateTask = new UpdateTaskImpl();
151-
doReturn(Tasks.forResult(mockFile)).when(updateApkClient).downloadApk(TEST_URL, updateTask);
145+
updateTask.addOnProgressListener(progressEvents::add);
146+
doReturn(Tasks.forResult(mockFile)).when(updateApkClient).downloadApk(TEST_URL);
152147

153148
updateApkClient.updateApk(updateTask, TEST_URL, activity);
154149
// sleep to wait for installTaskCompletionSource to be set
155150
Thread.sleep(1000);
156151
updateApkClient.setInstallationResult(RESULT_FAILED);
157152

153+
assertEquals(1, progressEvents.size());
154+
assertEquals(UpdateStatus.INSTALL_FAILED, progressEvents.get(0).getUpdateStatus());
158155
assertFalse(updateTask.isSuccessful());
159156
assertTrue(updateTask.getException() instanceof FirebaseAppDistributionException);
160157
FirebaseAppDistributionException e =
@@ -165,9 +162,8 @@ public void updateApk_whenInstallFailed_setsError() throws Exception {
165162

166163
@Test
167164
public void downloadApk_whenCalledMultipleTimes_returnsSameTask() {
168-
UpdateTaskImpl updateTask = new UpdateTaskImpl();
169-
Task<File> task1 = updateApkClient.downloadApk(TEST_URL, updateTask);
170-
Task<File> task2 = updateApkClient.downloadApk(TEST_URL, updateTask);
165+
Task<File> task1 = updateApkClient.downloadApk(TEST_URL);
166+
Task<File> task2 = updateApkClient.downloadApk(TEST_URL);
171167
assertEquals(task1, task2);
172168
}
173169
}

0 commit comments

Comments
 (0)