/* * Copyright (C) 2022 The Android Open Source Project * * 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.android.ondevicepersonalization.services.download.mdd; import static com.google.common.util.concurrent.Futures.immediateFailedFuture; import static com.google.common.util.concurrent.Futures.immediateVoidFuture; import android.content.Context; import android.net.Uri; import com.android.ondevicepersonalization.internal.util.LoggerFactory; import com.google.android.libraries.mobiledatadownload.DownloadException; import com.google.android.libraries.mobiledatadownload.downloader.DownloadRequest; import com.google.android.libraries.mobiledatadownload.downloader.FileDownloader; import com.google.android.libraries.mobiledatadownload.file.Opener; import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage; import com.google.android.libraries.mobiledatadownload.file.openers.WriteStreamOpener; import com.google.common.io.ByteStreams; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import java.io.InputStream; import java.io.OutputStream; import java.util.Set; import java.util.concurrent.Executor; /** * A {@link FileDownloader} that "downloads" by copying the file from the Resources. * Files for local download should be placed in the package's Resources. * *

Note that OnDevicePersonalizationLocalFileDownloader ignores DownloadConditions. */ public final class OnDevicePersonalizationLocalFileDownloader implements FileDownloader { private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger(); private static final String TAG = "OnDevicePersonalizationLocalFileDownloader"; /** * The uri to download should be formatted as an android.resource uri: * android.resource://// */ private static final Set sDebugSchemes = Set.of("android.resource", "file"); private final Executor mExecutor; private final SynchronousFileStorage mFileStorage; private final Context mContext; public OnDevicePersonalizationLocalFileDownloader( SynchronousFileStorage fileStorage, Executor executor, Context context) { this.mFileStorage = fileStorage; this.mExecutor = executor; this.mContext = context; } /** * Determines if given uri is local odp uri * * @return true if uri is a local odp uri, false otherwise */ public static boolean isLocalOdpUri(Uri uri) { String scheme = uri.getScheme(); if (scheme != null && sDebugSchemes.contains(scheme)) { return true; } return false; } /** * Performs a localFile download for the given request */ @Override public ListenableFuture startDownloading(DownloadRequest downloadRequest) { return Futures.submitAsync(() -> startDownloadingInternal(downloadRequest), mExecutor); } private ListenableFuture startDownloadingInternal(DownloadRequest downloadRequest) { Uri fileUri = downloadRequest.fileUri(); String urlToDownload = downloadRequest.urlToDownload(); Uri uriToDownload = Uri.parse(urlToDownload); // Strip away the query params for local download. uriToDownload = new Uri.Builder() .scheme(uriToDownload.getScheme()) .authority(uriToDownload.getAuthority()) .path(uriToDownload.getPath()).build(); sLogger.d(TAG + ": Starting local download for url: " + urlToDownload); try { Opener writeStreamOpener = WriteStreamOpener.create(); long writtenBytes; try (OutputStream out = mFileStorage.open(fileUri, writeStreamOpener)) { InputStream in = mContext.getContentResolver().openInputStream(uriToDownload); writtenBytes = ByteStreams.copy(in, out); } sLogger.d(TAG + ": File URI " + fileUri + " download complete, writtenBytes: " + writtenBytes); } catch (Exception e) { sLogger.e(e, TAG + ": %s: startDownloading got exception", urlToDownload); return immediateFailedFuture( DownloadException.builder() .setDownloadResultCode( DownloadException.DownloadResultCode .ANDROID_DOWNLOADER_HTTP_ERROR) .build()); } return immediateVoidFuture(); } }