• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.libraries.mobiledatadownload;
17 
18 import com.google.android.libraries.mobiledatadownload.TaskScheduler.ConstraintOverrides;
19 import com.google.common.base.Optional;
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.util.concurrent.Futures;
22 import com.google.common.util.concurrent.ListenableFuture;
23 import com.google.errorprone.annotations.CanIgnoreReturnValue;
24 import com.google.errorprone.annotations.CheckReturnValue;
25 import com.google.mobiledatadownload.ClientConfigProto.ClientFileGroup;
26 import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup;
27 import java.util.Map;
28 
29 /** The root object and entry point for the MobileDataDownload library. */
30 public interface MobileDataDownload {
31 
32   /**
33    * Adds for download the data file group in {@link AddFileGroupRequest}, after running validation
34    * on the group. This group will replace any previous version of this group once it is downloaded.
35    *
36    * <p>This api takes {@link AddFileGroupRequest} that contains data file group, and it can be used
37    * to set extra params such as account.
38    *
39    * <p>This doesn't start the download right away. The download starts later when the tasks
40    * scheduled via {@link #schedulePeriodicTasks} are run.
41    *
42    * <p>Calling this api with the exact same parameters multiple times is a no-op.
43    *
44    * @param addFileGroupRequest The request to add file group in MDD.
45    * @return ListenableFuture of true if the group was successfully added, or the group was already
46    *     present; ListenableFuture of false if the group is invalid or an I/O error occurs.
47    */
addFileGroup(AddFileGroupRequest addFileGroupRequest)48   ListenableFuture<Boolean> addFileGroup(AddFileGroupRequest addFileGroupRequest);
49 
50   /**
51    * Removes all versions of the data file group that matches {@link RemoveFileGroupRequest} from
52    * MDD. If no data file group matches, this call is a no-op.
53    *
54    * <p>This api takes {@link RemoveFileGroupRequest} that contains data file group, and it can be
55    * used to set extra params such as account.
56    *
57    * @param removeFileGroupRequest The request to remove file group from MDD.
58    * @return Listenable of true if the group was successfully removed, or no group matches;
59    *     Listenable of false if the matching group fails to be removed.
60    */
removeFileGroup(RemoveFileGroupRequest removeFileGroupRequest)61   ListenableFuture<Boolean> removeFileGroup(RemoveFileGroupRequest removeFileGroupRequest);
62 
63   /**
64    * Removes all versions of the data file groups that match {@link RemoveFileGroupsByFilterRequest}
65    * from MDD. If no data file group matches, this call is a no-op.
66    *
67    * <p>This api takes a {@link RemoveFileGroupsByFilterRequest} that contains optional filters for
68    * the group name, group source, associated account, etc.
69    *
70    * <p>A resolved future will only be returned if the removal completes successfully for all
71    * matching file groups. If any failures occur during this method, it will return a failed future
72    * with an {@link AggregateException} containing the failures that occurred.
73    *
74    * <p>NOTE: This only removes the metadata from MDD, not file content. Downloaded files that are
75    * no longer needed are deleted during MDD's daily maintenance task.
76    *
77    * @param removeFileGroupsByFilterRequest The request to remove file group from MDD.
78    * @return ListenableFuture that resolves with {@link RemoveFileGroupsByFilterResponse}, or fails
79    *     with {@link AggregateException}
80    */
removeFileGroupsByFilter( RemoveFileGroupsByFilterRequest removeFileGroupsByFilterRequest)81   ListenableFuture<RemoveFileGroupsByFilterResponse> removeFileGroupsByFilter(
82       RemoveFileGroupsByFilterRequest removeFileGroupsByFilterRequest);
83 
84   /**
85    * Gets the file group definition that was added to MDD. This API cannot be used to access files,
86    * but it can be accessed by populators to manipulate the existing file group state - eg, to
87    * rename a file group, or otherwise migrate from one format to another.
88    *
89    * @return DataFileGroup if downloaded file group is found, otherwise a failing LF.
90    */
readDataFileGroup( ReadDataFileGroupRequest readDataFileGroupRequest)91   default ListenableFuture<DataFileGroup> readDataFileGroup(
92       ReadDataFileGroupRequest readDataFileGroupRequest) {
93     throw new UnsupportedOperationException();
94   }
95 
96   /**
97    * Gets DataFileGroup definitions that were added to MDD by filter. This API cannot be used to
98    * access files.
99    *
100    * <p>Only present fields in {@link ReadDataFileGroupsByFilterRequest} will be used to perform the
101    * filtering. For example, if no account is specified in the filter, file groups won't be filtered
102    * based on account.
103    *
104    * @param readDataFileGroupsByFilterRequest The request to get multiple data file groups after
105    *     filtering.
106    * @return The ListenableFuture that will resolve to a list of the requested data file groups.
107    *     This ListenableFuture will resolve to all data file groups when {@code
108    *     readDataFileGroupsByFilterRequest.includeAllGroups} is true.
109    */
readDataFileGroupsByFilter( ReadDataFileGroupsByFilterRequest readDataFileGroupsByFilterRequest)110   default ListenableFuture<ImmutableList<DataFileGroup>> readDataFileGroupsByFilter(
111       ReadDataFileGroupsByFilterRequest readDataFileGroupsByFilterRequest) {
112     throw new UnsupportedOperationException();
113   }
114 
115   /**
116    * Returns the latest downloaded data that we have for the given group name.
117    *
118    * <p>This api takes an instance of {@link GetFileGroupRequest} that contains group name, and it
119    * can be used to set extra params such as account.
120    *
121    * <p>This listenable future will return null if no group exists or has been downloaded for the
122    * given group name.
123    *
124    * <p>Note: getFileGroup returns a snapshot of the latest state, but it's possible for the state
125    * to change between a getFileGroup call and accessing the files if the ClientFileGroup gets
126    * cached. Caching the returned ClientFileGroup is therefore discouraged.
127    *
128    * @param getFileGroupRequest The request to get a single file group.
129    * @return The ListenableFuture of requested client file group for the given request.
130    */
getFileGroup(GetFileGroupRequest getFileGroupRequest)131   ListenableFuture<ClientFileGroup> getFileGroup(GetFileGroupRequest getFileGroupRequest);
132 
133   /**
134    * Returns all the data that we have for the given {@link GetFileGroupsByFilterRequest}.
135    *
136    * <p>This listenable future will return a list of file groups with their current download status.
137    *
138    * <p>Only present fields in {@link GetFileGroupsByFilterRequest} will be used to perform the
139    * filtering, i.e. when no account is specified in the filter, file groups won't be filtered based
140    * on account.
141    *
142    * <p>Note: getFileGroupsByFilter returns a snapshot of the latest state, but it's possible for
143    * the state to change between a getFileGroupsByFilter call and accessing the files if the
144    * ClientFileGroup gets cached. Caching the returned ClientFileGroup is therefore discouraged.
145    *
146    * @param getFileGroupsByFilterRequest The request to get multiple file groups after filtering.
147    * @return The ListenableFuture that will resolve to a list of the requested client file groups,
148    *     including pending and downloaded versions; this ListenableFuture will resolve to all client
149    *     file groups when {@code getFileGroupsByFilterRequest.includeAllGroups} is true.
150    */
getFileGroupsByFilter( GetFileGroupsByFilterRequest getFileGroupsByFilterRequest)151   ListenableFuture<ImmutableList<ClientFileGroup>> getFileGroupsByFilter(
152       GetFileGroupsByFilterRequest getFileGroupsByFilterRequest);
153 
154   /**
155    * Imports Inline Files into an Existing MDD File Group.
156    *
157    * <p>This api takes a {@link ImportFilesRequest} containing identifying information about an
158    * existing File Group, an optional list of {@link DataFile}s to import into the existing File
159    * Group, and a Map of file content to import into MDD.
160    *
161    * <p>The identifying information is used to identify a file group and its specific version. This
162    * prevents the caller from accidentally importing files into the wrong file group or the wrong
163    * version of the file group. An optional {@link Account} parameter can also be specified if the
164    * existing file group was associated with an account.
165    *
166    * <p>The given {@link DataFile} list allows updated files (still compatible with a given file
167    * group version) to be imported into MDD. This API wll merge the given DataFiles into the
168    * existing file group in the following manner:
169    *
170    * <ul>
171    *   <li>DataFiles included in the DataFile list but not the existing file group will be added as
172    *       new DataFiles
173    *   <li>DataFiles included in the DataFile list will replace DataFiles in the existing file group
174    *       if their file Ids match.
175    *   <li>DataFiles included in the existing file group but not the DataFile list will remain
176    *       untouched.
177    * </ul>
178    *
179    * <p>{@link ImportFilesRequest} also requires a Map of file sources that should be imported by
180    * MDD. The Map is keyed by the fileIds of DataFiles and contains the contents of the file to
181    * import within a {@link ByteString}. This Map must contains an entry for all {@link DataFile}s
182    * which require an inline file source. Only "Inline" {@link DataFile}s should be included in this
183    * map (see details below).
184    *
185    * <p>An inline {@link DataFile} is the same as a standard {@link DataFile}, but instead of an
186    * "https" url, the url should match the following format:
187    *
188    * <pre>{@code "inlinefile:<key>"}</pre>
189    *
190    * <p>Where {@code key} is a unique identifier of the file. In most cases, the checksum should be
191    * used as this key. If the checksum is not used, another unique identifier should be used to
192    * allow proper deduping of the file import within MDD.
193    *
194    * <p>Example inline file url:
195    *
196    * <pre>{@code inlinefile:sha1:9a4ea3ca81d3f1d631531cbc216a62d9b10509ee}</pre>
197    *
198    * <p>NOTE: Inline files can be specified by the given DataFile list in {@link
199    * ImportFilesRequest}, but can also be specified by a {@link DataFileGroup} added via {@link
200    * #addFileGroup}. A File Group that contains inline files will not be considered DOWNLOADED until
201    * all inline files are imported via this API.
202    *
203    * <p>Because this method performs an update to the stored File Group metadata, the given {@link
204    * ImportFilesRequest} must satisfy the following conditions:
205    *
206    * <ul>
207    *   <li>The requests identifying information must match an existing File Group
208    *   <li>All inline DataFiles must have file content specified in the request's Inline File Map
209    * </ul>
210    *
211    * <p>If either of these conditions is not met, this operation will return a failed
212    * ListenableFuture.
213    *
214    * <p>Finally, this API is a atomic operation. That is, <em>either all inline files will be
215    * imported successfully or none will be imported</em>. If there is a failure with importing a
216    * file, MDD will not update the file group (i.e. future calls to {@link #getFileGroup} will
217    * return the same {@link ClientFileGroup} as before this call).
218    *
219    * @param importFilesRequest Request containing required parameters to perform import files
220    *     operation.
221    * @return ListenableFuture that resolves when all inline files are successfully imported.
222    */
importFiles(ImportFilesRequest importFilesRequest)223   ListenableFuture<Void> importFiles(ImportFilesRequest importFilesRequest);
224 
225   /**
226    * Downloads a single file.
227    *
228    * <p>This api takes {@link SingleFileDownloadRequest}, which contains a download url of the file
229    * to download. the destination location on device must also be specified. See
230    * SingleFileDownloadRequest for full list of required/optional parameters.
231    *
232    * <p>The returned ListenableFuture will fail if there is an error during the download. The caller
233    * is responsible for calling downloadFile again to restart the download.
234    *
235    * <p>The caller can be notified of progress by providing a {@link SingleFileDownloadListener}.
236    * This listener will also provide callbacks for a completed download, failed download, or paused
237    * download due to connectivity loss.
238    *
239    * <p>The caller can specify constraints that should be used for the download by providing a
240    * {@link com.google.android.libraries.mobiledatadownload.downloader.DownloadConstraints}. This
241    * allows downloads to only start when on Wifi, for example. By default, no constraints are
242    * specified.
243    *
244    * @param singleFileDownloadRequest The request to download a file.
245    * @return ListenableFuture that resolves when file is downloaded.
246    */
247   @CheckReturnValue
downloadFile(SingleFileDownloadRequest singleFileDownloadRequest)248   ListenableFuture<Void> downloadFile(SingleFileDownloadRequest singleFileDownloadRequest);
249 
250   /**
251    * Downloads and returns the latest downloaded data that we have for the given group name.
252    *
253    * <p>This api takes {@link DownloadFileGroupRequest} that contains group name, and it can be used
254    * to set extra params such as account, download conditions, and download listener.
255    *
256    * <p>The group name must be added using {@link #addFileGroup} before downloading the file group.
257    *
258    * <p>The returned ListenableFuture will be resolved when the file group is downloaded. It can
259    * also be used to cancel the download.
260    *
261    * <p>The returned ListenableFuture would fail if there is any error during the download. Client
262    * is responsible to call the downloadFileGroup to resume the download.
263    *
264    * <p>Download progress is supported through the DownloadListener.
265    *
266    * <p>To download under any conditions, clients should use {@link
267    * Constants.NO_RESTRICTIONS_DOWNLOAD_CONDITIONS}
268    *
269    * @param downloadFileGroupRequest The request to download file group.
270    */
downloadFileGroup( DownloadFileGroupRequest downloadFileGroupRequest)271   ListenableFuture<ClientFileGroup> downloadFileGroup(
272       DownloadFileGroupRequest downloadFileGroupRequest);
273 
274   /**
275    * Downloads a file using a foreground service and notification.
276    *
277    * <p>This is similar to {@link #downloadFile}, but allows the download to continue running when
278    * the app enters the background.
279    *
280    * <p>The notification created for the download includes a cancel action. This will allow the
281    * download to be cancelled even when the app is in the background.
282    *
283    * <p>The cancel action in the notification menu requires the ForegroundService to be registered
284    * with the application (via the AndroidManifest.xml). This allows the cancellation intents to be
285    * properly picked up. To register the service, the following lines must be included in the app's
286    * {@code AndroidManifest.xml}:
287    *
288    * <pre>{@code
289    * <!-- Needed by foreground download service -->
290    * <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
291    *
292    * <!-- Service for MDD foreground downloads -->
293    * <service
294    *   android:name="com.google.android.libraries.mobiledatadownload.foreground.sting.ForegroundDownloadService"
295    *   android:exported="false" />
296    * }</pre>
297    *
298    * <p>NOTE: The above excerpt is for Framework and Sting apps. Dagger apps should use the same
299    * excerpt, but change the {@code android:name} property to:
300    *
301    * <pre>{@code
302    * android:name="com.google.android.libraries.mobiledatadownload.foreground.dagger.ForegroundDownloadService"
303    * }</pre>
304    */
305   @CheckReturnValue
downloadFileWithForegroundService( SingleFileDownloadRequest singleFileDownloadRequest)306   ListenableFuture<Void> downloadFileWithForegroundService(
307       SingleFileDownloadRequest singleFileDownloadRequest);
308 
309   /**
310    * Download a file group and show foreground download progress in a notification. User can cancel
311    * the download from the notification menu.
312    *
313    * <p>The cancel action in the notification menu requires the ForegroundService to be registered
314    * with the application (via the AndroidManifest.xml). This allows the cancellation intents to be
315    * properly picked up. To register the service, the following lines must be included in the app's
316    * {@code AndroidManifest.xml}:
317    *
318    * <pre>{@code
319    * <!-- Needed by foreground download service -->
320    * <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
321    *
322    * <!-- Service for MDD foreground downloads -->
323    * <service
324    *   android:name="com.google.android.libraries.mobiledatadownload.foreground.sting.ForegroundDownloadService"
325    *   android:exported="false" />
326    * }</pre>
327    *
328    * <p>NOTE: The above excerpt is for Framework and Sting apps. Dagger apps should use the same
329    * excerpt, but change the {@code android:name} property to:
330    *
331    * <pre>{@code
332    * android:name="com.google.android.libraries.mobiledatadownload.foreground.dagger.ForegroundDownloadService"
333    * }</pre>
334    */
downloadFileGroupWithForegroundService( DownloadFileGroupRequest downloadFileGroupRequest)335   ListenableFuture<ClientFileGroup> downloadFileGroupWithForegroundService(
336       DownloadFileGroupRequest downloadFileGroupRequest);
337 
338   /**
339    * Cancel an on-going foreground download.
340    *
341    * <p>Attempts to cancel an on-going foreground download using best effort. If download is unknown
342    * to MDD, this operation is a noop.
343    *
344    * <p>The key passed here must be created using {@link ForegroundDownloadKey}, and must match the
345    * properties used from the request. Depending on which API was used to start the download, this
346    * would be {@link DownloadFileGroupRequest} for {@link SingleFileDownloadRequest}.
347    *
348    * <p><b>NOTE:</b> In most cases, clients will not need to call this -- it is meant to allow the
349    * ForegroundDownloadService to cancel a download via the Cancel action registered to a
350    * notification.
351    *
352    * <p>Clients should prefer to cancel the future returned to them from the download call.
353    *
354    * @param downloadKey the key associated with the download
355    */
cancelForegroundDownload(String downloadKey)356   void cancelForegroundDownload(String downloadKey);
357 
358   /**
359    * Triggers the execution of MDD maintenance.
360    *
361    * <p>MDD needs to run maintenance task once a day. If you call {@link
362    * #schedulePeriodicBackgroundTasks} api, the maintenance will be called automatically. In case
363    * you don't want to schedule MDD tasks, you can call this maintenance method directly.
364    *
365    * <p>If you do need to call this api, make sure that this api is called exactly once every day.
366    *
367    * <p>The returned ListenableFuture would fail if the maintenance execution doesn't succeed.
368    */
maintenance()369   ListenableFuture<Void> maintenance();
370 
371   /**
372    * Perform garbage collection, which includes removing expired file groups and unreferenced files.
373    *
374    * <p>By default, this is run as part of {@link #maintenance} so doesn't need to be invoked
375    * directly by client code. If you disabled that behavior via {@link
376    * Flags#mddEnableGarbageCollection} then this method should be periodically called to clean up
377    * unused files.
378    */
collectGarbage()379   ListenableFuture<Void> collectGarbage();
380 
381   /**
382    * Schedule periodic tasks that will download and verify all file groups when the required
383    * conditions are met, using the given {@link TaskScheduler}.
384    *
385    * <p>If the host app doesn't provide a TaskScheduler, calling this API will be a no-op.
386    *
387    * @deprecated Use the {@link schedulePeriodicBackgroundTasks} instead.
388    */
389   @Deprecated
schedulePeriodicTasks()390   void schedulePeriodicTasks();
391 
392   /**
393    * Schedule periodic background tasks that will download and verify all file groups when the
394    * required conditions are met, using the given {@link TaskScheduler}.
395    *
396    * <p>If the host app doesn't provide a TaskScheduler, calling this API will be a no-op.
397    */
schedulePeriodicBackgroundTasks()398   ListenableFuture<Void> schedulePeriodicBackgroundTasks();
399 
400   /**
401    * Schedule periodic background tasks that will download and verify all file groups when the
402    * required conditions are met, using the given {@link TaskScheduler}.
403    *
404    * <p>If the host app doesn't provide a TaskScheduler, calling this API will be a no-op.
405    *
406    * @param constraintOverridesMap to allow clients to override constraints requirements.
407    *     <p><code>{@code
408    *  ConstraintOverrides wifiOverrides =
409    *     ConstraintOverrides.newBuilder()
410    *         .setRequiresCharging(false)
411    *         .setRequiresDeviceIdle(true)
412    *         .build();
413    * ConstraintOverrides cellularOverrides =
414    *     ConstraintOverrides.newBuilder()
415    *         .setRequiresCharging(true)
416    *         .setRequiresDeviceIdle(false)
417    *         .build();
418    *
419    *  Map<String, ConstraintOverrides> constraintOverridesMap = new HashMap<>();
420    *  constraintOverridesMap.put(TaskScheduler.WIFI_CHARGING_PERIODIC_TASK, wifiOverrides);
421    *  constraintOverridesMap.put(TaskScheduler.CELLULAR_CHARGING_PERIODIC_TASK, cellularOverrides);
422    *
423    *  mobileDataDownload.schedulePeriodicBackgroundTasks(Optional.of(constraintOverridesMap)).get();
424    * }</code>
425    */
schedulePeriodicBackgroundTasks( Optional<Map<String, ConstraintOverrides>> constraintOverridesMap)426   ListenableFuture<Void> schedulePeriodicBackgroundTasks(
427       Optional<Map<String, ConstraintOverrides>> constraintOverridesMap);
428 
429   /**
430    * Cancels previously-scheduled periodic background tasks using the given {@link TaskScheduler}.
431    * Cancelling is best-effort and only meant to be used in an emergency; most apps will never need
432    * to call it.
433    *
434    * <p>If the host app doesn't provide a TaskScheduler, calling this API is a no-op.
435    */
cancelPeriodicBackgroundTasks()436   default ListenableFuture<Void> cancelPeriodicBackgroundTasks() {
437     // TODO(b/223822302): remove default once all implementations have been updated to include it
438     return Futures.immediateVoidFuture();
439   }
440 
441   /**
442    * Handle a task scheduled via a task scheduling service.
443    *
444    * <p>This method should not be called on the main thread, as it does work on the thread it is
445    * called on.
446    *
447    * @return a listenable future which indicates when any async task scheduled is complete.
448    */
handleTask(String tag)449   ListenableFuture<Void> handleTask(String tag);
450 
451   /** Clear MDD metadata and its managed files. MDD will be reset to a clean state. */
clear()452   ListenableFuture<Void> clear();
453 
454   /**
455    * Return MDD debug info as a string. This could return some PII information so it's not
456    * recommended to be called in production build.
457    *
458    * <p>This debug info string could be very long. In order to print them in adb logcat, we have to
459    * split the string. See how it is done in our sample app: <internal>
460    */
getDebugInfoAsString()461   String getDebugInfoAsString();
462 
463   /**
464    * Reports usage of a file group back to MDD. This can be used to track errors with file group
465    * roll outs. Each usage of the file group should result in a single call of this method in order
466    * to allow for accurate metrics server side.
467    *
468    * @param usageEvent that will be logged.
469    * @return a listenable future which indicates that the UsageEvent has been logged.
470    */
471   @CanIgnoreReturnValue
reportUsage(UsageEvent usageEvent)472   ListenableFuture<Void> reportUsage(UsageEvent usageEvent);
473 }
474