summaryrefslogtreecommitdiff
path: root/java/com/google/android/libraries/mobiledatadownload/MobileDataDownload.java
blob: 1894e86cddff4b843848dc38bfb6a16bb5da03d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
/*
 * Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.android.libraries.mobiledatadownload;

import com.google.android.libraries.mobiledatadownload.TaskScheduler.ConstraintOverrides;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import com.google.mobiledatadownload.ClientConfigProto.ClientFileGroup;
import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup;
import java.util.Map;

/** The root object and entry point for the MobileDataDownload library. */
public interface MobileDataDownload {

  /**
   * Adds for download the data file group in {@link AddFileGroupRequest}, after running validation
   * on the group. This group will replace any previous version of this group once it is downloaded.
   *
   * <p>This api takes {@link AddFileGroupRequest} that contains data file group, and it can be used
   * to set extra params such as account.
   *
   * <p>This doesn't start the download right away. The download starts later when the tasks
   * scheduled via {@link #schedulePeriodicTasks} are run.
   *
   * <p>Calling this api with the exact same parameters multiple times is a no-op.
   *
   * @param addFileGroupRequest The request to add file group in MDD.
   * @return ListenableFuture of true if the group was successfully added, or the group was already
   *     present; ListenableFuture of false if the group is invalid or an I/O error occurs.
   */
  ListenableFuture<Boolean> addFileGroup(AddFileGroupRequest addFileGroupRequest);

  /**
   * Removes all versions of the data file group that matches {@link RemoveFileGroupRequest} from
   * MDD. If no data file group matches, this call is a no-op.
   *
   * <p>This api takes {@link RemoveFileGroupRequest} that contains data file group, and it can be
   * used to set extra params such as account.
   *
   * @param removeFileGroupRequest The request to remove file group from MDD.
   * @return Listenable of true if the group was successfully removed, or no group matches;
   *     Listenable of false if the matching group fails to be removed.
   */
  ListenableFuture<Boolean> removeFileGroup(RemoveFileGroupRequest removeFileGroupRequest);

  /**
   * Removes all versions of the data file groups that match {@link RemoveFileGroupsByFilterRequest}
   * from MDD. If no data file group matches, this call is a no-op.
   *
   * <p>This api takes a {@link RemoveFileGroupsByFilterRequest} that contains optional filters for
   * the group name, group source, associated account, etc.
   *
   * <p>A resolved future will only be returned if the removal completes successfully for all
   * matching file groups. If any failures occur during this method, it will return a failed future
   * with an {@link AggregateException} containing the failures that occurred.
   *
   * <p>NOTE: This only removes the metadata from MDD, not file content. Downloaded files that are
   * no longer needed are deleted during MDD's daily maintenance task.
   *
   * @param removeFileGroupsByFilterRequest The request to remove file group from MDD.
   * @return ListenableFuture that resolves with {@link RemoveFileGroupsByFilterResponse}, or fails
   *     with {@link AggregateException}
   */
  ListenableFuture<RemoveFileGroupsByFilterResponse> removeFileGroupsByFilter(
      RemoveFileGroupsByFilterRequest removeFileGroupsByFilterRequest);

  /**
   * Gets the file group definition that was added to MDD. This API cannot be used to access files,
   * but it can be accessed by populators to manipulate the existing file group state - eg, to
   * rename a file group, or otherwise migrate from one format to another.
   *
   * @return DataFileGroup if downloaded file group is found, otherwise a failing LF.
   */
  default ListenableFuture<DataFileGroup> readDataFileGroup(
      ReadDataFileGroupRequest readDataFileGroupRequest) {
    throw new UnsupportedOperationException();
  }

  /**
   * Returns the latest downloaded data that we have for the given group name.
   *
   * <p>This api takes an instance of {@link GetFileGroupRequest} that contains group name, and it
   * can be used to set extra params such as account.
   *
   * <p>This listenable future will return null if no group exists or has been downloaded for the
   * given group name.
   *
   * <p>Note: getFileGroup returns a snapshot of the latest state, but it's possible for the state
   * to change between a getFileGroup call and accessing the files if the ClientFileGroup gets
   * cached. Caching the returned ClientFileGroup is therefore discouraged.
   *
   * @param getFileGroupRequest The request to get a single file group.
   * @return The ListenableFuture of requested client file group for the given request.
   */
  ListenableFuture<ClientFileGroup> getFileGroup(GetFileGroupRequest getFileGroupRequest);

