Skip to content

Commit ec78ab8

Browse files
authored
Implement background update option in getModel (#2254)
* Update in background * Adding more download type handling and unit tests. * Make internal files hidden * Clean up after merge. * Updates FirebaseMlLogEvent ModelOption name to Options to make internal proto. This allows for proper json to proto conversion and in local tests messages now reach the spanner queue.
1 parent 43f5aa2 commit ec78ab8

File tree

4 files changed

+445
-77
lines changed

4 files changed

+445
-77
lines changed

firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/FirebaseModelDownloader.java

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.google.firebase.ml.modeldownloader.internal.ModelFileDownloadService;
3030
import com.google.firebase.ml.modeldownloader.internal.ModelFileManager;
3131
import com.google.firebase.ml.modeldownloader.internal.SharedPreferencesUtil;
32+
import java.io.File;
3233
import java.util.Set;
3334
import java.util.concurrent.Executor;
3435
import java.util.concurrent.Executors;
@@ -120,47 +121,89 @@ public Task<CustomModel> getModel(
120121
@Nullable CustomModelDownloadConditions conditions)
121122
throws Exception {
122123
CustomModel localModel = sharedPreferencesUtil.getCustomModelDetails(modelName);
124+
if (localModel == null) {
125+
// no local model - get latest.
126+
return getCustomModelTask(modelName, conditions);
127+
}
128+
123129
switch (downloadType) {
124130
case LOCAL_MODEL:
125-
if (localModel != null) {
126-
return Tasks.forResult(localModel);
127-
}
128-
Task<CustomModel> modelDetails =
129-
modelDownloadService.getCustomModelDetails(
130-
firebaseOptions.getProjectId(), modelName, null);
131-
132-
// no local model - start download.
133-
return modelDetails.continueWithTask(
134-
executor,
135-
modelDetailTask -> {
136-
if (modelDetailTask.isSuccessful()) {
137-
// start download
138-
return fileDownloadService
139-
.download(modelDetailTask.getResult(), conditions)
140-
.continueWithTask(
141-
executor,
142-
downloadTask -> {
143-
if (downloadTask.isSuccessful()) {
144-
// read the updated model
145-
CustomModel downloadedModel =
146-
sharedPreferencesUtil.getCustomModelDetails(modelName);
147-
// TODO(annz) trigger file move here as well... right now it's temp
148-
// call loadNewlyDownloadedModelFile
149-
return Tasks.forResult(downloadedModel);
150-
}
151-
return Tasks.forException(new Exception("File download failed."));
152-
});
153-
}
154-
return Tasks.forException(modelDetailTask.getException());
155-
});
131+
return Tasks.forResult(localModel);
156132
case LATEST_MODEL:
157-
// check for latest model and download newest
158-
break;
133+
// check for latest model, wait for download if newer model exists
134+
return getCustomModelTask(modelName, conditions, localModel.getModelHash());
159135
case LOCAL_MODEL_UPDATE_IN_BACKGROUND:
160-
// start download in back ground return current model if not null.
161-
break;
136+
// start download in back ground, return local model
137+
getCustomModelTask(modelName, conditions, localModel.getModelHash());
138+
return Tasks.forResult(localModel);
162139
}
163-
throw new UnsupportedOperationException("Not yet implemented.");
140+
throw new IllegalArgumentException(
141+
"Unsupported downloadType, please chose LOCAL_MODEL, LATEST_MODEL, or LOCAL_MODEL_UPDATE_IN_BACKGROUND");
142+
}
143+
144+
// This version of getCustomModelTask will always call the modelDownloadService and upon
145+
// success will then trigger file download.
146+
private Task<CustomModel> getCustomModelTask(
147+
@NonNull String modelName, @Nullable CustomModelDownloadConditions conditions)
148+
throws Exception {
149+
return getCustomModelTask(modelName, conditions, null);
150+
}
151+
152+
// This version of getCustomModelTask will call the modelDownloadService and upon
153+
// success will only trigger file download, if there is a new model hash value.
154+
private Task<CustomModel> getCustomModelTask(
155+
@NonNull String modelName,
156+
@Nullable CustomModelDownloadConditions conditions,
157+
@Nullable String modelHash)
158+
throws Exception {
159+
Task<CustomModel> incomingModelDetails =
160+
modelDownloadService.getCustomModelDetails(
161+
firebaseOptions.getProjectId(), modelName, modelHash);
162+
163+
return incomingModelDetails.continueWithTask(
164+
executor,
165+
incomingModelDetailTask -> {
166+
if (incomingModelDetailTask.isSuccessful()) {
167+
CustomModel currentModel = sharedPreferencesUtil.getCustomModelDetails(modelName);
168+
// null means we have the latest model
169+
if (incomingModelDetailTask.getResult() == null) {
170+
return Tasks.forResult(currentModel);
171+
}
172+
173+
// if modelHash matches current local model just return local model.
174+
// Should be handled by above case but just in case.
175+
if (currentModel != null
176+
&& currentModel
177+
.getModelHash()
178+
.equals(incomingModelDetails.getResult().getModelHash())) {
179+
if (!currentModel.getLocalFilePath().isEmpty()
180+
&& new File(currentModel.getLocalFilePath()).exists()) {
181+
return Tasks.forResult(currentModel);
182+
}
183+
// todo(annzimmer) this shouldn't happen unless they are calling the sdk with multiple
184+
// sets of download types/conditions.
185+
// this should be a download in progress - add appropriate handling.
186+
}
187+
188+
// start download
189+
return fileDownloadService
190+
.download(incomingModelDetailTask.getResult(), conditions)
191+
.continueWithTask(
192+
executor,
193+
downloadTask -> {
194+
if (downloadTask.isSuccessful()) {
195+
// read the updated model
196+
CustomModel downloadedModel =
197+
sharedPreferencesUtil.getCustomModelDetails(modelName);
198+
// trigger the file to be moved to permanent location.
199+
fileDownloadService.loadNewlyDownloadedModelFile(downloadedModel);
200+
return Tasks.forResult(downloadedModel);
201+
}
202+
return Tasks.forException(new Exception("File download failed."));
203+
});
204+
}
205+
return Tasks.forException(incomingModelDetailTask.getException());
206+
});
164207
}
165208

166209
/**

firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/internal/ModelFileDownloadService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ public File loadNewlyDownloadedModelFile(CustomModel model) throws Exception {
299299
new CustomModel(
300300
model.getName(), model.getModelHash(), model.getSize(), 0, newModelFile.getPath()));
301301

302-
// Cleans up the old files if it is the initial creation.
302+
// todo(annzimmer) Cleans up the old files if it is the initial creation.
303303
return newModelFile;
304304
} else if (statusCode == DownloadManager.STATUS_FAILED) {
305305
// reset original model - removing download id.

firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/internal/ModelFileManager.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,8 @@
3838
*/
3939
public class ModelFileManager {
4040

41+
public static final String CUSTOM_MODEL_ROOT_PATH = "com.google.firebase.ml.custom.models";
4142
private static final String TAG = "FirebaseModelFileManage";
42-
43-
@VisibleForTesting
44-
static final String CUSTOM_MODEL_ROOT_PATH = "com.google.firebase.ml.custom.models";
45-
4643
private static final int INVALID_INDEX = -1;
4744
private final Context context;
4845
private final FirebaseApp firebaseApp;

0 commit comments

Comments
 (0)