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