14
14
15
15
package com .google .firebase .app .distribution ;
16
16
17
+ import static com .google .firebase .app .distribution .FirebaseAppDistributionException .Status .DOWNLOAD_FAILURE ;
17
18
import static com .google .firebase .app .distribution .FirebaseAppDistributionException .Status .NETWORK_FAILURE ;
18
19
import static com .google .firebase .app .distribution .TaskUtils .safeSetTaskException ;
19
20
23
24
import androidx .annotation .GuardedBy ;
24
25
import androidx .annotation .NonNull ;
25
26
import androidx .annotation .VisibleForTesting ;
27
+ import com .google .firebase .app .distribution .Constants .ErrorMessages ;
26
28
import com .google .firebase .app .distribution .internal .InstallActivity ;
27
29
import com .google .firebase .app .distribution .internal .LogWrapper ;
28
30
import com .google .firebase .app .distribution .internal .SignInResultActivity ;
29
31
import java .io .IOException ;
30
- import java .net . URL ;
32
+ import java .util . concurrent . Executor ;
31
33
import java .util .concurrent .Executors ;
32
34
import javax .net .ssl .HttpsURLConnection ;
33
35
@@ -36,6 +38,8 @@ class AabUpdater {
36
38
private static final String TAG = "UpdateAabClient:" ;
37
39
38
40
private final FirebaseAppDistributionLifecycleNotifier lifecycleNotifier ;
41
+ private final HttpsUrlConnectionFactory httpsUrlConnectionFactory ;
42
+ private final Executor executor ;
39
43
40
44
@ GuardedBy ("updateAabLock" )
41
45
private UpdateTaskImpl cachedUpdateTask ;
@@ -46,12 +50,20 @@ class AabUpdater {
46
50
private final Object updateAabLock = new Object ();
47
51
48
52
AabUpdater () {
49
- this (FirebaseAppDistributionLifecycleNotifier .getInstance ());
53
+ this (
54
+ FirebaseAppDistributionLifecycleNotifier .getInstance (),
55
+ new HttpsUrlConnectionFactory (),
56
+ Executors .newSingleThreadExecutor ());
50
57
}
51
58
52
- AabUpdater (@ NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier ) {
59
+ AabUpdater (
60
+ @ NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier ,
61
+ @ NonNull HttpsUrlConnectionFactory httpsUrlConnectionFactory ,
62
+ @ NonNull Executor executor ) {
53
63
this .lifecycleNotifier = lifecycleNotifier ;
64
+ this .httpsUrlConnectionFactory = httpsUrlConnectionFactory ;
54
65
lifecycleNotifier .addOnActivityStartedListener (this ::onActivityStarted );
66
+ this .executor = executor ;
55
67
}
56
68
57
69
@ VisibleForTesting
@@ -78,77 +90,78 @@ UpdateTaskImpl updateAab(@NonNull AppDistributionReleaseInternal newRelease) {
78
90
}
79
91
}
80
92
81
- HttpsURLConnection openHttpsUrlConnection (String downloadUrl )
93
+ private String fetchDownloadRedirectUrl (String downloadUrl )
82
94
throws FirebaseAppDistributionException {
83
95
HttpsURLConnection httpsURLConnection ;
96
+ int responseCode ;
84
97
85
98
try {
86
- URL url = new URL (downloadUrl );
87
- httpsURLConnection = (HttpsURLConnection ) url .openConnection ();
99
+ httpsURLConnection = httpsUrlConnectionFactory .openConnection (downloadUrl );
100
+ httpsURLConnection .setInstanceFollowRedirects (false );
101
+ responseCode = httpsURLConnection .getResponseCode ();
88
102
} catch (IOException e ) {
89
103
throw new FirebaseAppDistributionException (
90
- Constants . ErrorMessages . NETWORK_ERROR , NETWORK_FAILURE , e );
104
+ "Failed to open connection to: " + downloadUrl , NETWORK_FAILURE , e );
91
105
}
92
- return httpsURLConnection ;
106
+
107
+ if (!isRedirectResponse (responseCode )) {
108
+ throw new FirebaseAppDistributionException (
109
+ "Expected redirect response code, but got: " + responseCode , DOWNLOAD_FAILURE );
110
+ }
111
+ String redirect = httpsURLConnection .getHeaderField ("Location" );
112
+ httpsURLConnection .disconnect ();
113
+
114
+ if (redirect == null ) {
115
+ throw new FirebaseAppDistributionException (
116
+ "No Location header found in response from: " + downloadUrl , DOWNLOAD_FAILURE );
117
+ } else if (redirect .isEmpty ()) {
118
+ throw new FirebaseAppDistributionException (
119
+ "Empty Location header found in response from: " + downloadUrl , DOWNLOAD_FAILURE );
120
+ }
121
+
122
+ return redirect ;
123
+ }
124
+
125
+ private static boolean isRedirectResponse (int responseCode ) {
126
+ return responseCode >= 300 && responseCode < 400 ;
93
127
}
94
128
95
129
private void redirectToPlayForAabUpdate (String downloadUrl ) {
96
130
synchronized (updateAabLock ) {
97
131
if (lifecycleNotifier .getCurrentActivity () == null ) {
98
132
safeSetTaskException (
99
133
cachedUpdateTask ,
100
- new FirebaseAppDistributionException (
101
- Constants .ErrorMessages .APP_BACKGROUNDED ,
102
- FirebaseAppDistributionException .Status .DOWNLOAD_FAILURE ));
134
+ new FirebaseAppDistributionException (ErrorMessages .APP_BACKGROUNDED , DOWNLOAD_FAILURE ));
103
135
return ;
104
136
}
105
137
}
106
138
107
139
// The 302 redirect is obtained here to open the play store directly and avoid opening chrome
108
- Executors .newSingleThreadExecutor ()
109
- .execute ( // Execute the network calls on a background thread
110
- () -> {
111
- HttpsURLConnection connection ;
112
- String redirect ;
113
- try {
114
- connection = openHttpsUrlConnection (downloadUrl );
115
-
116
- // To get url to play without redirect we do this connection
117
- connection .setInstanceFollowRedirects (false );
118
- redirect = connection .getHeaderField ("Location" );
119
- connection .disconnect ();
120
- connection .getInputStream ().close ();
121
- } catch (FirebaseAppDistributionException | IOException e ) {
122
- setUpdateTaskCompletionErrorWithDefault (
123
- e ,
124
- new FirebaseAppDistributionException (
125
- Constants .ErrorMessages .NETWORK_ERROR ,
126
- FirebaseAppDistributionException .Status .DOWNLOAD_FAILURE ));
127
- return ;
128
- }
129
-
130
- if (!redirect .isEmpty ()) {
131
- Intent updateIntent = new Intent (Intent .ACTION_VIEW );
132
- updateIntent .setData (Uri .parse (redirect ));
133
- updateIntent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
134
- LogWrapper .getInstance ().v (TAG + "Redirecting to play" );
135
-
136
- synchronized (updateAabLock ) {
137
- lifecycleNotifier .getCurrentActivity ().startActivity (updateIntent );
138
- cachedUpdateTask .updateProgress (
139
- UpdateProgress .builder ()
140
- .setApkBytesDownloaded (-1 )
141
- .setApkFileTotalBytes (-1 )
142
- .setUpdateStatus (UpdateStatus .REDIRECTED_TO_PLAY )
143
- .build ());
144
- }
145
- } else {
146
- setUpdateTaskCompletionError (
147
- new FirebaseAppDistributionException (
148
- Constants .ErrorMessages .NETWORK_ERROR ,
149
- FirebaseAppDistributionException .Status .DOWNLOAD_FAILURE ));
150
- }
151
- });
140
+ executor .execute ( // Execute the network calls on a background thread
141
+ () -> {
142
+ String redirectUrl ;
143
+ try {
144
+ redirectUrl = fetchDownloadRedirectUrl (downloadUrl );
145
+ } catch (FirebaseAppDistributionException e ) {
146
+ setUpdateTaskCompletionError (e );
147
+ return ;
148
+ }
149
+
150
+ Intent updateIntent = new Intent (Intent .ACTION_VIEW );
151
+ updateIntent .setData (Uri .parse (redirectUrl ));
152
+ updateIntent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
153
+ LogWrapper .getInstance ().v (TAG + "Redirecting to play" );
154
+
155
+ synchronized (updateAabLock ) {
156
+ lifecycleNotifier .getCurrentActivity ().startActivity (updateIntent );
157
+ cachedUpdateTask .updateProgress (
158
+ UpdateProgress .builder ()
159
+ .setApkBytesDownloaded (-1 )
160
+ .setApkFileTotalBytes (-1 )
161
+ .setUpdateStatus (UpdateStatus .REDIRECTED_TO_PLAY )
162
+ .build ());
163
+ }
164
+ });
152
165
}
153
166
154
167
private void setUpdateTaskCompletionError (FirebaseAppDistributionException e ) {
@@ -157,21 +170,12 @@ private void setUpdateTaskCompletionError(FirebaseAppDistributionException e) {
157
170
}
158
171
}
159
172
160
- private void setUpdateTaskCompletionErrorWithDefault (
161
- Exception e , FirebaseAppDistributionException defaultFirebaseException ) {
162
- if (e instanceof FirebaseAppDistributionException ) {
163
- setUpdateTaskCompletionError ((FirebaseAppDistributionException ) e );
164
- } else {
165
- setUpdateTaskCompletionError (defaultFirebaseException );
166
- }
167
- }
168
-
169
173
void tryCancelAabUpdateTask () {
170
174
synchronized (updateAabLock ) {
171
175
safeSetTaskException (
172
176
cachedUpdateTask ,
173
177
new FirebaseAppDistributionException (
174
- Constants . ErrorMessages .UPDATE_CANCELED ,
178
+ ErrorMessages .UPDATE_CANCELED ,
175
179
FirebaseAppDistributionException .Status .INSTALLATION_CANCELED ,
176
180
ReleaseUtils .convertToAppDistributionRelease (aabReleaseInProgress )));
177
181
}
0 commit comments