• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.file.behaviors;
17 
18 import android.net.Uri;
19 import com.google.android.libraries.mobiledatadownload.file.common.Fragment;
20 import com.google.android.libraries.mobiledatadownload.file.common.ParamComputer;
21 import java.util.List;
22 import java.util.concurrent.CountDownLatch;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.Future;
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.TimeoutException;
27 
28 /**
29  * Computed Uris are available only after the stream has been fully consumed or closed. This class
30  * enforces that behavior, and prevents any race conditions that could otherwise happen if a client
31  * tried to fetch the computed uri while another thread was still processing the stream.
32  */
33 // TODO: ShouldNotSubclass Future
34 @SuppressWarnings("ShouldNotSubclass")
35 final class ComputedUriFuture implements Future<Uri>, ParamComputer.Callback {
36   private static final String TRANSFORM_PARAM = "transform";
37 
38   private final Uri uri;
39   private final Fragment fragment;
40   private final CountDownLatch countDownLatch;
41   private final Fragment.Param.Builder transformsParamBuilder;
42 
43   /** Construct a new instance with the original uri, and param computers. */
ComputedUriFuture(Uri uri, List<ParamComputer> paramComputers)44   ComputedUriFuture(Uri uri, List<ParamComputer> paramComputers) {
45     this.uri = uri;
46     this.fragment = Fragment.parse(uri);
47     countDownLatch = new CountDownLatch(paramComputers.size());
48     for (ParamComputer paramComputer : paramComputers) {
49       paramComputer.setCallback(this);
50     }
51     Fragment.Param transformParam = fragment.findParam(TRANSFORM_PARAM);
52     this.transformsParamBuilder =
53         (transformParam != null)
54             ? transformParam.toBuilder()
55             : Fragment.Param.builder(TRANSFORM_PARAM);
56   }
57 
58   @Override
onParamValueComputed(Fragment.ParamValue value)59   public void onParamValueComputed(Fragment.ParamValue value) {
60     transformsParamBuilder.addValue(value);
61     countDownLatch.countDown();
62   }
63 
64   @Override
cancel(boolean mayInterruptIfRunning)65   public boolean cancel(boolean mayInterruptIfRunning) {
66     return false;
67   }
68 
69   @Override
isCancelled()70   public boolean isCancelled() {
71     return false;
72   }
73 
74   @Override
isDone()75   public boolean isDone() {
76     return (countDownLatch.getCount() == 0);
77   }
78 
79   @Override
get()80   public Uri get() throws InterruptedException, ExecutionException {
81     countDownLatch.await();
82     Fragment computedFragment = fragment.toBuilder().addParam(transformsParamBuilder).build();
83     return uri.buildUpon().encodedFragment(computedFragment.toString()).build();
84   }
85 
86   @Override
get(long timeout, TimeUnit unit)87   public Uri get(long timeout, TimeUnit unit)
88       throws InterruptedException, ExecutionException, TimeoutException {
89     if (!countDownLatch.await(timeout, unit)) {
90       throw new TimeoutException();
91     }
92     return get();
93   }
94 }
95