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