• 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;
17 
18 import android.net.Uri;
19 import com.google.android.libraries.mobiledatadownload.file.common.Sizable;
20 import com.google.android.libraries.mobiledatadownload.file.common.internal.ForwardingInputStream;
21 import com.google.android.libraries.mobiledatadownload.file.common.internal.Preconditions;
22 import com.google.android.libraries.mobiledatadownload.file.spi.Monitor;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.util.ArrayList;
26 import java.util.List;
27 import javax.annotation.Nullable;
28 
29 /** Stream that invokes input stream monitors. */
30 final class MonitorInputStream extends ForwardingInputStream implements Sizable {
31   private final List<Monitor.InputMonitor> inputMonitors;
32 
MonitorInputStream(InputStream input, List<Monitor.InputMonitor> inputMonitors)33   private MonitorInputStream(InputStream input, List<Monitor.InputMonitor> inputMonitors) {
34     super(input);
35     this.inputMonitors = inputMonitors;
36     Preconditions.checkArgument(input != null, "Input was null");
37   }
38 
39   /**
40    * Wraps {@code in} with a new stream that orchestrates {@code monitors}, or returns null if this
41    * IO doesn't need to be monitored.
42    */
43   @Nullable
newInstance(List<Monitor> monitors, Uri uri, InputStream in)44   public static MonitorInputStream newInstance(List<Monitor> monitors, Uri uri, InputStream in) {
45     List<Monitor.InputMonitor> inputMonitors = new ArrayList<>();
46     for (Monitor monitor : monitors) {
47       Monitor.InputMonitor inputMonitor = monitor.monitorRead(uri);
48       if (inputMonitor != null) {
49         inputMonitors.add(inputMonitor);
50       }
51     }
52     return !inputMonitors.isEmpty() ? new MonitorInputStream(in, inputMonitors) : null;
53   }
54 
55   @Override
read()56   public int read() throws IOException {
57     int result = in.read();
58     if (result != -1) {
59       byte[] b = new byte[] {(byte) result};
60       for (Monitor.InputMonitor inputMonitor : inputMonitors) {
61         inputMonitor.bytesRead(b, 0, 1);
62       }
63     }
64     return result;
65   }
66 
67   @Override
read(byte[] b)68   public int read(byte[] b) throws IOException {
69     int result = in.read(b);
70     if (result != -1) {
71       for (Monitor.InputMonitor inputMonitor : inputMonitors) {
72         inputMonitor.bytesRead(b, 0, result);
73       }
74     }
75     return result;
76   }
77 
78   @Override
read(byte[] b, int off, int len)79   public int read(byte[] b, int off, int len) throws IOException {
80     int result = in.read(b, off, len);
81     if (result != -1) {
82       for (Monitor.InputMonitor inputMonitor : inputMonitors) {
83         inputMonitor.bytesRead(b, off, result);
84       }
85     }
86     return result;
87   }
88 
89   @Nullable
90   @Override
size()91   public Long size() throws IOException {
92     if (!(in instanceof Sizable)) {
93       return null;
94     }
95     return ((Sizable) in).size();
96   }
97 
98   @Override
close()99   public void close() throws IOException {
100     for (Monitor.InputMonitor inputMonitor : inputMonitors) {
101       try {
102         inputMonitor.close();
103       } catch (Throwable t) {
104         // Ignore.
105       }
106     }
107     super.close();
108   }
109 }
110