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.populator; 17 18 import static com.google.common.util.concurrent.Futures.immediateFuture; 19 20 import android.util.Log; 21 import com.google.android.libraries.mobiledatadownload.AddFileGroupRequest; 22 import com.google.android.libraries.mobiledatadownload.FileGroupPopulator; 23 import com.google.android.libraries.mobiledatadownload.MobileDataDownload; 24 import com.google.android.libraries.mobiledatadownload.internal.logging.LogUtil; 25 import com.google.android.libraries.mobiledatadownload.tracing.PropagatedFutures; 26 import com.google.common.base.Optional; 27 import com.google.common.base.Supplier; 28 import com.google.common.util.concurrent.FutureCallback; 29 import com.google.common.util.concurrent.ListenableFuture; 30 import com.google.common.util.concurrent.MoreExecutors; 31 import com.google.errorprone.annotations.CanIgnoreReturnValue; 32 import com.google.mobiledatadownload.DownloadConfigProto.DataFileGroup; 33 34 /** 35 * FileGroupPopulator that gets a single file group from a supplier and populates it. 36 * 37 * <p>Client can set an optional file group overrider to override fields in the {@link 38 * DataFileGroup} if needed. 39 * 40 * <p>The overrider could also be used for on device targeting and filtering in the case that the 41 * file group is provided from a server. 42 */ 43 public final class SingleDataFileGroupPopulator implements FileGroupPopulator { 44 45 private static final String TAG = "SingleDataFileGroupPop"; 46 47 /** Builder for {@link SingleDataFileGroupPopulator}. */ 48 public static final class Builder { 49 private Supplier<DataFileGroup> dataFileGroupSupplier; 50 private Optional<DataFileGroupOverrider> overriderOptional = Optional.absent(); 51 52 @CanIgnoreReturnValue setDataFileGroupSupplier(Supplier<DataFileGroup> dataFileGroupSupplier)53 public Builder setDataFileGroupSupplier(Supplier<DataFileGroup> dataFileGroupSupplier) { 54 this.dataFileGroupSupplier = dataFileGroupSupplier; 55 return this; 56 } 57 58 /** 59 * Sets the optional file group overrider that takes the {@link DataFileGroup} and returns a 60 * {@link DataFileGroup} after being overridden. If the overrider returns a null data file 61 * group, nothing will be populated. 62 */ 63 @CanIgnoreReturnValue setOverriderOptional(Optional<DataFileGroupOverrider> overriderOptional)64 public Builder setOverriderOptional(Optional<DataFileGroupOverrider> overriderOptional) { 65 this.overriderOptional = overriderOptional; 66 return this; 67 } 68 build()69 public SingleDataFileGroupPopulator build() { 70 return new SingleDataFileGroupPopulator(this); 71 } 72 } 73 74 private final Supplier<DataFileGroup> dataFileGroupSupplier; 75 private final Optional<DataFileGroupOverrider> overriderOptional; 76 77 /** Returns a Builder for SingleDataFileGroupPopulator. */ builder()78 public static Builder builder() { 79 return new Builder(); 80 } 81 SingleDataFileGroupPopulator(Builder builder)82 private SingleDataFileGroupPopulator(Builder builder) { 83 this.dataFileGroupSupplier = builder.dataFileGroupSupplier; 84 this.overriderOptional = builder.overriderOptional; 85 } 86 87 @Override refreshFileGroups(MobileDataDownload mobileDataDownload)88 public ListenableFuture<Void> refreshFileGroups(MobileDataDownload mobileDataDownload) { 89 LogUtil.d("%s: Add file group to Mdd.", TAG); 90 91 // Override data file group if the overrider is present. If the overrider returns an absent 92 // data file group, nothing will be populated. 93 ListenableFuture<Optional<DataFileGroup>> dataFileGroupOptionalFuture = 94 immediateFuture(Optional.absent()); 95 if (dataFileGroupSupplier.get() != null 96 && !dataFileGroupSupplier.get().getGroupName().isEmpty()) { 97 dataFileGroupOptionalFuture = 98 overriderOptional.isPresent() 99 ? overriderOptional.get().override(dataFileGroupSupplier.get()) 100 : immediateFuture(Optional.of(dataFileGroupSupplier.get())); 101 } 102 103 ListenableFuture<Boolean> addFileGroupFuture = 104 PropagatedFutures.transformAsync( 105 dataFileGroupOptionalFuture, 106 dataFileGroupOptional -> { 107 if (dataFileGroupOptional.isPresent() 108 && !dataFileGroupOptional.get().getGroupName().isEmpty()) { 109 return mobileDataDownload.addFileGroup( 110 AddFileGroupRequest.newBuilder() 111 .setDataFileGroup(dataFileGroupOptional.get()) 112 .build()); 113 } 114 LogUtil.d("%s: Not adding file group because of overrider.", TAG); 115 return immediateFuture(false); 116 }, 117 MoreExecutors.directExecutor()); 118 119 PropagatedFutures.addCallback( 120 addFileGroupFuture, 121 new FutureCallback<Boolean>() { 122 @Override 123 public void onSuccess(Boolean result) { 124 String groupName = dataFileGroupSupplier.get().getGroupName(); 125 if (result.booleanValue()) { 126 Log.d(TAG, "Added file group " + groupName); 127 } else { 128 Log.e(TAG, "Failed to add file group " + groupName); 129 } 130 } 131 132 @Override 133 public void onFailure(Throwable t) { 134 Log.e(TAG, "Failed to add file group", t); 135 } 136 }, 137 MoreExecutors.directExecutor()); 138 139 return PropagatedFutures.whenAllComplete(addFileGroupFuture) 140 .call(() -> null, MoreExecutors.directExecutor()); 141 } 142 } 143