1 /* 2 * Copyright (C) 2022 The Android Open Source Project 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 17 package com.android.ondevicepersonalization.services.download.mdd; 18 19 import static com.google.common.util.concurrent.Futures.immediateFailedFuture; 20 import static com.google.common.util.concurrent.Futures.immediateVoidFuture; 21 22 import android.content.Context; 23 import android.net.Uri; 24 25 26 import com.android.ondevicepersonalization.internal.util.LoggerFactory; 27 28 import com.google.android.libraries.mobiledatadownload.DownloadException; 29 import com.google.android.libraries.mobiledatadownload.downloader.DownloadRequest; 30 import com.google.android.libraries.mobiledatadownload.downloader.FileDownloader; 31 import com.google.android.libraries.mobiledatadownload.file.Opener; 32 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage; 33 import com.google.android.libraries.mobiledatadownload.file.openers.WriteStreamOpener; 34 import com.google.common.io.ByteStreams; 35 import com.google.common.util.concurrent.Futures; 36 import com.google.common.util.concurrent.ListenableFuture; 37 38 import java.io.InputStream; 39 import java.io.OutputStream; 40 import java.util.Set; 41 import java.util.concurrent.Executor; 42 43 /** 44 * A {@link FileDownloader} that "downloads" by copying the file from the Resources. 45 * Files for local download should be placed in the package's Resources. 46 * 47 * <p>Note that OnDevicePersonalizationLocalFileDownloader ignores DownloadConditions. 48 */ 49 public final class OnDevicePersonalizationLocalFileDownloader implements FileDownloader { 50 51 private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); 52 private static final String TAG = "OnDevicePersonalizationLocalFileDownloader"; 53 54 /** 55 * The uri to download should be formatted as an android.resource uri: 56 * android.resource://<package_name>/<resource_type>/<resource_name> 57 */ 58 private static final Set<String> sDebugSchemes = Set.of("android.resource", "file"); 59 60 private final Executor mExecutor; 61 private final SynchronousFileStorage mFileStorage; 62 private final Context mContext; 63 OnDevicePersonalizationLocalFileDownloader( SynchronousFileStorage fileStorage, Executor executor, Context context)64 public OnDevicePersonalizationLocalFileDownloader( 65 SynchronousFileStorage fileStorage, Executor executor, 66 Context context) { 67 this.mFileStorage = fileStorage; 68 this.mExecutor = executor; 69 this.mContext = context; 70 } 71 72 /** 73 * Determines if given uri is local odp uri 74 * 75 * @return true if uri is a local odp uri, false otherwise 76 */ isLocalOdpUri(Uri uri)77 public static boolean isLocalOdpUri(Uri uri) { 78 String scheme = uri.getScheme(); 79 if (scheme != null && sDebugSchemes.contains(scheme)) { 80 return true; 81 } 82 return false; 83 } 84 85 /** 86 * Performs a localFile download for the given request 87 */ 88 @Override startDownloading(DownloadRequest downloadRequest)89 public ListenableFuture<Void> startDownloading(DownloadRequest downloadRequest) { 90 return Futures.submitAsync(() -> startDownloadingInternal(downloadRequest), 91 mExecutor); 92 } 93 startDownloadingInternal(DownloadRequest downloadRequest)94 private ListenableFuture<Void> startDownloadingInternal(DownloadRequest downloadRequest) { 95 Uri fileUri = downloadRequest.fileUri(); 96 String urlToDownload = downloadRequest.urlToDownload(); 97 Uri uriToDownload = Uri.parse(urlToDownload); 98 // Strip away the query params for local download. 99 uriToDownload = new Uri.Builder() 100 .scheme(uriToDownload.getScheme()) 101 .authority(uriToDownload.getAuthority()) 102 .path(uriToDownload.getPath()).build(); 103 sLogger.d(TAG + ": Starting local download for url: " + urlToDownload); 104 105 try { 106 Opener<OutputStream> writeStreamOpener = WriteStreamOpener.create(); 107 long writtenBytes; 108 try (OutputStream out = mFileStorage.open(fileUri, writeStreamOpener)) { 109 InputStream in = mContext.getContentResolver().openInputStream(uriToDownload); 110 writtenBytes = ByteStreams.copy(in, out); 111 } 112 sLogger.d(TAG + ": File URI " + fileUri 113 + " download complete, writtenBytes: " + writtenBytes); 114 } catch (Exception e) { 115 sLogger.e(e, TAG + ": %s: startDownloading got exception", urlToDownload); 116 return immediateFailedFuture( 117 DownloadException.builder() 118 .setDownloadResultCode( 119 DownloadException.DownloadResultCode 120 .ANDROID_DOWNLOADER_HTTP_ERROR) 121 .build()); 122 } 123 124 return immediateVoidFuture(); 125 } 126 } 127