  /**
   * Returns all the data that we have for the given {@link GetFileGroupsByFilterRequest}.
   *
   * <p>This listenable future will return a list of file groups with their current download status.
   *
   * <p>Only present fields in {@link GetFileGroupsByFilterRequest} will be used to perform the
   * filtering, i.e. when no account is specified in the filter, file groups won't be filtered based
   * on account.
   *
   * <p>Note: getFileGroupsByFilter returns a snapshot of the latest state, but it's possible for
   * the state to change between a getFileGroupsByFilter call and accessing the files if the
   * ClientFileGroup gets cached. Caching the returned ClientFileGroup is therefore discouraged.
   *
   * @param getFileGroupsByFilterRequest The request to get multiple file groups after filtering.
   * @return The ListenableFuture that will resolve to a list of the requested client file groups,
   *     including pending and downloaded versions; this ListenableFuture will resolve to all client
   *     file groups when {@code getFileGroupsByFilterRequest.includeAllGroups} is true.
   */
  ListenableFuture<ImmutableList<ClientFileGroup>> getFileGroupsByFilter(
      GetFileGroupsByFilterRequest getFileGroupsByFilterRequest);

  /**
   * Imports Inline Files into an Existing MDD File Group.
   *
   * <p>This api takes a {@link ImportFilesRequest} containing identifying information about an
   * existing File Group, an optional list of {@link DataFile}s to import into the existing File
   * Group, and a Map of file content to import into MDD.
   *
   * <p>The identifying information is used to identify a file group and its specific version. This
   * prevents the caller from accidentally importing files into the wrong file group or the wrong
   * version of the file group. An optional {@link Account} parameter can also be specified if the
   * existing file group was associated with an account.
   *
   * <p>The given {@link DataFile} list allows updated files (still compatible with a given file
   * group version) to be imported into MDD. This API wll merge the given DataFiles into the
   * existing file group in the following manner:
   *
   * <ul>
   *   <li>DataFiles included in the DataFile list but not the existing file group will be added as
   *       new DataFiles
   *   <li>DataFiles included in the DataFile list will replace DataFiles in the existing file group
   *       if their file Ids match.
   *   <li>DataFiles included in the existing file group but not the DataFile list will remain
   *       untouched.
   * </ul>
   *
   * <p>{@link ImportFilesRequest} also requires a Map of file sources that should be imported by
   * MDD. The Map is keyed by the fileIds of DataFiles and contains the contents of the file to
   * import within a {@link ByteString}. This Map must contains an entry for all {@link DataFile}s
   * which require an inline file source. Only "Inline" {@link DataFile}s should be included in this
   * map (see details below).
   *
   * <p>An inline {@link DataFile} is the same as a standard {@link DataFile}, but instead of an
   * "https" url, the url should match the following format:
   *
   * <pre>{@code "inlinefile:<key>"}</pre>
   *
   * <p>Where {@code key} is a unique identifier of the file. In most cases, the checksum should be
   * used as this key. If the checksum is not used, another unique identifier should be used to
   * allow proper deduping of the file import within MDD.
   *
   * <p>Example inline file url:
   *
   * <pre>{@code inlinefile:sha1:9a4ea3ca81d3f1d631531cbc216a62d9b10509ee}</pre>
   *
   * <p>NOTE: Inline files can be specified by the given DataFile list in {@link
   * ImportFilesRequest}, but can also be specified by a {@link DataFileGroup} added via {@link
   * #addFileGroup}. A File Group that contains inline files will not be considered DOWNLOADED until
   * all inline files are imported via this API.
   *
   * <p>Because this method performs an update to the stored File Group metadata, the given {@link
   * ImportFilesRequest} must satisfy the following conditions:
   *
   * <ul>
   *   <li>The requests identifying information must match an existing File Group
   *   <li>All inline DataFiles must have file content specified in the request's Inline File Map
   * </ul>
   *
   * <p>If either of these conditions is not met, this operation will return a failed
   * ListenableFuture.
   *
   * <p>Finally, this API is a atomic operation. That is, <em>either all inline files will be
   * imported successfully or none will be imported</em>. If there is a failure with importing a
   * file, MDD will not update the file group (i.e. future calls to {@link #getFileGroup} will
   * return the same {@link ClientFileGroup} as before this call).
   *
   * @param importFilesRequest Request containing required parameters to perform import files
   *     operation.
   * @return ListenableFuture that resolves when all inline files are successfully imported.
   */
  ListenableFuture<Void> importFiles(ImportFilesRequest importFilesRequest);

  /**
   * Downloads a single file.
   *
   * <p>This api takes {@link SingleFileDownloadRequest}, which contains a download url of the file
   * to download. the destination location on device must also be specified. See
   * SingleFileDownloadRequest for full list of required/optional parameters.
   *
   * <p>The returned ListenableFuture will fail if there is an error during the download. The caller
   * is responsible for calling downloadFile again to restart the download.
   *
   * <p>The caller can be notified of progress by providing a {@link SingleFileDownloadListener}.
   * This listener will also provide callbacks for a completed download, failed download, or paused
   * download due to connectivity loss.
   *
   * <p>The caller can specify constraints that should be used for the download by providing a
   * {@link com.google.android.libraries.mobiledatadownload.downloader.DownloadConstraints}. This
   * allows downloads to only start when on Wifi, for example. By default, no constraints are
   * specified.
   *
   * @param singleFileDownloadRequest The request to download a file.
   * @return ListenableFuture that resolves when file is downloaded.
   */
  @CheckReturnValue
  ListenableFuture<Void> downloadFile(SingleFileDownloadRequest singleFileDownloadRequest);

