summaryrefslogtreecommitdiff
path: root/java/com/google/android/libraries/mobiledatadownload/DownloadException.java
blob: cc9a1488bbdd2f14a4fc5ca44dde280cf9dbcc8d (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
/*
 * 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 static com.google.common.util.concurrent.Futures.immediateFailedFuture;

import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;

/** Thrown when there is a download failure. */
public final class DownloadException extends Exception {
  /** This error code is a representation of {@code MddDownloadResult.Code}. */
  private final DownloadResultCode downloadResultCode;

  /**
   * This is the result of calling download, which should be identical to {@code
   * MddDownloadResult.Code}.
   */
  // LINT.IfChange
  public enum DownloadResultCode {
    UNSPECIFIED(0), // unset value

    // File downloaded successfully.
    // This is just a placeholder, we currently don't log for success case.
    SUCCESS(1),

    // The error we don't know.
    UNKNOWN_ERROR(2),

    // The errors from the android downloader outside MDD, which comes from:
    // <internal>
    ANDROID_DOWNLOADER_UNKNOWN(100),
    ANDROID_DOWNLOADER_CANCELED(101),
    ANDROID_DOWNLOADER_INVALID_REQUEST(102),
    ANDROID_DOWNLOADER_HTTP_ERROR(103),
    ANDROID_DOWNLOADER_REQUEST_ERROR(104),
    ANDROID_DOWNLOADER_RESPONSE_OPEN_ERROR(105),
    ANDROID_DOWNLOADER_RESPONSE_CLOSE_ERROR(106),
    ANDROID_DOWNLOADER_NETWORK_IO_ERROR(107),
    ANDROID_DOWNLOADER_DISK_IO_ERROR(108),
    ANDROID_DOWNLOADER_FILE_SYSTEM_ERROR(109),
    ANDROID_DOWNLOADER_UNKNOWN_IO_ERROR(110),
    ANDROID_DOWNLOADER_OAUTH_ERROR(111),

    // The errors from the android downloader v2 outside MDD, which comes from:
    // <internal>
    ANDROID_DOWNLOADER2_ERROR(200),

    // The data file group has not been added to MDD by the time the caller
    // makes download API call.
    GROUP_NOT_FOUND_ERROR(300),

    // The DownloadListener is present but the DownloadMonitor is not provided.
    DOWNLOAD_MONITOR_NOT_PROVIDED_ERROR(301),

    // Errors from unsatisfied download preconditions.
    INSECURE_URL_ERROR(302),
    LOW_DISK_ERROR(303),

    // Errors from download preparation.
    UNABLE_TO_CREATE_FILE_URI_ERROR(304),
    SHARED_FILE_NOT_FOUND_ERROR(305),
    MALFORMED_FILE_URI_ERROR(306),
    UNABLE_TO_CREATE_MOBSTORE_RESPONSE_WRITER_ERROR(307),

    // Errors from file validation.
    UNABLE_TO_VALIDATE_DOWNLOAD_FILE_ERROR(308),
    DOWNLOADED_FILE_NOT_FOUND_ERROR(309),
    DOWNLOADED_FILE_CHECKSUM_MISMATCH_ERROR(310),
    CUSTOM_FILEGROUP_VALIDATION_FAILED(330),

    // Errors from download transforms.
    UNABLE_TO_SERIALIZE_DOWNLOAD_TRANSFORM_ERROR(311),
    DOWNLOAD_TRANSFORM_IO_ERROR(312),
    FINAL_FILE_CHECKSUM_MISMATCH_ERROR(313),

    // Errors from delta download.
    DELTA_DOWNLOAD_BASE_FILE_NOT_FOUND_ERROR(314),
    DELTA_DOWNLOAD_DECODE_IO_ERROR(315),

    // The error occurs after the file is ready.
    UNABLE_TO_UPDATE_FILE_STATE_ERROR(316),

    // Fail to update the file group metadata.
    UNABLE_TO_UPDATE_GROUP_METADATA_ERROR(317),

    // Errors from sharing files with the blob storage.
    // Failed to update the metadata max_expiration_date.
    UNABLE_TO_UPDATE_FILE_MAX_EXPIRATION_DATE(318),
    // Failed to share the file before SharedFileManager.startDownload is called.
    UNABLE_SHARE_FILE_BEFORE_DOWNLOAD_ERROR(319),
    // Failed to share the file after SharedFileManager.startDownload is called.
    UNABLE_SHARE_FILE_AFTER_DOWNLOAD_ERROR(320),

    // Download errors related to isolated file structure
    UNABLE_TO_REMOVE_SYMLINK_STRUCTURE(321),
    UNABLE_TO_CREATE_SYMLINK_STRUCTURE(322),

    // Download errors related to importing inline files
    // Failed to reserve file entries
    UNABLE_TO_RESERVE_FILE_ENTRY(323),
    // Invalid use of inlinefile url scheme
    INVALID_INLINE_FILE_URL_SCHEME(324),
    // Error performing inline file download
    INLINE_FILE_IO_ERROR(327),
    // Missing required inline download parms in FileDownloader's DownloadRequest
    MISSING_INLINE_DOWNLOAD_PARAMS(328),
    // Missing required inline file source in ImportFilesRequest
    MISSING_INLINE_FILE_SOURCE(329),

    // Download errors related to URL parsing
    MALFORMED_DOWNLOAD_URL(325),
    UNSUPPORTED_DOWNLOAD_URL_SCHEME(326),

    // Download errors for manifest file group populator.
    MANIFEST_FILE_GROUP_POPULATOR_INVALID_FLAG_ERROR(400),
    MANIFEST_FILE_GROUP_POPULATOR_CONTENT_CHANGED_DURING_DOWNLOAD_ERROR(401),
    MANIFEST_FILE_GROUP_POPULATOR_PARSE_MANIFEST_FILE_ERROR(402),
    MANIFEST_FILE_GROUP_POPULATOR_DELETE_MANIFEST_FILE_ERROR(403),
    MANIFEST_FILE_GROUP_POPULATOR_METADATA_IO_ERROR(404),

    // GDD specific download errors, reserved from 2000-2999.
    GDD_INVALID_ACCOUNT(2000),
    GDD_INVALID_AUTH_TOKEN(2001),
    GDD_FAIL_IN_SYNC_RUNNER(2002),
    GDD_INVALID_ELEMENT_COMBINATION_RECEIVED(2003),
    GDD_INVALID_INLINE_PAYLOAD_ELEMENT_DATA(2004),
    GDD_INVALID_CURRENT_ACTIVE_ELEMENT_DATA(2005),
    GDD_INVALID_NEXT_PENDING_ELEMENT_DATA(2006),
    GDD_CURRENT_ACTIVE_GROUP_HAS_NO_INLINE_FILE(2007),
    GDD_FAIL_TO_ADD_NEXT_PENDING_GROUP(2008),
    GDD_MISSING_ACCOUNT_FOR_PRIVATE_SYNC(2009),
    GDD_FAIL_IN_SYNC_RUNNER_PUBLIC(2010),
    GDD_FAIL_IN_SYNC_RUNNER_PRIVATE(2011),
    GDD_PUBLIC_SYNC_SUCCESS(2012),
    GDD_PRIVATE_SYNC_SUCCESS(2013),
    GDD_FAIL_TO_RETRIEVE_ZWIEBACK_TOKEN(2014);

    private final int code;

    DownloadResultCode(int code) {
      this.code = code;
    }

    /** Returns the int code corresponding to this enum value. */
    public int getCode() {
      return code;
    }
  }
  // LINT.ThenChange(<internal>)

  /** Builder for {@link DownloadException}. */
  public static final class Builder {
    private DownloadResultCode downloadResultCode;
    private String message;
    private Throwable cause;

    /** Sets the {@link DownloadResultCode}. */
    public Builder setDownloadResultCode(DownloadResultCode downloadResultCode) {
      this.downloadResultCode = downloadResultCode;
      return this;
    }

    /** Sets the error message. */
    public Builder setMessage(String message) {
      this.message = message;
      return this;
    }

    /** Sets the cause of the exception. */
    public Builder setCause(Throwable cause) {
      this.cause = cause;
      return this;
    }

    /** Returns a {@link DownloadException} instance. */
    public DownloadException build() {
      Preconditions.checkNotNull(downloadResultCode);
      if (message == null) {
        message = "Download result code: " + downloadResultCode.name();
      }
      return new DownloadException(this);
    }
  }

  /** Returns a Builder for {@link DownloadException}. */
  public static Builder builder() {
    return new Builder();
  }

  public DownloadResultCode getDownloadResultCode() {
    return downloadResultCode;
  }

  /**
   * Wraps the throwable with {@link DownloadException} and returns a failed future only if the
   * input future fails.
   */
  public static <T> ListenableFuture<T> wrapIfFailed(
      ListenableFuture<T> future, DownloadResultCode code, String message) {
    return Futures.catchingAsync(
        future,
        Throwable.class,
        (Throwable t) -> immediateFailedFuture(wrap(t, code, message)),
        MoreExecutors.directExecutor());
  }

  /** Wraps the throwable with {@link DownloadException}. */
  private static DownloadException wrap(
      Throwable throwable, DownloadResultCode code, String message) {
    return DownloadException.builder()
        .setDownloadResultCode(code)
        .setMessage(message)
        .setCause(throwable)
        .build();
  }

  private DownloadException(Builder builder) {
    super(builder.message, builder.cause);
    this.downloadResultCode = builder.downloadResultCode;
  }
}