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.downloader.offroad.dagger.downloader2; 17 18 import static com.google.common.util.concurrent.Futures.immediateFuture; 19 20 import android.content.Context; 21 22 import com.google.android.downloader.AndroidConnectivityHandler; 23 import com.google.android.downloader.Downloader; 24 import com.google.android.downloader.Downloader.StateChangeCallback; 25 import com.google.android.downloader.FloggerDownloaderLogger; 26 import com.google.android.downloader.PlatformUrlEngine; 27 import com.google.android.downloader.UrlEngine; 28 import com.google.android.libraries.mobiledatadownload.Flags; 29 import com.google.android.libraries.mobiledatadownload.annotations.MddControlExecutor; 30 import com.google.android.libraries.mobiledatadownload.annotations.MddDownloadExecutor; 31 import com.google.android.libraries.mobiledatadownload.annotations.SocketTrafficTag; 32 import com.google.android.libraries.mobiledatadownload.downloader.FileDownloader; 33 import com.google.android.libraries.mobiledatadownload.downloader.OAuthTokenProvider; 34 import com.google.android.libraries.mobiledatadownload.downloader.offroad.ExceptionHandler; 35 import com.google.android.libraries.mobiledatadownload.downloader.offroad.Offroad2FileDownloader; 36 import com.google.android.libraries.mobiledatadownload.downloader.offroad.dagger.BaseOffroadFileDownloaderModule; 37 import com.google.android.libraries.mobiledatadownload.file.SynchronousFileStorage; 38 import com.google.android.libraries.mobiledatadownload.file.integration.downloader.DownloadMetadataStore; 39 import com.google.android.libraries.mobiledatadownload.monitor.DownloadProgressMonitor; 40 import com.google.common.base.Optional; 41 import com.google.common.base.Supplier; 42 import com.google.common.util.concurrent.ListeningExecutorService; 43 44 import java.util.concurrent.ScheduledExecutorService; 45 46 import javax.annotation.Nullable; 47 import javax.inject.Singleton; 48 49 import dagger.Lazy; 50 import dagger.Module; 51 import dagger.Provides; 52 import dagger.multibindings.IntoMap; 53 import dagger.multibindings.StringKey; 54 55 /** 56 * Dagger module for providing FileDownloader that uses Android Downloader2. 57 * 58 * <p>This module should only be used when {@link MultiSchemeFileDownloader} is being provided by 59 * {@link FileDownloaderModule}. That module includes a map of FileDownloader Suppliers, which this 60 * module assumes is available to bind into. 61 */ 62 @Module( 63 includes = { 64 BaseOffroadFileDownloaderModule.class, 65 BaseFileDownloaderDepsModule.class, 66 }) 67 public abstract class BaseFileDownloaderModule { 68 @Provides 69 @Singleton 70 @IntoMap 71 @StringKey("https") provideFileDownloader( Context context, @MddDownloadExecutor ScheduledExecutorService downloadExecutor, @MddControlExecutor ListeningExecutorService controlExecutor, SynchronousFileStorage fileStorage, DownloadMetadataStore downloadMetadataStore, Optional<DownloadProgressMonitor> downloadProgressMonitor, Optional<Lazy<UrlEngine>> urlEngineOptional, Optional<Lazy<ExceptionHandler>> exceptionHandlerOptional, Optional<Lazy<OAuthTokenProvider>> authTokenProviderOptional, @SocketTrafficTag Optional<Integer> trafficTag, Flags flags)72 static Supplier<FileDownloader> provideFileDownloader( 73 Context context, 74 @MddDownloadExecutor ScheduledExecutorService downloadExecutor, 75 @MddControlExecutor ListeningExecutorService controlExecutor, 76 SynchronousFileStorage fileStorage, 77 DownloadMetadataStore downloadMetadataStore, 78 Optional<DownloadProgressMonitor> downloadProgressMonitor, 79 Optional<Lazy<UrlEngine>> urlEngineOptional, 80 Optional<Lazy<ExceptionHandler>> exceptionHandlerOptional, 81 Optional<Lazy<OAuthTokenProvider>> authTokenProviderOptional, 82 // Optional<Supplier<CookieJar>> cookieJarSupplierOptional, 83 @SocketTrafficTag Optional<Integer> trafficTag, 84 Flags flags) { 85 return () -> 86 createOffroad2FileDownloader( 87 context, 88 downloadExecutor, 89 controlExecutor, 90 fileStorage, 91 downloadMetadataStore, 92 downloadProgressMonitor, 93 urlEngineOptional, 94 exceptionHandlerOptional, 95 authTokenProviderOptional, 96 // cookieJarSupplierOptional, 97 trafficTag, 98 flags); 99 } 100 101 /** 102 * Manual provider of Offroad2FileDownloader. 103 * 104 * <p>NOTE: This method should only be used when manually wiring up dependencies, such as when 105 * dagger/hilt are not available. If using dagger/hilt, this method is not needed. By 106 * registering 107 * this module in the dagger graph, the above @Provides method will automatically provide this 108 * dependency. 109 */ createOffroad2FileDownloader( Context context, ScheduledExecutorService downloadExecutor, ListeningExecutorService controlExecutor, SynchronousFileStorage fileStorage, DownloadMetadataStore downloadMetadataStore, Optional<DownloadProgressMonitor> downloadProgressMonitor, Optional<Lazy<UrlEngine>> urlEngineOptional, Optional<Lazy<ExceptionHandler>> exceptionHandlerOptional, Optional<Lazy<OAuthTokenProvider>> authTokenProviderOptional, Optional<Integer> trafficTag, Flags flags)110 public static Offroad2FileDownloader createOffroad2FileDownloader( 111 Context context, 112 ScheduledExecutorService downloadExecutor, 113 ListeningExecutorService controlExecutor, 114 SynchronousFileStorage fileStorage, 115 DownloadMetadataStore downloadMetadataStore, 116 Optional<DownloadProgressMonitor> downloadProgressMonitor, 117 Optional<Lazy<UrlEngine>> urlEngineOptional, 118 Optional<Lazy<ExceptionHandler>> exceptionHandlerOptional, 119 Optional<Lazy<OAuthTokenProvider>> authTokenProviderOptional, 120 // Optional<Supplier<CookieJar>> cookieJarSupplierOptional, 121 Optional<Integer> trafficTag, 122 Flags flags) { 123 @Nullable 124 com.google.android.downloader.OAuthTokenProvider authTokenProvider = 125 authTokenProviderOptional.isPresent() 126 ? convertToDownloaderAuthTokenProvider( 127 authTokenProviderOptional.get().get()) 128 : null; 129 130 ExceptionHandler handler = 131 exceptionHandlerOptional.transform(Lazy::get).or( 132 ExceptionHandler.withDefaultHandling()); 133 134 UrlEngine urlEngine; 135 if (urlEngineOptional.isPresent()) { 136 urlEngine = urlEngineOptional.get().get(); 137 } else { 138 // Use {@link PlatformUrlEngine} if one was not provided. 139 urlEngine = 140 new PlatformUrlEngine( 141 controlExecutor, 142 /* connectTimeoutMs = */ flags.timeToWaitForDownloader(), 143 /* readTimeoutMs = */ flags.timeToWaitForDownloader() 144 ); 145 } 146 147 AndroidConnectivityHandler connectivityHandler = 148 new AndroidConnectivityHandler( 149 context, downloadExecutor, /* timeoutMillis= */ 150 flags.timeToWaitForDownloader()); 151 152 FloggerDownloaderLogger logger = new FloggerDownloaderLogger(); 153 154 Downloader downloader = 155 new Downloader.Builder() 156 .withIOExecutor(controlExecutor) 157 .withConnectivityHandler(connectivityHandler) 158 .withMaxConcurrentDownloads(flags.downloaderMaxThreads()) 159 .withLogger(logger) 160 .addUrlEngine("https", urlEngine) 161 .build(); 162 163 if (downloadProgressMonitor.isPresent()) { 164 // Wire up downloader's state changes to DownloadProgressMonitor to handle connectivity 165 // pauses. 166 StateChangeCallback callback = 167 state -> { 168 if (state.getNumDownloadsPendingConnectivity() > 0 169 && state.getNumDownloadsInFlight() == 0) { 170 // Handle network connectivity pauses 171 downloadProgressMonitor.get().pausedForConnectivity(); 172 } 173 }; 174 downloader.registerStateChangeCallback(callback, controlExecutor); 175 } 176 177 return new Offroad2FileDownloader( 178 downloader, 179 fileStorage, 180 downloadExecutor, 181 authTokenProvider, 182 downloadMetadataStore, 183 handler, 184 // cookieJarSupplierOptional, 185 trafficTag); 186 } 187 188 private static com.google.android.downloader.OAuthTokenProvider convertToDownloaderAuthTokenProvider(OAuthTokenProvider authTokenProvider)189 convertToDownloaderAuthTokenProvider(OAuthTokenProvider authTokenProvider) { 190 return uri -> immediateFuture(authTokenProvider.provideOAuthToken(uri.toString())); 191 } 192 BaseFileDownloaderModule()193 private BaseFileDownloaderModule() { 194 } 195 } 196