  /**
   * Downloads and returns the latest downloaded data that we have for the given group name.
   *
   * <p>This api takes {@link DownloadFileGroupRequest} that contains group name, and it can be used
   * to set extra params such as account, download conditions, and download listener.
   *
   * <p>The group name must be added using {@link #addFileGroup} before downloading the file group.
   *
   * <p>The returned ListenableFuture will be resolved when the file group is downloaded. It can
   * also be used to cancel the download.
   *
   * <p>The returned ListenableFuture would fail if there is any error during the download. Client
   * is responsible to call the downloadFileGroup to resume the download.
   *
   * <p>Download progress is supported through the DownloadListener.
   *
   * <p>To download under any conditions, clients should use {@link
   * Constants.NO_RESTRICTIONS_DOWNLOAD_CONDITIONS}
   *
   * @param downloadFileGroupRequest The request to download file group.
   */
  ListenableFuture<ClientFileGroup> downloadFileGroup(
      DownloadFileGroupRequest downloadFileGroupRequest);

  /**
   * Downloads a file using a foreground service and notification.
   *
   * <p>This is similar to {@link #downloadFile}, but allows the download to continue running when
   * the app enters the background.
   *
   * <p>The notification created for the download includes a cancel action. This will allow the
   * download to be cancelled even when the app is in the background.
   *
   * <p>The cancel action in the notification menu requires the ForegroundService to be registered
   * with the application (via the AndroidManifest.xml). This allows the cancellation intents to be
   * properly picked up. To register the service, the following lines must be included in the app's
   * {@code AndroidManifest.xml}:
   *
   * <pre>{@code
   * <!-- Needed by foreground download service -->
   * <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
   *
   * <!-- Service for MDD foreground downloads -->
   * <service
   *   android:name="com.google.android.libraries.mobiledatadownload.foreground.sting.ForegroundDownloadService"
   *   android:exported="false" />
   * }</pre>
   *
   * <p>NOTE: The above excerpt is for Framework and Sting apps. Dagger apps should use the same
   * excerpt, but change the {@code android:name} property to:
   *
   * <pre>{@code
   * android:name="com.google.android.libraries.mobiledatadownload.foreground.dagger.ForegroundDownloadService"
   * }</pre>
   */
  @CheckReturnValue
  ListenableFuture<Void> downloadFileWithForegroundService(
      SingleFileDownloadRequest singleFileDownloadRequest);

  /**
   * Download a file group and show foreground download progress in a notification. User can cancel
   * the download from the notification menu.
   *
   * <p>The cancel action in the notification menu requires the ForegroundService to be registered
   * with the application (via the AndroidManifest.xml). This allows the cancellation intents to be
   * properly picked up. To register the service, the following lines must be included in the app's
   * {@code AndroidManifest.xml}:
   *
   * <pre>{@code
   * <!-- Needed by foreground download service -->
   * <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
   *
   * <!-- Service for MDD foreground downloads -->
   * <service
   *   android:name="com.google.android.libraries.mobiledatadownload.foreground.sting.ForegroundDownloadService"
   *   android:exported="false" />
   * }</pre>
   *
   * <p>NOTE: The above excerpt is for Framework and Sting apps. Dagger apps should use the same
   * excerpt, but change the {@code android:name} property to:
   *
   * <pre>{@code
   * android:name="com.google.android.libraries.mobiledatadownload.foreground.dagger.ForegroundDownloadService"
   * }</pre>
   */
  ListenableFuture<ClientFileGroup> downloadFileGroupWithForegroundService(
      DownloadFileGroupRequest downloadFileGroupRequest);

  /**
   * Cancel an on-going foreground download.
   *
   * <p>Attempts to cancel an on-going foreground download using best effort. If download is unknown
   * to MDD, this operation is a noop.
   *
   * <p>The key passed here must be created using {@link ForegroundDownloadKey}, and must match the
   * properties used from the request. Depending on which API was used to start the download, this
   * would be {@link DownloadFileGroupRequest} for {@link SingleFileDownloadRequest}.
   *
   * <p><b>NOTE:</b> In most cases, clients will not need to call this -- it is meant to allow the
   * ForegroundDownloadService to cancel a download via the Cancel action registered to a
   * notification.
   *
   * <p>Clients should prefer to cancel the future returned to them from the download call.
   *
   * @param downloadKey the key associated with the download
   */
  void cancelForegroundDownload(String downloadKey);

