Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      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 
     17 package android.os;
     18 
     19 import static android.os.ParcelFileDescriptor.MODE_APPEND;
     20 import static android.os.ParcelFileDescriptor.MODE_CREATE;
     21 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
     22 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
     23 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
     24 import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
     25 import static android.system.OsConstants.F_OK;
     26 import static android.system.OsConstants.O_ACCMODE;
     27 import static android.system.OsConstants.O_APPEND;
     28 import static android.system.OsConstants.O_CREAT;
     29 import static android.system.OsConstants.O_RDONLY;
     30 import static android.system.OsConstants.O_RDWR;
     31 import static android.system.OsConstants.O_TRUNC;
     32 import static android.system.OsConstants.O_WRONLY;
     33 import static android.system.OsConstants.R_OK;
     34 import static android.system.OsConstants.SPLICE_F_MORE;
     35 import static android.system.OsConstants.SPLICE_F_MOVE;
     36 import static android.system.OsConstants.S_ISFIFO;
     37 import static android.system.OsConstants.S_ISREG;
     38 import static android.system.OsConstants.W_OK;
     39 
     40 import android.annotation.NonNull;
     41 import android.annotation.Nullable;
     42 import android.annotation.TestApi;
     43 import android.annotation.UnsupportedAppUsage;
     44 import android.content.ContentResolver;
     45 import android.provider.DocumentsContract.Document;
     46 import android.system.ErrnoException;
     47 import android.system.Os;
     48 import android.system.StructStat;
     49 import android.text.TextUtils;
     50 import android.util.Log;
     51 import android.util.Slog;
     52 import android.webkit.MimeTypeMap;
     53 
     54 import com.android.internal.annotations.VisibleForTesting;
     55 import com.android.internal.util.ArrayUtils;
     56 import com.android.internal.util.SizedInputStream;
     57 
     58 import libcore.io.IoUtils;
     59 import libcore.util.EmptyArray;
     60 
     61 import java.io.BufferedInputStream;
     62 import java.io.ByteArrayOutputStream;
     63 import java.io.File;
     64 import java.io.FileDescriptor;
     65 import java.io.FileInputStream;
     66 import java.io.FileNotFoundException;
     67 import java.io.FileOutputStream;
     68 import java.io.FilenameFilter;
     69 import java.io.IOException;
     70 import java.io.InputStream;
     71 import java.io.OutputStream;
     72 import java.nio.charset.StandardCharsets;
     73 import java.security.DigestInputStream;
     74 import java.security.MessageDigest;
     75 import java.security.NoSuchAlgorithmException;
     76 import java.util.Arrays;
     77 import java.util.Collection;
     78 import java.util.Comparator;
     79 import java.util.Objects;
     80 import java.util.concurrent.Executor;
     81 import java.util.concurrent.TimeUnit;
     82 import java.util.regex.Pattern;
     83 import java.util.zip.CRC32;
     84 import java.util.zip.CheckedInputStream;
     85 
     86 /**
     87  * Utility methods useful for working with files.
     88  */
     89 public final class FileUtils {
     90     private static final String TAG = "FileUtils";
     91 
     92     /** {@hide} */ public static final int S_IRWXU = 00700;
     93     /** {@hide} */ public static final int S_IRUSR = 00400;
     94     /** {@hide} */ public static final int S_IWUSR = 00200;
     95     /** {@hide} */ public static final int S_IXUSR = 00100;
     96 
     97     /** {@hide} */ public static final int S_IRWXG = 00070;
     98     /** {@hide} */ public static final int S_IRGRP = 00040;
     99     /** {@hide} */ public static final int S_IWGRP = 00020;
    100     /** {@hide} */ public static final int S_IXGRP = 00010;
    101 
    102     /** {@hide} */ public static final int S_IRWXO = 00007;
    103     /** {@hide} */ public static final int S_IROTH = 00004;
    104     /** {@hide} */ public static final int S_IWOTH = 00002;
    105     /** {@hide} */ public static final int S_IXOTH = 00001;
    106 
    107     @UnsupportedAppUsage
    108     private FileUtils() {
    109     }
    110 
    111     /** Regular expression for safe filenames: no spaces or metacharacters.
    112       *
    113       * Use a preload holder so that FileUtils can be compile-time initialized.
    114       */
    115     private static class NoImagePreloadHolder {
    116         public static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+");
    117     }
    118 
    119     // non-final so it can be toggled by Robolectric's ShadowFileUtils
    120     private static boolean sEnableCopyOptimizations = true;
    121 
    122     private static final long COPY_CHECKPOINT_BYTES = 524288;
    123 
    124     /**
    125      * Listener that is called periodically as progress is made.
    126      */
    127     public interface ProgressListener {
    128         public void onProgress(long progress);
    129     }
    130 
    131     /**
    132      * Set owner and mode of of given {@link File}.
    133      *
    134      * @param mode to apply through {@code chmod}
    135      * @param uid to apply through {@code chown}, or -1 to leave unchanged
    136      * @param gid to apply through {@code chown}, or -1 to leave unchanged
    137      * @return 0 on success, otherwise errno.
    138      * @hide
    139      */
    140     @UnsupportedAppUsage
    141     public static int setPermissions(File path, int mode, int uid, int gid) {
    142         return setPermissions(path.getAbsolutePath(), mode, uid, gid);
    143     }
    144 
    145     /**
    146      * Set owner and mode of of given path.
    147      *
    148      * @param mode to apply through {@code chmod}
    149      * @param uid to apply through {@code chown}, or -1 to leave unchanged
    150      * @param gid to apply through {@code chown}, or -1 to leave unchanged
    151      * @return 0 on success, otherwise errno.
    152      * @hide
    153      */
    154     @UnsupportedAppUsage
    155     public static int setPermissions(String path, int mode, int uid, int gid) {
    156         try {
    157             Os.chmod(path, mode);
    158         } catch (ErrnoException e) {
    159             Slog.w(TAG, "Failed to chmod(" + path + "): " + e);
    160             return e.errno;
    161         }
    162 
    163         if (uid >= 0 || gid >= 0) {
    164             try {
    165                 Os.chown(path, uid, gid);
    166             } catch (ErrnoException e) {
    167                 Slog.w(TAG, "Failed to chown(" + path + "): " + e);
    168                 return e.errno;
    169             }
    170         }
    171 
    172         return 0;
    173     }
    174 
    175     /**
    176      * Set owner and mode of of given {@link FileDescriptor}.
    177      *
    178      * @param mode to apply through {@code chmod}
    179      * @param uid to apply through {@code chown}, or -1 to leave unchanged
    180      * @param gid to apply through {@code chown}, or -1 to leave unchanged
    181      * @return 0 on success, otherwise errno.
    182      * @hide
    183      */
    184     @UnsupportedAppUsage
    185     public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) {
    186         try {
    187             Os.fchmod(fd, mode);
    188         } catch (ErrnoException e) {
    189             Slog.w(TAG, "Failed to fchmod(): " + e);
    190             return e.errno;
    191         }
    192 
    193         if (uid >= 0 || gid >= 0) {
    194             try {
    195                 Os.fchown(fd, uid, gid);
    196             } catch (ErrnoException e) {
    197                 Slog.w(TAG, "Failed to fchown(): " + e);
    198                 return e.errno;
    199             }
    200         }
    201 
    202         return 0;
    203     }
    204 
    205     /**
    206      * Copy the owner UID, owner GID, and mode bits from one file to another.
    207      *
    208      * @param from File where attributes should be copied from.
    209      * @param to File where attributes should be copied to.
    210      * @hide
    211      */
    212     public static void copyPermissions(@NonNull File from, @NonNull File to) throws IOException {
    213         try {
    214             final StructStat stat = Os.stat(from.getAbsolutePath());
    215             Os.chmod(to.getAbsolutePath(), stat.st_mode);
    216             Os.chown(to.getAbsolutePath(), stat.st_uid, stat.st_gid);
    217         } catch (ErrnoException e) {
    218             throw e.rethrowAsIOException();
    219         }
    220     }
    221 
    222     /**
    223      * @deprecated use {@link Os#stat(String)} instead.
    224      * @hide
    225      */
    226     @Deprecated
    227     public static int getUid(String path) {
    228         try {
    229             return Os.stat(path).st_uid;
    230         } catch (ErrnoException e) {
    231             return -1;
    232         }
    233     }
    234 
    235     /**
    236      * Perform an fsync on the given FileOutputStream.  The stream at this
    237      * point must be flushed but not yet closed.
    238      *
    239      * @hide
    240      */
    241     @UnsupportedAppUsage
    242     public static boolean sync(FileOutputStream stream) {
    243         try {
    244             if (stream != null) {
    245                 stream.getFD().sync();
    246             }
    247             return true;
    248         } catch (IOException e) {
    249         }
    250         return false;
    251     }
    252 
    253     /**
    254      * @deprecated use {@link #copy(File, File)} instead.
    255      * @hide
    256      */
    257     @UnsupportedAppUsage
    258     @Deprecated
    259     public static boolean copyFile(File srcFile, File destFile) {
    260         try {
    261             copyFileOrThrow(srcFile, destFile);
    262             return true;
    263         } catch (IOException e) {
    264             return false;
    265         }
    266     }
    267 
    268     /**
    269      * @deprecated use {@link #copy(File, File)} instead.
    270      * @hide
    271      */
    272     @Deprecated
    273     public static void copyFileOrThrow(File srcFile, File destFile) throws IOException {
    274         try (InputStream in = new FileInputStream(srcFile)) {
    275             copyToFileOrThrow(in, destFile);
    276         }
    277     }
    278 
    279     /**
    280      * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
    281      * @hide
    282      */
    283     @UnsupportedAppUsage
    284     @Deprecated
    285     public static boolean copyToFile(InputStream inputStream, File destFile) {
    286         try {
    287             copyToFileOrThrow(inputStream, destFile);
    288             return true;
    289         } catch (IOException e) {
    290             return false;
    291         }
    292     }
    293 
    294     /**
    295      * @deprecated use {@link #copy(InputStream, OutputStream)} instead.
    296      * @hide
    297      */
    298     @Deprecated
    299     public static void copyToFileOrThrow(InputStream in, File destFile) throws IOException {
    300         if (destFile.exists()) {
    301             destFile.delete();
    302         }
    303         try (FileOutputStream out = new FileOutputStream(destFile)) {
    304             copy(in, out);
    305             try {
    306                 Os.fsync(out.getFD());
    307             } catch (ErrnoException e) {
    308                 throw e.rethrowAsIOException();
    309             }
    310         }
    311     }
    312 
    313     /**
    314      * Copy the contents of one file to another, replacing any existing content.
    315      * <p>
    316      * Attempts to use several optimization strategies to copy the data in the
    317      * kernel before falling back to a userspace copy as a last resort.
    318      *
    319      * @return number of bytes copied.
    320      * @hide
    321      */
    322     public static long copy(@NonNull File from, @NonNull File to) throws IOException {
    323         return copy(from, to, null, null, null);
    324     }
    325 
    326     /**
    327      * Copy the contents of one file to another, replacing any existing content.
    328      * <p>
    329      * Attempts to use several optimization strategies to copy the data in the
    330      * kernel before falling back to a userspace copy as a last resort.
    331      *
    332      * @param signal to signal if the copy should be cancelled early.
    333      * @param executor that listener events should be delivered via.
    334      * @param listener to be periodically notified as the copy progresses.
    335      * @return number of bytes copied.
    336      * @hide
    337      */
    338     public static long copy(@NonNull File from, @NonNull File to,
    339             @Nullable CancellationSignal signal, @Nullable Executor executor,
    340             @Nullable ProgressListener listener) throws IOException {
    341         try (FileInputStream in = new FileInputStream(from);
    342                 FileOutputStream out = new FileOutputStream(to)) {
    343             return copy(in, out, signal, executor, listener);
    344         }
    345     }
    346 
    347     /**
    348      * Copy the contents of one stream to another.
    349      * <p>
    350      * Attempts to use several optimization strategies to copy the data in the
    351      * kernel before falling back to a userspace copy as a last resort.
    352      *
    353      * @return number of bytes copied.
    354      */
    355     public static long copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
    356         return copy(in, out, null, null, null);
    357     }
    358 
    359     /**
    360      * Copy the contents of one stream to another.
    361      * <p>
    362      * Attempts to use several optimization strategies to copy the data in the
    363      * kernel before falling back to a userspace copy as a last resort.
    364      *
    365      * @param signal to signal if the copy should be cancelled early.
    366      * @param executor that listener events should be delivered via.
    367      * @param listener to be periodically notified as the copy progresses.
    368      * @return number of bytes copied.
    369      */
    370     public static long copy(@NonNull InputStream in, @NonNull OutputStream out,
    371             @Nullable CancellationSignal signal, @Nullable Executor executor,
    372             @Nullable ProgressListener listener) throws IOException {
    373         if (sEnableCopyOptimizations) {
    374             if (in instanceof FileInputStream && out instanceof FileOutputStream) {
    375                 return copy(((FileInputStream) in).getFD(), ((FileOutputStream) out).getFD(),
    376                         signal, executor, listener);
    377             }
    378         }
    379 
    380         // Worse case fallback to userspace
    381         return copyInternalUserspace(in, out, signal, executor, listener);
    382     }
    383 
    384     /**
    385      * Copy the contents of one FD to another.
    386      * <p>
    387      * Attempts to use several optimization strategies to copy the data in the
    388      * kernel before falling back to a userspace copy as a last resort.
    389      *
    390      * @return number of bytes copied.
    391      */
    392     public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out)
    393             throws IOException {
    394         return copy(in, out, null, null, null);
    395     }
    396 
    397     /**
    398      * Copy the contents of one FD to another.
    399      * <p>
    400      * Attempts to use several optimization strategies to copy the data in the
    401      * kernel before falling back to a userspace copy as a last resort.
    402      *
    403      * @param signal to signal if the copy should be cancelled early.
    404      * @param executor that listener events should be delivered via.
    405      * @param listener to be periodically notified as the copy progresses.
    406      * @return number of bytes copied.
    407      */
    408     public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
    409             @Nullable CancellationSignal signal, @Nullable Executor executor,
    410             @Nullable ProgressListener listener) throws IOException {
    411         return copy(in, out, Long.MAX_VALUE, signal, executor, listener);
    412     }
    413 
    414     /**
    415      * Copy the contents of one FD to another.
    416      * <p>
    417      * Attempts to use several optimization strategies to copy the data in the
    418      * kernel before falling back to a userspace copy as a last resort.
    419      *
    420      * @param count the number of bytes to copy.
    421      * @param signal to signal if the copy should be cancelled early.
    422      * @param executor that listener events should be delivered via.
    423      * @param listener to be periodically notified as the copy progresses.
    424      * @return number of bytes copied.
    425      * @hide
    426      */
    427     public static long copy(@NonNull FileDescriptor in, @NonNull FileDescriptor out, long count,
    428             @Nullable CancellationSignal signal, @Nullable Executor executor,
    429             @Nullable ProgressListener listener) throws IOException {
    430         if (sEnableCopyOptimizations) {
    431             try {
    432                 final StructStat st_in = Os.fstat(in);
    433                 final StructStat st_out = Os.fstat(out);
    434                 if (S_ISREG(st_in.st_mode) && S_ISREG(st_out.st_mode)) {
    435                     return copyInternalSendfile(in, out, count, signal, executor, listener);
    436                 } else if (S_ISFIFO(st_in.st_mode) || S_ISFIFO(st_out.st_mode)) {
    437                     return copyInternalSplice(in, out, count, signal, executor, listener);
    438                 }
    439             } catch (ErrnoException e) {
    440                 throw e.rethrowAsIOException();
    441             }
    442         }
    443 
    444         // Worse case fallback to userspace
    445         return copyInternalUserspace(in, out, count, signal, executor, listener);
    446     }
    447 
    448     /**
    449      * Requires one of input or output to be a pipe.
    450      *
    451      * @hide
    452      */
    453     @VisibleForTesting
    454     public static long copyInternalSplice(FileDescriptor in, FileDescriptor out, long count,
    455             CancellationSignal signal, Executor executor, ProgressListener listener)
    456             throws ErrnoException {
    457         long progress = 0;
    458         long checkpoint = 0;
    459 
    460         long t;
    461         while ((t = Os.splice(in, null, out, null, Math.min(count, COPY_CHECKPOINT_BYTES),
    462                 SPLICE_F_MOVE | SPLICE_F_MORE)) != 0) {
    463             progress += t;
    464             checkpoint += t;
    465             count -= t;
    466 
    467             if (checkpoint >= COPY_CHECKPOINT_BYTES) {
    468                 if (signal != null) {
    469                     signal.throwIfCanceled();
    470                 }
    471                 if (executor != null && listener != null) {
    472                     final long progressSnapshot = progress;
    473                     executor.execute(() -> {
    474                         listener.onProgress(progressSnapshot);
    475                     });
    476                 }
    477                 checkpoint = 0;
    478             }
    479         }
    480         if (executor != null && listener != null) {
    481             final long progressSnapshot = progress;
    482             executor.execute(() -> {
    483                 listener.onProgress(progressSnapshot);
    484             });
    485         }
    486         return progress;
    487     }
    488 
    489     /**
    490      * Requires both input and output to be a regular file.
    491      *
    492      * @hide
    493      */
    494     @VisibleForTesting
    495     public static long copyInternalSendfile(FileDescriptor in, FileDescriptor out, long count,
    496             CancellationSignal signal, Executor executor, ProgressListener listener)
    497             throws ErrnoException {
    498         long progress = 0;
    499         long checkpoint = 0;
    500 
    501         long t;
    502         while ((t = Os.sendfile(out, in, null, Math.min(count, COPY_CHECKPOINT_BYTES))) != 0) {
    503             progress += t;
    504             checkpoint += t;
    505             count -= t;
    506 
    507             if (checkpoint >= COPY_CHECKPOINT_BYTES) {
    508                 if (signal != null) {
    509                     signal.throwIfCanceled();
    510                 }
    511                 if (executor != null && listener != null) {
    512                     final long progressSnapshot = progress;
    513                     executor.execute(() -> {
    514                         listener.onProgress(progressSnapshot);
    515                     });
    516                 }
    517                 checkpoint = 0;
    518             }
    519         }
    520         if (executor != null && listener != null) {
    521             final long progressSnapshot = progress;
    522             executor.execute(() -> {
    523                 listener.onProgress(progressSnapshot);
    524             });
    525         }
    526         return progress;
    527     }
    528 
    529     /** {@hide} */
    530     @Deprecated
    531     @VisibleForTesting
    532     public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out,
    533             ProgressListener listener, CancellationSignal signal, long count)
    534             throws IOException {
    535         return copyInternalUserspace(in, out, count, signal, Runnable::run, listener);
    536     }
    537 
    538     /** {@hide} */
    539     @VisibleForTesting
    540     public static long copyInternalUserspace(FileDescriptor in, FileDescriptor out, long count,
    541             CancellationSignal signal, Executor executor, ProgressListener listener)
    542             throws IOException {
    543         if (count != Long.MAX_VALUE) {
    544             return copyInternalUserspace(new SizedInputStream(new FileInputStream(in), count),
    545                     new FileOutputStream(out), signal, executor, listener);
    546         } else {
    547             return copyInternalUserspace(new FileInputStream(in),
    548                     new FileOutputStream(out), signal, executor, listener);
    549         }
    550     }
    551 
    552     /** {@hide} */
    553     @VisibleForTesting
    554     public static long copyInternalUserspace(InputStream in, OutputStream out,
    555             CancellationSignal signal, Executor executor, ProgressListener listener)
    556             throws IOException {
    557         long progress = 0;
    558         long checkpoint = 0;
    559         byte[] buffer = new byte[8192];
    560 
    561         int t;
    562         while ((t = in.read(buffer)) != -1) {
    563             out.write(buffer, 0, t);
    564 
    565             progress += t;
    566             checkpoint += t;
    567 
    568             if (checkpoint >= COPY_CHECKPOINT_BYTES) {
    569                 if (signal != null) {
    570                     signal.throwIfCanceled();
    571                 }
    572                 if (executor != null && listener != null) {
    573                     final long progressSnapshot = progress;
    574                     executor.execute(() -> {
    575                         listener.onProgress(progressSnapshot);
    576                     });
    577                 }
    578                 checkpoint = 0;
    579             }
    580         }
    581         if (executor != null && listener != null) {
    582             final long progressSnapshot = progress;
    583             executor.execute(() -> {
    584                 listener.onProgress(progressSnapshot);
    585             });
    586         }
    587         return progress;
    588     }
    589 
    590     /**
    591      * Check if a filename is "safe" (no metacharacters or spaces).
    592      * @param file  The file to check
    593      * @hide
    594      */
    595     @UnsupportedAppUsage
    596     public static boolean isFilenameSafe(File file) {
    597         // Note, we check whether it matches what's known to be safe,
    598         // rather than what's known to be unsafe.  Non-ASCII, control
    599         // characters, etc. are all unsafe by default.
    600         return NoImagePreloadHolder.SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches();
    601     }
    602 
    603     /**
    604      * Read a text file into a String, optionally limiting the length.
    605      * @param file to read (will not seek, so things like /proc files are OK)
    606      * @param max length (positive for head, negative of tail, 0 for no limit)
    607      * @param ellipsis to add of the file was truncated (can be null)
    608      * @return the contents of the file, possibly truncated
    609      * @throws IOException if something goes wrong reading the file
    610      * @hide
    611      */
    612     @UnsupportedAppUsage
    613     public static String readTextFile(File file, int max, String ellipsis) throws IOException {
    614         InputStream input = new FileInputStream(file);
    615         // wrapping a BufferedInputStream around it because when reading /proc with unbuffered
    616         // input stream, bytes read not equal to buffer size is not necessarily the correct
    617         // indication for EOF; but it is true for BufferedInputStream due to its implementation.
    618         BufferedInputStream bis = new BufferedInputStream(input);
    619         try {
    620             long size = file.length();
    621             if (max > 0 || (size > 0 && max == 0)) {  // "head" mode: read the first N bytes
    622                 if (size > 0 && (max == 0 || size < max)) max = (int) size;
    623                 byte[] data = new byte[max + 1];
    624                 int length = bis.read(data);
    625                 if (length <= 0) return "";
    626                 if (length <= max) return new String(data, 0, length);
    627                 if (ellipsis == null) return new String(data, 0, max);
    628                 return new String(data, 0, max) + ellipsis;
    629             } else if (max < 0) {  // "tail" mode: keep the last N
    630                 int len;
    631                 boolean rolled = false;
    632                 byte[] last = null;
    633                 byte[] data = null;
    634                 do {
    635                     if (last != null) rolled = true;
    636                     byte[] tmp = last; last = data; data = tmp;
    637                     if (data == null) data = new byte[-max];
    638                     len = bis.read(data);
    639                 } while (len == data.length);
    640 
    641                 if (last == null && len <= 0) return "";
    642                 if (last == null) return new String(data, 0, len);
    643                 if (len > 0) {
    644                     rolled = true;
    645                     System.arraycopy(last, len, last, 0, last.length - len);
    646                     System.arraycopy(data, 0, last, last.length - len, len);
    647                 }
    648                 if (ellipsis == null || !rolled) return new String(last);
    649                 return ellipsis + new String(last);
    650             } else {  // "cat" mode: size unknown, read it all in streaming fashion
    651                 ByteArrayOutputStream contents = new ByteArrayOutputStream();
    652                 int len;
    653                 byte[] data = new byte[1024];
    654                 do {
    655                     len = bis.read(data);
    656                     if (len > 0) contents.write(data, 0, len);
    657                 } while (len == data.length);
    658                 return contents.toString();
    659             }
    660         } finally {
    661             bis.close();
    662             input.close();
    663         }
    664     }
    665 
    666     /** {@hide} */
    667     @UnsupportedAppUsage
    668     public static void stringToFile(File file, String string) throws IOException {
    669         stringToFile(file.getAbsolutePath(), string);
    670     }
    671 
    672     /**
    673      * Writes the bytes given in {@code content} to the file whose absolute path
    674      * is {@code filename}.
    675      *
    676      * @hide
    677      */
    678     public static void bytesToFile(String filename, byte[] content) throws IOException {
    679         if (filename.startsWith("/proc/")) {
    680             final int oldMask = StrictMode.allowThreadDiskWritesMask();
    681             try (FileOutputStream fos = new FileOutputStream(filename)) {
    682                 fos.write(content);
    683             } finally {
    684                 StrictMode.setThreadPolicyMask(oldMask);
    685             }
    686         } else {
    687             try (FileOutputStream fos = new FileOutputStream(filename)) {
    688                 fos.write(content);
    689             }
    690         }
    691     }
    692 
    693     /**
    694      * Writes string to file. Basically same as "echo -n $string > $filename"
    695      *
    696      * @param filename
    697      * @param string
    698      * @throws IOException
    699      * @hide
    700      */
    701     @UnsupportedAppUsage
    702     public static void stringToFile(String filename, String string) throws IOException {
    703         bytesToFile(filename, string.getBytes(StandardCharsets.UTF_8));
    704     }
    705 
    706     /**
    707      * Computes the checksum of a file using the CRC32 checksum routine. The
    708      * value of the checksum is returned.
    709      *
    710      * @param file the file to checksum, must not be null
    711      * @return the checksum value or an exception is thrown.
    712      * @deprecated this is a weak hashing algorithm, and should not be used due
    713      *             to its potential for collision.
    714      * @hide
    715      */
    716     @UnsupportedAppUsage
    717     @Deprecated
    718     public static long checksumCrc32(File file) throws FileNotFoundException, IOException {
    719         CRC32 checkSummer = new CRC32();
    720         CheckedInputStream cis = null;
    721 
    722         try {
    723             cis = new CheckedInputStream( new FileInputStream(file), checkSummer);
    724             byte[] buf = new byte[128];
    725             while(cis.read(buf) >= 0) {
    726                 // Just read for checksum to get calculated.
    727             }
    728             return checkSummer.getValue();
    729         } finally {
    730             if (cis != null) {
    731                 try {
    732                     cis.close();
    733                 } catch (IOException e) {
    734                 }
    735             }
    736         }
    737     }
    738 
    739     /**
    740      * Compute the digest of the given file using the requested algorithm.
    741      *
    742      * @param algorithm Any valid algorithm accepted by
    743      *            {@link MessageDigest#getInstance(String)}.
    744      * @hide
    745      */
    746     public static byte[] digest(@NonNull File file, @NonNull String algorithm)
    747             throws IOException, NoSuchAlgorithmException {
    748         try (FileInputStream in = new FileInputStream(file)) {
    749             return digest(in, algorithm);
    750         }
    751     }
    752 
    753     /**
    754      * Compute the digest of the given file using the requested algorithm.
    755      *
    756      * @param algorithm Any valid algorithm accepted by
    757      *            {@link MessageDigest#getInstance(String)}.
    758      * @hide
    759      */
    760     public static byte[] digest(@NonNull InputStream in, @NonNull String algorithm)
    761             throws IOException, NoSuchAlgorithmException {
    762         // TODO: implement kernel optimizations
    763         return digestInternalUserspace(in, algorithm);
    764     }
    765 
    766     /**
    767      * Compute the digest of the given file using the requested algorithm.
    768      *
    769      * @param algorithm Any valid algorithm accepted by
    770      *            {@link MessageDigest#getInstance(String)}.
    771      * @hide
    772      */
    773     public static byte[] digest(FileDescriptor fd, String algorithm)
    774             throws IOException, NoSuchAlgorithmException {
    775         // TODO: implement kernel optimizations
    776         return digestInternalUserspace(new FileInputStream(fd), algorithm);
    777     }
    778 
    779     private static byte[] digestInternalUserspace(InputStream in, String algorithm)
    780             throws IOException, NoSuchAlgorithmException {
    781         final MessageDigest digest = MessageDigest.getInstance(algorithm);
    782         try (DigestInputStream digestStream = new DigestInputStream(in, digest)) {
    783             final byte[] buffer = new byte[8192];
    784             while (digestStream.read(buffer) != -1) {
    785             }
    786         }
    787         return digest.digest();
    788     }
    789 
    790     /**
    791      * Delete older files in a directory until only those matching the given
    792      * constraints remain.
    793      *
    794      * @param minCount Always keep at least this many files.
    795      * @param minAgeMs Always keep files younger than this age, in milliseconds.
    796      * @return if any files were deleted.
    797      * @hide
    798      */
    799     @UnsupportedAppUsage
    800     public static boolean deleteOlderFiles(File dir, int minCount, long minAgeMs) {
    801         if (minCount < 0 || minAgeMs < 0) {
    802             throw new IllegalArgumentException("Constraints must be positive or 0");
    803         }
    804 
    805         final File[] files = dir.listFiles();
    806         if (files == null) return false;
    807 
    808         // Sort with newest files first
    809         Arrays.sort(files, new Comparator<File>() {
    810             @Override
    811             public int compare(File lhs, File rhs) {
    812                 return Long.compare(rhs.lastModified(), lhs.lastModified());
    813             }
    814         });
    815 
    816         // Keep at least minCount files
    817         boolean deleted = false;
    818         for (int i = minCount; i < files.length; i++) {
    819             final File file = files[i];
    820 
    821             // Keep files newer than minAgeMs
    822             final long age = System.currentTimeMillis() - file.lastModified();
    823             if (age > minAgeMs) {
    824                 if (file.delete()) {
    825                     Log.d(TAG, "Deleted old file " + file);
    826                     deleted = true;
    827                 }
    828             }
    829         }
    830         return deleted;
    831     }
    832 
    833     /**
    834      * Test if a file lives under the given directory, either as a direct child
    835      * or a distant grandchild.
    836      * <p>
    837      * Both files <em>must</em> have been resolved using
    838      * {@link File#getCanonicalFile()} to avoid symlink or path traversal
    839      * attacks.
    840      *
    841      * @hide
    842      */
    843     public static boolean contains(File[] dirs, File file) {
    844         for (File dir : dirs) {
    845             if (contains(dir, file)) {
    846                 return true;
    847             }
    848         }
    849         return false;
    850     }
    851 
    852     /** {@hide} */
    853     public static boolean contains(Collection<File> dirs, File file) {
    854         for (File dir : dirs) {
    855             if (contains(dir, file)) {
    856                 return true;
    857             }
    858         }
    859         return false;
    860     }
    861 
    862     /**
    863      * Test if a file lives under the given directory, either as a direct child
    864      * or a distant grandchild.
    865      * <p>
    866      * Both files <em>must</em> have been resolved using
    867      * {@link File#getCanonicalFile()} to avoid symlink or path traversal
    868      * attacks.
    869      *
    870      * @hide
    871      */
    872     @TestApi
    873     public static boolean contains(File dir, File file) {
    874         if (dir == null || file == null) return false;
    875         return contains(dir.getAbsolutePath(), file.getAbsolutePath());
    876     }
    877 
    878     /**
    879      * Test if a file lives under the given directory, either as a direct child
    880      * or a distant grandchild.
    881      * <p>
    882      * Both files <em>must</em> have been resolved using
    883      * {@link File#getCanonicalFile()} to avoid symlink or path traversal
    884      * attacks.
    885      *
    886      * @hide
    887      */
    888     public static boolean contains(String dirPath, String filePath) {
    889         if (dirPath.equals(filePath)) {
    890             return true;
    891         }
    892         if (!dirPath.endsWith("/")) {
    893             dirPath += "/";
    894         }
    895         return filePath.startsWith(dirPath);
    896     }
    897 
    898     /** {@hide} */
    899     public static boolean deleteContentsAndDir(File dir) {
    900         if (deleteContents(dir)) {
    901             return dir.delete();
    902         } else {
    903             return false;
    904         }
    905     }
    906 
    907     /** {@hide} */
    908     @UnsupportedAppUsage
    909     public static boolean deleteContents(File dir) {
    910         File[] files = dir.listFiles();
    911         boolean success = true;
    912         if (files != null) {
    913             for (File file : files) {
    914                 if (file.isDirectory()) {
    915                     success &= deleteContents(file);
    916                 }
    917                 if (!file.delete()) {
    918                     Log.w(TAG, "Failed to delete " + file);
    919                     success = false;
    920                 }
    921             }
    922         }
    923         return success;
    924     }
    925 
    926     private static boolean isValidExtFilenameChar(char c) {
    927         switch (c) {
    928             case '\0':
    929             case '/':
    930                 return false;
    931             default:
    932                 return true;
    933         }
    934     }
    935 
    936     /**
    937      * Check if given filename is valid for an ext4 filesystem.
    938      *
    939      * @hide
    940      */
    941     public static boolean isValidExtFilename(String name) {
    942         return (name != null) && name.equals(buildValidExtFilename(name));
    943     }
    944 
    945     /**
    946      * Mutate the given filename to make it valid for an ext4 filesystem,
    947      * replacing any invalid characters with "_".
    948      *
    949      * @hide
    950      */
    951     public static String buildValidExtFilename(String name) {
    952         if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
    953             return "(invalid)";
    954         }
    955         final StringBuilder res = new StringBuilder(name.length());
    956         for (int i = 0; i < name.length(); i++) {
    957             final char c = name.charAt(i);
    958             if (isValidExtFilenameChar(c)) {
    959                 res.append(c);
    960             } else {
    961                 res.append('_');
    962             }
    963         }
    964         trimFilename(res, 255);
    965         return res.toString();
    966     }
    967 
    968     private static boolean isValidFatFilenameChar(char c) {
    969         if ((0x00 <= c && c <= 0x1f)) {
    970             return false;
    971         }
    972         switch (c) {
    973             case '"':
    974             case '*':
    975             case '/':
    976             case ':':
    977             case '<':
    978             case '>':
    979             case '?':
    980             case '\\':
    981             case '|':
    982             case 0x7F:
    983                 return false;
    984             default:
    985                 return true;
    986         }
    987     }
    988 
    989     /**
    990      * Check if given filename is valid for a FAT filesystem.
    991      *
    992      * @hide
    993      */
    994     public static boolean isValidFatFilename(String name) {
    995         return (name != null) && name.equals(buildValidFatFilename(name));
    996     }
    997 
    998     /**
    999      * Mutate the given filename to make it valid for a FAT filesystem,
   1000      * replacing any invalid characters with "_".
   1001      *
   1002      * @hide
   1003      */
   1004     public static String buildValidFatFilename(String name) {
   1005         if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
   1006             return "(invalid)";
   1007         }
   1008         final StringBuilder res = new StringBuilder(name.length());
   1009         for (int i = 0; i < name.length(); i++) {
   1010             final char c = name.charAt(i);
   1011             if (isValidFatFilenameChar(c)) {
   1012                 res.append(c);
   1013             } else {
   1014                 res.append('_');
   1015             }
   1016         }
   1017         // Even though vfat allows 255 UCS-2 chars, we might eventually write to
   1018         // ext4 through a FUSE layer, so use that limit.
   1019         trimFilename(res, 255);
   1020         return res.toString();
   1021     }
   1022 
   1023     /** {@hide} */
   1024     @VisibleForTesting
   1025     public static String trimFilename(String str, int maxBytes) {
   1026         final StringBuilder res = new StringBuilder(str);
   1027         trimFilename(res, maxBytes);
   1028         return res.toString();
   1029     }
   1030 
   1031     /** {@hide} */
   1032     private static void trimFilename(StringBuilder res, int maxBytes) {
   1033         byte[] raw = res.toString().getBytes(StandardCharsets.UTF_8);
   1034         if (raw.length > maxBytes) {
   1035             maxBytes -= 3;
   1036             while (raw.length > maxBytes) {
   1037                 res.deleteCharAt(res.length() / 2);
   1038                 raw = res.toString().getBytes(StandardCharsets.UTF_8);
   1039             }
   1040             res.insert(res.length() / 2, "...");
   1041         }
   1042     }
   1043 
   1044     /** {@hide} */
   1045     public static String rewriteAfterRename(File beforeDir, File afterDir, String path) {
   1046         if (path == null) return null;
   1047         final File result = rewriteAfterRename(beforeDir, afterDir, new File(path));
   1048         return (result != null) ? result.getAbsolutePath() : null;
   1049     }
   1050 
   1051     /** {@hide} */
   1052     public static String[] rewriteAfterRename(File beforeDir, File afterDir, String[] paths) {
   1053         if (paths == null) return null;
   1054         final String[] result = new String[paths.length];
   1055         for (int i = 0; i < paths.length; i++) {
   1056             result[i] = rewriteAfterRename(beforeDir, afterDir, paths[i]);
   1057         }
   1058         return result;
   1059     }
   1060 
   1061     /**
   1062      * Given a path under the "before" directory, rewrite it to live under the
   1063      * "after" directory. For example, {@code /before/foo/bar.txt} would become
   1064      * {@code /after/foo/bar.txt}.
   1065      *
   1066      * @hide
   1067      */
   1068     public static File rewriteAfterRename(File beforeDir, File afterDir, File file) {
   1069         if (file == null || beforeDir == null || afterDir == null) return null;
   1070         if (contains(beforeDir, file)) {
   1071             final String splice = file.getAbsolutePath().substring(
   1072                     beforeDir.getAbsolutePath().length());
   1073             return new File(afterDir, splice);
   1074         }
   1075         return null;
   1076     }
   1077 
   1078     /** {@hide} */
   1079     private static File buildUniqueFileWithExtension(File parent, String name, String ext)
   1080             throws FileNotFoundException {
   1081         File file = buildFile(parent, name, ext);
   1082 
   1083         // If conflicting file, try adding counter suffix
   1084         int n = 0;
   1085         while (file.exists()) {
   1086             if (n++ >= 32) {
   1087                 throw new FileNotFoundException("Failed to create unique file");
   1088             }
   1089             file = buildFile(parent, name + " (" + n + ")", ext);
   1090         }
   1091 
   1092         return file;
   1093     }
   1094 
   1095     /**
   1096      * Generates a unique file name under the given parent directory. If the display name doesn't
   1097      * have an extension that matches the requested MIME type, the default extension for that MIME
   1098      * type is appended. If a file already exists, the name is appended with a numerical value to
   1099      * make it unique.
   1100      *
   1101      * For example, the display name 'example' with 'text/plain' MIME might produce
   1102      * 'example.txt' or 'example (1).txt', etc.
   1103      *
   1104      * @throws FileNotFoundException
   1105      * @hide
   1106      */
   1107     public static File buildUniqueFile(File parent, String mimeType, String displayName)
   1108             throws FileNotFoundException {
   1109         final String[] parts = splitFileName(mimeType, displayName);
   1110         return buildUniqueFileWithExtension(parent, parts[0], parts[1]);
   1111     }
   1112 
   1113     /** {@hide} */
   1114     public static File buildNonUniqueFile(File parent, String mimeType, String displayName) {
   1115         final String[] parts = splitFileName(mimeType, displayName);
   1116         return buildFile(parent, parts[0], parts[1]);
   1117     }
   1118 
   1119     /**
   1120      * Generates a unique file name under the given parent directory, keeping
   1121      * any extension intact.
   1122      *
   1123      * @hide
   1124      */
   1125     public static File buildUniqueFile(File parent, String displayName)
   1126             throws FileNotFoundException {
   1127         final String name;
   1128         final String ext;
   1129 
   1130         // Extract requested extension from display name
   1131         final int lastDot = displayName.lastIndexOf('.');
   1132         if (lastDot >= 0) {
   1133             name = displayName.substring(0, lastDot);
   1134             ext = displayName.substring(lastDot + 1);
   1135         } else {
   1136             name = displayName;
   1137             ext = null;
   1138         }
   1139 
   1140         return buildUniqueFileWithExtension(parent, name, ext);
   1141     }
   1142 
   1143     /**
   1144      * Splits file name into base name and extension.
   1145      * If the display name doesn't have an extension that matches the requested MIME type, the
   1146      * extension is regarded as a part of filename and default extension for that MIME type is
   1147      * appended.
   1148      *
   1149      * @hide
   1150      */
   1151     public static String[] splitFileName(String mimeType, String displayName) {
   1152         String name;
   1153         String ext;
   1154 
   1155         if (Document.MIME_TYPE_DIR.equals(mimeType)) {
   1156             name = displayName;
   1157             ext = null;
   1158         } else {
   1159             String mimeTypeFromExt;
   1160 
   1161             // Extract requested extension from display name
   1162             final int lastDot = displayName.lastIndexOf('.');
   1163             if (lastDot >= 0) {
   1164                 name = displayName.substring(0, lastDot);
   1165                 ext = displayName.substring(lastDot + 1);
   1166                 mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
   1167                         ext.toLowerCase());
   1168             } else {
   1169                 name = displayName;
   1170                 ext = null;
   1171                 mimeTypeFromExt = null;
   1172             }
   1173 
   1174             if (mimeTypeFromExt == null) {
   1175                 mimeTypeFromExt = ContentResolver.MIME_TYPE_DEFAULT;
   1176             }
   1177 
   1178             final String extFromMimeType;
   1179             if (ContentResolver.MIME_TYPE_DEFAULT.equals(mimeType)) {
   1180                 extFromMimeType = null;
   1181             } else {
   1182                 extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
   1183             }
   1184 
   1185             if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
   1186                 // Extension maps back to requested MIME type; allow it
   1187             } else {
   1188                 // No match; insist that create file matches requested MIME
   1189                 name = displayName;
   1190                 ext = extFromMimeType;
   1191             }
   1192         }
   1193 
   1194         if (ext == null) {
   1195             ext = "";
   1196         }
   1197 
   1198         return new String[] { name, ext };
   1199     }
   1200 
   1201     /** {@hide} */
   1202     private static File buildFile(File parent, String name, String ext) {
   1203         if (TextUtils.isEmpty(ext)) {
   1204             return new File(parent, name);
   1205         } else {
   1206             return new File(parent, name + "." + ext);
   1207         }
   1208     }
   1209 
   1210     /** {@hide} */
   1211     public static @NonNull String[] listOrEmpty(@Nullable File dir) {
   1212         return (dir != null) ? ArrayUtils.defeatNullable(dir.list())
   1213                 : EmptyArray.STRING;
   1214     }
   1215 
   1216     /** {@hide} */
   1217     public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) {
   1218         return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles())
   1219                 : ArrayUtils.EMPTY_FILE;
   1220     }
   1221 
   1222     /** {@hide} */
   1223     public static @NonNull File[] listFilesOrEmpty(@Nullable File dir, FilenameFilter filter) {
   1224         return (dir != null) ? ArrayUtils.defeatNullable(dir.listFiles(filter))
   1225                 : ArrayUtils.EMPTY_FILE;
   1226     }
   1227 
   1228     /** {@hide} */
   1229     public static @Nullable File newFileOrNull(@Nullable String path) {
   1230         return (path != null) ? new File(path) : null;
   1231     }
   1232 
   1233     /**
   1234      * Creates a directory with name {@code name} under an existing directory {@code baseDir}.
   1235      * Returns a {@code File} object representing the directory on success, {@code null} on
   1236      * failure.
   1237      *
   1238      * @hide
   1239      */
   1240     public static @Nullable File createDir(File baseDir, String name) {
   1241         final File dir = new File(baseDir, name);
   1242 
   1243         return createDir(dir) ? dir : null;
   1244     }
   1245 
   1246     /** @hide */
   1247     public static boolean createDir(File dir) {
   1248         if (dir.exists()) {
   1249             return dir.isDirectory();
   1250         }
   1251 
   1252         return dir.mkdir();
   1253     }
   1254 
   1255     /**
   1256      * Round the given size of a storage device to a nice round power-of-two
   1257      * value, such as 256MB or 32GB. This avoids showing weird values like
   1258      * "29.5GB" in UI.
   1259      *
   1260      * @hide
   1261      */
   1262     public static long roundStorageSize(long size) {
   1263         long val = 1;
   1264         long pow = 1;
   1265         while ((val * pow) < size) {
   1266             val <<= 1;
   1267             if (val > 512) {
   1268                 val = 1;
   1269                 pow *= 1000;
   1270             }
   1271         }
   1272         return val * pow;
   1273     }
   1274 
   1275     /**
   1276      * Closes the given object quietly, ignoring any checked exceptions. Does
   1277      * nothing if the given object is {@code null}.
   1278      */
   1279     public static void closeQuietly(@Nullable AutoCloseable closeable) {
   1280         IoUtils.closeQuietly(closeable);
   1281     }
   1282 
   1283     /**
   1284      * Closes the given object quietly, ignoring any checked exceptions. Does
   1285      * nothing if the given object is {@code null}.
   1286      */
   1287     public static void closeQuietly(@Nullable FileDescriptor fd) {
   1288         IoUtils.closeQuietly(fd);
   1289     }
   1290 
   1291     /** {@hide} */
   1292     public static int translateModeStringToPosix(String mode) {
   1293         // Sanity check for invalid chars
   1294         for (int i = 0; i < mode.length(); i++) {
   1295             switch (mode.charAt(i)) {
   1296                 case 'r':
   1297                 case 'w':
   1298                 case 't':
   1299                 case 'a':
   1300                     break;
   1301                 default:
   1302                     throw new IllegalArgumentException("Bad mode: " + mode);
   1303             }
   1304         }
   1305 
   1306         int res = 0;
   1307         if (mode.startsWith("rw")) {
   1308             res = O_RDWR | O_CREAT;
   1309         } else if (mode.startsWith("w")) {
   1310             res = O_WRONLY | O_CREAT;
   1311         } else if (mode.startsWith("r")) {
   1312             res = O_RDONLY;
   1313         } else {
   1314             throw new IllegalArgumentException("Bad mode: " + mode);
   1315         }
   1316         if (mode.indexOf('t') != -1) {
   1317             res |= O_TRUNC;
   1318         }
   1319         if (mode.indexOf('a') != -1) {
   1320             res |= O_APPEND;
   1321         }
   1322         return res;
   1323     }
   1324 
   1325     /** {@hide} */
   1326     public static String translateModePosixToString(int mode) {
   1327         String res = "";
   1328         if ((mode & O_ACCMODE) == O_RDWR) {
   1329             res = "rw";
   1330         } else if ((mode & O_ACCMODE) == O_WRONLY) {
   1331             res = "w";
   1332         } else if ((mode & O_ACCMODE) == O_RDONLY) {
   1333             res = "r";
   1334         } else {
   1335             throw new IllegalArgumentException("Bad mode: " + mode);
   1336         }
   1337         if ((mode & O_TRUNC) == O_TRUNC) {
   1338             res += "t";
   1339         }
   1340         if ((mode & O_APPEND) == O_APPEND) {
   1341             res += "a";
   1342         }
   1343         return res;
   1344     }
   1345 
   1346     /** {@hide} */
   1347     public static int translateModePosixToPfd(int mode) {
   1348         int res = 0;
   1349         if ((mode & O_ACCMODE) == O_RDWR) {
   1350             res = MODE_READ_WRITE;
   1351         } else if ((mode & O_ACCMODE) == O_WRONLY) {
   1352             res = MODE_WRITE_ONLY;
   1353         } else if ((mode & O_ACCMODE) == O_RDONLY) {
   1354             res = MODE_READ_ONLY;
   1355         } else {
   1356             throw new IllegalArgumentException("Bad mode: " + mode);
   1357         }
   1358         if ((mode & O_CREAT) == O_CREAT) {
   1359             res |= MODE_CREATE;
   1360         }
   1361         if ((mode & O_TRUNC) == O_TRUNC) {
   1362             res |= MODE_TRUNCATE;
   1363         }
   1364         if ((mode & O_APPEND) == O_APPEND) {
   1365             res |= MODE_APPEND;
   1366         }
   1367         return res;
   1368     }
   1369 
   1370     /** {@hide} */
   1371     public static int translateModePfdToPosix(int mode) {
   1372         int res = 0;
   1373         if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
   1374             res = O_RDWR;
   1375         } else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) {
   1376             res = O_WRONLY;
   1377         } else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) {
   1378             res = O_RDONLY;
   1379         } else {
   1380             throw new IllegalArgumentException("Bad mode: " + mode);
   1381         }
   1382         if ((mode & MODE_CREATE) == MODE_CREATE) {
   1383             res |= O_CREAT;
   1384         }
   1385         if ((mode & MODE_TRUNCATE) == MODE_TRUNCATE) {
   1386             res |= O_TRUNC;
   1387         }
   1388         if ((mode & MODE_APPEND) == MODE_APPEND) {
   1389             res |= O_APPEND;
   1390         }
   1391         return res;
   1392     }
   1393 
   1394     /** {@hide} */
   1395     public static int translateModeAccessToPosix(int mode) {
   1396         if (mode == F_OK) {
   1397             // There's not an exact mapping, so we attempt a read-only open to
   1398             // determine if a file exists
   1399             return O_RDONLY;
   1400         } else if ((mode & (R_OK | W_OK)) == (R_OK | W_OK)) {
   1401             return O_RDWR;
   1402         } else if ((mode & R_OK) == R_OK) {
   1403             return O_RDONLY;
   1404         } else if ((mode & W_OK) == W_OK) {
   1405             return O_WRONLY;
   1406         } else {
   1407             throw new IllegalArgumentException("Bad mode: " + mode);
   1408         }
   1409     }
   1410 
   1411     /** {@hide} */
   1412     @VisibleForTesting
   1413     public static class MemoryPipe extends Thread implements AutoCloseable {
   1414         private final FileDescriptor[] pipe;
   1415         private final byte[] data;
   1416         private final boolean sink;
   1417 
   1418         private MemoryPipe(byte[] data, boolean sink) throws IOException {
   1419             try {
   1420                 this.pipe = Os.pipe();
   1421             } catch (ErrnoException e) {
   1422                 throw e.rethrowAsIOException();
   1423             }
   1424             this.data = data;
   1425             this.sink = sink;
   1426         }
   1427 
   1428         private MemoryPipe startInternal() {
   1429             super.start();
   1430             return this;
   1431         }
   1432 
   1433         public static MemoryPipe createSource(byte[] data) throws IOException {
   1434             return new MemoryPipe(data, false).startInternal();
   1435         }
   1436 
   1437         public static MemoryPipe createSink(byte[] data) throws IOException {
   1438             return new MemoryPipe(data, true).startInternal();
   1439         }
   1440 
   1441         public FileDescriptor getFD() {
   1442             return sink ? pipe[1] : pipe[0];
   1443         }
   1444 
   1445         public FileDescriptor getInternalFD() {
   1446             return sink ? pipe[0] : pipe[1];
   1447         }
   1448 
   1449         @Override
   1450         public void run() {
   1451             final FileDescriptor fd = getInternalFD();
   1452             try {
   1453                 int i = 0;
   1454                 while (i < data.length) {
   1455                     if (sink) {
   1456                         i += Os.read(fd, data, i, data.length - i);
   1457                     } else {
   1458                         i += Os.write(fd, data, i, data.length - i);
   1459                     }
   1460                 }
   1461             } catch (IOException | ErrnoException e) {
   1462                 // Ignored
   1463             } finally {
   1464                 if (sink) {
   1465                     SystemClock.sleep(TimeUnit.SECONDS.toMillis(1));
   1466                 }
   1467                 IoUtils.closeQuietly(fd);
   1468             }
   1469         }
   1470 
   1471         @Override
   1472         public void close() throws Exception {
   1473             IoUtils.closeQuietly(getFD());
   1474         }
   1475     }
   1476 }
   1477