15
15
import java .io .InputStream ;
16
16
import java .io .IOException ;
17
17
import java .io .OutputStream ;
18
+ import java .lang .Math ;
18
19
import java .net .HttpURLConnection ;
19
20
import java .net .URI ;
20
21
import java .net .URISyntaxException ;
21
22
import java .net .URL ;
22
23
import java .util .Date ;
23
24
import java .util .concurrent .CancellationException ;
24
25
import java .util .concurrent .ExecutionException ;
26
+ import java .util .concurrent .locks .Lock ;
27
+ import java .util .concurrent .locks .ReentrantLock ;
25
28
26
29
public final class ResourceUpdater {
27
30
private static final String TAG = "ResourceUpdater" ;
@@ -58,20 +61,44 @@ enum InstallMode {
58
61
IMMEDIATE
59
62
}
60
63
64
+ /// Lock that prevents replacement of the install file by the downloader
65
+ /// while this file is being extracted, since these can happen in parallel.
66
+ Lock getInstallationLock () {
67
+ return installationLock ;
68
+ }
69
+
70
+ // Patch file that's fully installed and is ready to serve assets.
71
+ // This file represents the final stage in the installation process.
72
+ public File getInstalledPatch () {
73
+ return new File (context .getFilesDir ().toString () + "/patch.zip" );
74
+ }
75
+
76
+ // Patch file that's finished downloading and is ready to be installed.
77
+ // This is a separate file in order to prevent serving assets from patch
78
+ // that failed installing for any reason, such as mismatched APK version.
79
+ File getDownloadedPatch () {
80
+ return new File (getInstalledPatch ().getPath () + ".install" );
81
+ }
82
+
61
83
private class DownloadTask extends AsyncTask <String , String , Void > {
62
84
@ Override
63
85
protected Void doInBackground (String ... unused ) {
64
86
try {
65
87
URL unresolvedURL = new URL (buildUpdateDownloadURL ());
66
- File localFile = getPatch ();
88
+
89
+ // Download to transient file to avoid extracting incomplete download.
90
+ File localFile = new File (getInstalledPatch ().getPath () + ".download" );
67
91
68
92
long startMillis = new Date ().getTime ();
69
93
Log .i (TAG , "Checking for updates at " + unresolvedURL );
70
94
71
95
HttpURLConnection connection =
72
96
(HttpURLConnection )unresolvedURL .openConnection ();
73
97
74
- long lastDownloadTime = localFile .lastModified ();
98
+ long lastDownloadTime = Math .max (
99
+ getDownloadedPatch ().lastModified (),
100
+ getInstalledPatch ().lastModified ());
101
+
75
102
if (lastDownloadTime != 0 ) {
76
103
Log .i (TAG , "Active update timestamp " + lastDownloadTime );
77
104
connection .setIfModifiedSince (lastDownloadTime );
@@ -107,9 +134,29 @@ protected Void doInBackground(String... unused) {
107
134
108
135
long totalMillis = new Date ().getTime () - startMillis ;
109
136
Log .i (TAG , "Update downloaded in " + totalMillis / 100 / 10. + "s" );
137
+ }
138
+ }
139
+
140
+ // Wait renaming the file if extraction is in progress.
141
+ installationLock .lock ();
142
+
143
+ try {
144
+ File updateFile = getDownloadedPatch ();
110
145
146
+ // Graduate downloaded file as ready for installation.
147
+ if (updateFile .exists () && !updateFile .delete ()) {
148
+ Log .w (TAG , "Could not delete file " + updateFile );
149
+ return null ;
150
+ }
151
+ if (!localFile .renameTo (updateFile )) {
152
+ Log .w (TAG , "Could not create file " + updateFile );
111
153
return null ;
112
154
}
155
+
156
+ return null ;
157
+
158
+ } finally {
159
+ installationLock .unlock ();
113
160
}
114
161
115
162
} catch (IOException e ) {
@@ -121,6 +168,7 @@ protected Void doInBackground(String... unused) {
121
168
122
169
private final Context context ;
123
170
private DownloadTask downloadTask ;
171
+ private final Lock installationLock = new ReentrantLock ();
124
172
125
173
public ResourceUpdater (Context context ) {
126
174
this .context = context ;
@@ -137,10 +185,6 @@ private String getAPKVersion() {
137
185
}
138
186
}
139
187
140
- public File getPatch () {
141
- return new File (context .getFilesDir ().toString () + "/patch.zip" );
142
- }
143
-
144
188
private String buildUpdateDownloadURL () {
145
189
Bundle metaData ;
146
190
try {
0 commit comments