  /**
   * Triggers the execution of MDD maintenance.
   *
   * <p>MDD needs to run maintenance task once a day. If you call {@link
   * #schedulePeriodicBackgroundTasks} api, the maintenance will be called automatically. In case
   * you don't want to schedule MDD tasks, you can call this maintenance method directly.
   *
   * <p>If you do need to call this api, make sure that this api is called exactly once every day.
   *
   * <p>The returned ListenableFuture would fail if the maintenance execution doesn't succeed.
   */
  ListenableFuture<Void> maintenance();

  /**
   * Perform garbage collection, which includes removing expired file groups and unreferenced files.
   *
   * <p>By default, this is run as part of {@link #maintenance} so doesn't need to be invoked
   * directly by client code. If you disabled that behavior via {@link
   * Flags#mddEnableGarbageCollection} then this method should be periodically called to clean up
   * unused files.
   */
  ListenableFuture<Void> collectGarbage();

  /**
   * Schedule periodic tasks that will download and verify all file groups when the required
   * conditions are met, using the given {@link TaskScheduler}.
   *
   * <p>If the host app doesn't provide a TaskScheduler, calling this API will be a no-op.
   *
   * @deprecated Use the {@link schedulePeriodicBackgroundTasks} instead.
   */
  @Deprecated
  void schedulePeriodicTasks();

  /**
   * Schedule periodic background tasks that will download and verify all file groups when the
   * required conditions are met, using the given {@link TaskScheduler}.
   *
   * <p>If the host app doesn't provide a TaskScheduler, calling this API will be a no-op.
   */
  ListenableFuture<Void> schedulePeriodicBackgroundTasks();

  /**
   * Schedule periodic background tasks that will download and verify all file groups when the
   * required conditions are met, using the given {@link TaskScheduler}.
   *
   * <p>If the host app doesn't provide a TaskScheduler, calling this API will be a no-op.
   *
   * @param constraintOverridesMap to allow clients to override constraints requirements.
   *     <p><code>{@code
   *  ConstraintOverrides wifiOverrides =
   *     ConstraintOverrides.newBuilder()
   *         .setRequiresCharging(false)
   *         .setRequiresDeviceIdle(true)
   *         .build();
   * ConstraintOverrides cellularOverrides =
   *     ConstraintOverrides.newBuilder()
   *         .setRequiresCharging(true)
   *         .setRequiresDeviceIdle(false)
   *         .build();
   *
   *  Map<String, ConstraintOverrides> constraintOverridesMap = new HashMap<>();
   *  constraintOverridesMap.put(TaskScheduler.WIFI_CHARGING_PERIODIC_TASK, wifiOverrides);
   *  constraintOverridesMap.put(TaskScheduler.CELLULAR_CHARGING_PERIODIC_TASK, cellularOverrides);
   *
   *  mobileDataDownload.schedulePeriodicBackgroundTasks(Optional.of(constraintOverridesMap)).get();
   * }</code>
   */
  ListenableFuture<Void> schedulePeriodicBackgroundTasks(
      Optional<Map<String, ConstraintOverrides>> constraintOverridesMap);

  /**
   * Cancels previously-scheduled periodic background tasks using the given {@link TaskScheduler}.
   * Cancelling is best-effort and only meant to be used in an emergency; most apps will never need
   * to call it.
   *
   * <p>If the host app doesn't provide a TaskScheduler, calling this API is a no-op.
   */
  default ListenableFuture<Void> cancelPeriodicBackgroundTasks() {
    // TODO(b/223822302): remove default once all implementations have been updated to include it
    return Futures.immediateVoidFuture();
  }

  /**
   * Handle a task scheduled via a task scheduling service.
   *
   * <p>This method should not be called on the main thread, as it does work on the thread it is
   * called on.
   *
   * @return a listenable future which indicates when any async task scheduled is complete.
   */
  ListenableFuture<Void> handleTask(String tag);

  /** Clear MDD metadata and its managed files. MDD will be reset to a clean state. */
  ListenableFuture<Void> clear();

  /**
   * Return MDD debug info as a string. This could return some PII information so it's not
   * recommended to be called in production build.
   *
   * <p>This debug info string could be very long. In order to print them in adb logcat, we have to
   * split the string. See how it is done in our sample app: <internal>
   */
  String getDebugInfoAsString();

  /**
   * Reports usage of a file group back to MDD. This can be used to track errors with file group
   * roll outs. Each usage of the file group should result in a single call of this method in order
   * to allow for accurate metrics server side.
   *
   * @param usageEvent that will be logged.
   * @return a listenable future which indicates that the UsageEvent has been logged.
   */
  @CanIgnoreReturnValue
  ListenableFuture<Void> reportUsage(UsageEvent usageEvent);
}