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.openers; 17 18 import android.annotation.TargetApi; 19 import android.os.Build; 20 import android.os.Process; 21 import android.system.ErrnoException; 22 import android.system.Os; 23 import android.system.OsConstants; 24 import com.google.android.libraries.mobiledatadownload.file.common.UnsupportedFileStorageOperation; 25 import java.io.File; 26 import java.io.IOException; 27 import java.util.concurrent.ExecutionException; 28 import java.util.concurrent.Future; 29 import java.util.concurrent.atomic.AtomicInteger; 30 31 /** Static utility methods pertaining to Posix pipes and streaming data through them. */ 32 final class Pipes { 33 34 private static final int MY_PID = Process.myPid(); 35 36 /** 37 * Creates a named pipe (FIFO) in {@code directory} that's guaranteed to not collide with any 38 * other running process. {@code tag} and {@code idGenerator} should be static and specific to the 39 * caller's class in order to avoid naming collisions with other callers within the same process. 40 * 41 * @throws IOException if the FIFO cannot be created or is not supported at this SDK level (21+) 42 */ 43 @TargetApi(Build.VERSION_CODES.LOLLIPOP) // for ErrnoException, Os, and OsConstants makeFifo(File directory, String tag, AtomicInteger idGenerator)44 static File makeFifo(File directory, String tag, AtomicInteger idGenerator) throws IOException { 45 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 46 throw new UnsupportedFileStorageOperation( 47 String.format("FIFOs require SDK level 21+; current level is %d", Build.VERSION.SDK_INT)); 48 } 49 int fifoId = idGenerator.getAndIncrement(); 50 String fifoName = ".mobstore-" + tag + "-" + MY_PID + "-" + fifoId + ".fifo"; 51 File fifoFile = new File(directory, fifoName); 52 fifoFile.delete(); // Delete stale FIFO if it exists (it shouldn't) 53 try { 54 Os.mkfifo(fifoFile.getAbsolutePath(), OsConstants.S_IRUSR | OsConstants.S_IWUSR); 55 return fifoFile; 56 } catch (ErrnoException e) { 57 fifoFile.delete(); 58 throw new IOException(e); 59 } 60 } 61 62 /** 63 * Blocks on {@code pumpFuture.get()} and propagates any exception it may have encountered as an 64 * {@code IOException}. 65 */ getAndPropagateAsIOException(Future<Throwable> pumpFuture)66 static void getAndPropagateAsIOException(Future<Throwable> pumpFuture) throws IOException { 67 try { 68 Throwable throwable = pumpFuture.get(); 69 if (throwable != null) { 70 if (throwable instanceof IOException) { 71 throw (IOException) throwable; 72 } 73 throw new IOException(throwable); 74 } 75 } catch (InterruptedException e) { 76 Thread.currentThread().interrupt(); // per <internal> 77 throw new IOException(e); 78 } catch (ExecutionException e) { 79 if (e.getCause() instanceof IOException) { 80 throw (IOException) e.getCause(); 81 } 82 throw new IOException(e); 83 } 84 } 85 Pipes()86 private Pipes() {} 87 } 88