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 static com.google.common.util.concurrent.Futures.immediateFailedFuture; 19 20 import com.google.android.libraries.mobiledatadownload.tracing.PropagatedFutures; 21 import com.google.common.base.Preconditions; 22 import com.google.common.util.concurrent.ListenableFuture; 23 import com.google.common.util.concurrent.MoreExecutors; 24 import com.google.errorprone.annotations.CanIgnoreReturnValue; 25 26 /** Thrown when there is a download failure. */ 27 public final class DownloadException extends Exception { 28 /** This error code is a representation of {@code MddDownloadResult.Code}. */ 29 private final DownloadResultCode downloadResultCode; 30 31 /** 32 * This is the result of calling download, which should be identical to {@code 33 * MddDownloadResult.Code}. 34 */ 35 // LINT.IfChange 36 public enum DownloadResultCode { 37 UNSPECIFIED(0), // unset value 38 39 // File downloaded successfully. 40 // This is just a placeholder, we currently don't log for success case. 41 SUCCESS(1), 42 43 // The error we don't know. 44 UNKNOWN_ERROR(2), 45 46 // The errors from the android downloader outside MDD, which comes from: 47 // <internal> 48 ANDROID_DOWNLOADER_UNKNOWN(100), 49 ANDROID_DOWNLOADER_CANCELED(101), 50 ANDROID_DOWNLOADER_INVALID_REQUEST(102), 51 ANDROID_DOWNLOADER_HTTP_ERROR(103), 52 ANDROID_DOWNLOADER_REQUEST_ERROR(104), 53 ANDROID_DOWNLOADER_RESPONSE_OPEN_ERROR(105), 54 ANDROID_DOWNLOADER_RESPONSE_CLOSE_ERROR(106), 55 ANDROID_DOWNLOADER_NETWORK_IO_ERROR(107), 56 ANDROID_DOWNLOADER_DISK_IO_ERROR(108), 57 ANDROID_DOWNLOADER_FILE_SYSTEM_ERROR(109), 58 ANDROID_DOWNLOADER_UNKNOWN_IO_ERROR(110), 59 ANDROID_DOWNLOADER_OAUTH_ERROR(111), 60 61 // The errors from the android downloader v2 outside MDD, which comes from: 62 // <internal> 63 ANDROID_DOWNLOADER2_ERROR(200), 64 65 // The data file group has not been added to MDD by the time the caller 66 // makes download API call. 67 GROUP_NOT_FOUND_ERROR(300), 68 69 // The DownloadListener is present but the DownloadMonitor is not provided. 70 DOWNLOAD_MONITOR_NOT_PROVIDED_ERROR(301), 71 72 // Errors from unsatisfied download preconditions. 73 INSECURE_URL_ERROR(302), 74 LOW_DISK_ERROR(303), 75 76 // Errors from download preparation. 77 UNABLE_TO_CREATE_FILE_URI_ERROR(304), 78 SHARED_FILE_NOT_FOUND_ERROR(305), 79 MALFORMED_FILE_URI_ERROR(306), 80 UNABLE_TO_CREATE_MOBSTORE_RESPONSE_WRITER_ERROR(307), 81 82 // Errors from file validation. 83 UNABLE_TO_VALIDATE_DOWNLOAD_FILE_ERROR(308), 84 DOWNLOADED_FILE_NOT_FOUND_ERROR(309), 85 DOWNLOADED_FILE_CHECKSUM_MISMATCH_ERROR(310), 86 CUSTOM_FILEGROUP_VALIDATION_FAILED(330), 87 88 // Errors from download transforms. 89 UNABLE_TO_SERIALIZE_DOWNLOAD_TRANSFORM_ERROR(311), 90 DOWNLOAD_TRANSFORM_IO_ERROR(312), 91 FINAL_FILE_CHECKSUM_MISMATCH_ERROR(313), 92 93 // Errors from delta download. 94 DELTA_DOWNLOAD_BASE_FILE_NOT_FOUND_ERROR(314), 95 DELTA_DOWNLOAD_DECODE_IO_ERROR(315), 96 97 // The error occurs after the file is ready. 98 UNABLE_TO_UPDATE_FILE_STATE_ERROR(316), 99 100 // Fail to update the file group metadata. 101 UNABLE_TO_UPDATE_GROUP_METADATA_ERROR(317), 102 103 // Errors from sharing files with the blob storage. 104 // Failed to update the metadata max_expiration_date. 105 UNABLE_TO_UPDATE_FILE_MAX_EXPIRATION_DATE(318), 106 // Failed to share the file before SharedFileManager.startDownload is called. 107 UNABLE_SHARE_FILE_BEFORE_DOWNLOAD_ERROR(319), 108 // Failed to share the file after SharedFileManager.startDownload is called. 109 UNABLE_SHARE_FILE_AFTER_DOWNLOAD_ERROR(320), 110 111 // Download errors related to isolated file structure 112 UNABLE_TO_REMOVE_SYMLINK_STRUCTURE(321), 113 UNABLE_TO_CREATE_SYMLINK_STRUCTURE(322), 114 115 // Download errors related to importing inline files 116 // Failed to reserve file entries 117 UNABLE_TO_RESERVE_FILE_ENTRY(323), 118 // Invalid use of inlinefile url scheme 119 INVALID_INLINE_FILE_URL_SCHEME(324), 120 // Error performing inline file download 121 INLINE_FILE_IO_ERROR(327), 122 // Missing required inline download parms in FileDownloader's DownloadRequest 123 MISSING_INLINE_DOWNLOAD_PARAMS(328), 124 // Missing required inline file source in ImportFilesRequest 125 MISSING_INLINE_FILE_SOURCE(329), 126 127 // Download errors related to URL parsing 128 MALFORMED_DOWNLOAD_URL(325), 129 UNSUPPORTED_DOWNLOAD_URL_SCHEME(326), 130 131 // Download errors for manifest file group populator. 132 MANIFEST_FILE_GROUP_POPULATOR_INVALID_FLAG_ERROR(400), 133 MANIFEST_FILE_GROUP_POPULATOR_CONTENT_CHANGED_DURING_DOWNLOAD_ERROR(401), 134 MANIFEST_FILE_GROUP_POPULATOR_PARSE_MANIFEST_FILE_ERROR(402), 135 MANIFEST_FILE_GROUP_POPULATOR_DELETE_MANIFEST_FILE_ERROR(403), 136 MANIFEST_FILE_GROUP_POPULATOR_METADATA_IO_ERROR(404), 137 138 // GDD specific download errors, reserved from 2000-2999. 139 GDD_INVALID_ACCOUNT(2000), 140 GDD_INVALID_AUTH_TOKEN(2001), 141 GDD_FAIL_IN_SYNC_RUNNER(2002), 142 GDD_INVALID_ELEMENT_COMBINATION_RECEIVED(2003), 143 GDD_INVALID_INLINE_PAYLOAD_ELEMENT_DATA(2004), 144 GDD_INVALID_CURRENT_ACTIVE_ELEMENT_DATA(2005), 145 GDD_INVALID_NEXT_PENDING_ELEMENT_DATA(2006), 146 GDD_CURRENT_ACTIVE_GROUP_HAS_NO_INLINE_FILE(2007), 147 GDD_FAIL_TO_ADD_NEXT_PENDING_GROUP(2008), 148 GDD_MISSING_ACCOUNT_FOR_PRIVATE_SYNC(2009), 149 GDD_FAIL_IN_SYNC_RUNNER_PUBLIC(2010), 150 GDD_FAIL_IN_SYNC_RUNNER_PRIVATE(2011), 151 GDD_PUBLIC_SYNC_SUCCESS(2012), 152 GDD_PRIVATE_SYNC_SUCCESS(2013), 153 GDD_FAIL_TO_RETRIEVE_ZWIEBACK_TOKEN(2014); 154 155 private final int code; 156 DownloadResultCode(int code)157 DownloadResultCode(int code) { 158 this.code = code; 159 } 160 161 /** Returns the int code corresponding to this enum value. */ getCode()162 public int getCode() { 163 return code; 164 } 165 } 166 // LINT.ThenChange(<internal>) 167 168 /** Builder for {@link DownloadException}. */ 169 public static final class Builder { 170 private DownloadResultCode downloadResultCode; 171 private String message; 172 private Throwable cause; 173 174 /** Sets the {@link DownloadResultCode}. */ 175 @CanIgnoreReturnValue setDownloadResultCode(DownloadResultCode downloadResultCode)176 public Builder setDownloadResultCode(DownloadResultCode downloadResultCode) { 177 this.downloadResultCode = downloadResultCode; 178 return this; 179 } 180 181 /** Sets the error message. */ 182 @CanIgnoreReturnValue setMessage(String message)183 public Builder setMessage(String message) { 184 this.message = message; 185 return this; 186 } 187 188 /** Sets the cause of the exception. */ 189 @CanIgnoreReturnValue setCause(Throwable cause)190 public Builder setCause(Throwable cause) { 191 this.cause = cause; 192 return this; 193 } 194 195 /** Returns a {@link DownloadException} instance. */ build()196 public DownloadException build() { 197 Preconditions.checkNotNull(downloadResultCode); 198 if (message == null) { 199 message = "Download result code: " + downloadResultCode.name(); 200 } 201 return new DownloadException(this); 202 } 203 } 204 205 /** Returns a Builder for {@link DownloadException}. */ builder()206 public static Builder builder() { 207 return new Builder(); 208 } 209 getDownloadResultCode()210 public DownloadResultCode getDownloadResultCode() { 211 return downloadResultCode; 212 } 213 214 /** 215 * Wraps the throwable with {@link DownloadException} and returns a failed future only if the 216 * input future fails. 217 */ wrapIfFailed( ListenableFuture<T> future, DownloadResultCode code, String message)218 public static <T> ListenableFuture<T> wrapIfFailed( 219 ListenableFuture<T> future, DownloadResultCode code, String message) { 220 return PropagatedFutures.catchingAsync( 221 future, 222 Throwable.class, 223 (Throwable t) -> immediateFailedFuture(wrap(t, code, message)), 224 MoreExecutors.directExecutor()); 225 } 226 227 /** Wraps the throwable with {@link DownloadException}. */ wrap( Throwable throwable, DownloadResultCode code, String message)228 private static DownloadException wrap( 229 Throwable throwable, DownloadResultCode code, String message) { 230 return DownloadException.builder() 231 .setDownloadResultCode(code) 232 .setMessage(message) 233 .setCause(throwable) 234 .build(); 235 } 236 DownloadException(Builder builder)237 private DownloadException(Builder builder) { 238 super(builder.message, builder.cause); 239 this.downloadResultCode = builder.downloadResultCode; 240 } 241 } 242