19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.os; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1985ced632680642fce680d141ddd10299ff849233Jeff Sharkeyimport android.annotation.NonNull; 2015447798a38d2b5acb1998731340255f4203f294Jeff Sharkeyimport android.annotation.Nullable; 2162539a220c6810f66b63060326bd1668f7d6b029Ben Kwaimport android.provider.DocumentsContract.Document; 2234385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.ErrnoException; 2334385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.Os; 2435871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkeyimport android.system.StructStat; 250cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkeyimport android.text.TextUtils; 26d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkeyimport android.util.Log; 27184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkeyimport android.util.Slog; 2862539a220c6810f66b63060326bd1668f7d6b029Ben Kwaimport android.webkit.MimeTypeMap; 29184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 304f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkeyimport com.android.internal.annotations.VisibleForTesting; 314f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey 32c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkeyimport libcore.util.EmptyArray; 33c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey 3490619816d99154d504a14774c6f2d5f4254ff780Guang Zhuimport java.io.BufferedInputStream; 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.ByteArrayOutputStream; 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File; 37184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkeyimport java.io.FileDescriptor; 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileInputStream; 396d25a990afffd5eb385aba3043d5dfad36f1539aWink Savilleimport java.io.FileNotFoundException; 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileOutputStream; 4135871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkeyimport java.io.FilenameFilter; 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException; 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.InputStream; 444f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkeyimport java.nio.charset.StandardCharsets; 45d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkeyimport java.util.Arrays; 46d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkeyimport java.util.Comparator; 4762539a220c6810f66b63060326bd1668f7d6b029Ben Kwaimport java.util.Objects; 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Pattern; 496d25a990afffd5eb385aba3043d5dfad36f1539aWink Savilleimport java.util.zip.CRC32; 506d25a990afffd5eb385aba3043d5dfad36f1539aWink Savilleimport java.util.zip.CheckedInputStream; 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Tools for managing files. Not for public consumption. 549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @hide 559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 566d25a990afffd5eb385aba3043d5dfad36f1539aWink Savillepublic class FileUtils { 57d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey private static final String TAG = "FileUtils"; 58d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRWXU = 00700; 609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRUSR = 00400; 619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IWUSR = 00200; 629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IXUSR = 00100; 639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRWXG = 00070; 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRGRP = 00040; 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IWGRP = 00020; 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IXGRP = 00010; 689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IRWXO = 00007; 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IROTH = 00004; 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IWOTH = 00002; 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static final int S_IXOTH = 00001; 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 740693fd85f69db553204168eeb342e6bf0b5fe7b5Andreas Gampe /** Regular expression for safe filenames: no spaces or metacharacters. 750693fd85f69db553204168eeb342e6bf0b5fe7b5Andreas Gampe * 760693fd85f69db553204168eeb342e6bf0b5fe7b5Andreas Gampe * Use a preload holder so that FileUtils can be compile-time initialized. 770693fd85f69db553204168eeb342e6bf0b5fe7b5Andreas Gampe */ 780693fd85f69db553204168eeb342e6bf0b5fe7b5Andreas Gampe private static class NoImagePreloadHolder { 790693fd85f69db553204168eeb342e6bf0b5fe7b5Andreas Gampe public static final Pattern SAFE_FILENAME_PATTERN = Pattern.compile("[\\w%+,./=_-]+"); 800693fd85f69db553204168eeb342e6bf0b5fe7b5Andreas Gampe } 819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 8285ced632680642fce680d141ddd10299ff849233Jeff Sharkey private static final File[] EMPTY = new File[0]; 8385ced632680642fce680d141ddd10299ff849233Jeff Sharkey 84184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey /** 85184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * Set owner and mode of of given {@link File}. 86184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * 87184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param mode to apply through {@code chmod} 88184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param uid to apply through {@code chown}, or -1 to leave unchanged 89184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param gid to apply through {@code chown}, or -1 to leave unchanged 90184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @return 0 on success, otherwise errno. 91184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey */ 92184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey public static int setPermissions(File path, int mode, int uid, int gid) { 93184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return setPermissions(path.getAbsolutePath(), mode, uid, gid); 94184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 95184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 96184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey /** 97184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * Set owner and mode of of given path. 98184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * 99184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param mode to apply through {@code chmod} 100184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param uid to apply through {@code chown}, or -1 to leave unchanged 101184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param gid to apply through {@code chown}, or -1 to leave unchanged 102184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @return 0 on success, otherwise errno. 103184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey */ 104184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey public static int setPermissions(String path, int mode, int uid, int gid) { 105184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 10634385d352da19805ae948215e2edbeedd16b7941Elliott Hughes Os.chmod(path, mode); 107184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 108184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey Slog.w(TAG, "Failed to chmod(" + path + "): " + e); 109184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return e.errno; 110184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 111184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 112184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey if (uid >= 0 || gid >= 0) { 113184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 11434385d352da19805ae948215e2edbeedd16b7941Elliott Hughes Os.chown(path, uid, gid); 115184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 116184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey Slog.w(TAG, "Failed to chown(" + path + "): " + e); 117184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return e.errno; 118184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 119184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 120184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 121184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return 0; 122184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 124184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey /** 125184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * Set owner and mode of of given {@link FileDescriptor}. 126184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * 127184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param mode to apply through {@code chmod} 128184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param uid to apply through {@code chown}, or -1 to leave unchanged 129184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @param gid to apply through {@code chown}, or -1 to leave unchanged 130184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * @return 0 on success, otherwise errno. 131184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey */ 132184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey public static int setPermissions(FileDescriptor fd, int mode, int uid, int gid) { 133184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 13434385d352da19805ae948215e2edbeedd16b7941Elliott Hughes Os.fchmod(fd, mode); 135184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 136184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey Slog.w(TAG, "Failed to fchmod(): " + e); 137184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return e.errno; 138184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 139184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 140184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey if (uid >= 0 || gid >= 0) { 141184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 14234385d352da19805ae948215e2edbeedd16b7941Elliott Hughes Os.fchown(fd, uid, gid); 143184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 144184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey Slog.w(TAG, "Failed to fchown(): " + e); 145184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return e.errno; 146184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 147184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 148184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 149184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return 0; 150184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 151184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey 15235871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey public static void copyPermissions(File from, File to) throws IOException { 15335871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey try { 15435871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey final StructStat stat = Os.stat(from.getAbsolutePath()); 15535871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey Os.chmod(to.getAbsolutePath(), stat.st_mode); 15635871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey Os.chown(to.getAbsolutePath(), stat.st_uid, stat.st_gid); 15735871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } catch (ErrnoException e) { 15835871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey throw e.rethrowAsIOException(); 15935871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } 16035871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } 16135871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey 162184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey /** 163184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey * Return owning UID of given path, otherwise -1. 164184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey */ 165184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey public static int getUid(String path) { 166184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey try { 16734385d352da19805ae948215e2edbeedd16b7941Elliott Hughes return Os.stat(path).st_uid; 168184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } catch (ErrnoException e) { 169184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey return -1; 170184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 171184a0100abc431fc3d6d8dd1b20212b84958cadaJeff Sharkey } 172053f61d6a6e23825e680dc49982e55c5b4299d61Dianne Hackborn 1738bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn /** 1748bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn * Perform an fsync on the given FileOutputStream. The stream at this 1758bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn * point must be flushed but not yet closed. 1768bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn */ 1778bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn public static boolean sync(FileOutputStream stream) { 1788bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn try { 1798bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn if (stream != null) { 1808bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn stream.getFD().sync(); 1818bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } 1828bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn return true; 1838bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } catch (IOException e) { 1848bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } 1858bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn return false; 1868bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn } 1878bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn 18835871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey @Deprecated 18935871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey public static boolean copyFile(File srcFile, File destFile) { 19035871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey try { 19135871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey copyFileOrThrow(srcFile, destFile); 19235871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey return true; 19335871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } catch (IOException e) { 19435871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey return false; 19535871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } 19635871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } 19735871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // copy a file from srcFile to destFile, return true if succeed, return 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // false if fail 20035871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey public static void copyFileOrThrow(File srcFile, File destFile) throws IOException { 20135871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey try (InputStream in = new FileInputStream(srcFile)) { 20235871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey copyToFileOrThrow(in, destFile); 20335871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } 20435871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } 20535871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey 20635871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey @Deprecated 20735871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey public static boolean copyToFile(InputStream inputStream, File destFile) { 2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 20935871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey copyToFileOrThrow(inputStream, destFile); 21035871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey return true; 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } catch (IOException e) { 21235871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey return false; 2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 21590619816d99154d504a14774c6f2d5f4254ff780Guang Zhu 2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copy data from a source stream to destFile. 2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Return true if succeed, return false if failed. 2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 22035871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey public static void copyToFileOrThrow(InputStream inputStream, File destFile) 22135871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey throws IOException { 22235871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey if (destFile.exists()) { 22335871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey destFile.delete(); 22435871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } 22535871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey FileOutputStream out = new FileOutputStream(destFile); 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 22735871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey byte[] buffer = new byte[4096]; 22835871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey int bytesRead; 22935871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey while ((bytesRead = inputStream.read(buffer)) >= 0) { 23035871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey out.write(buffer, 0, bytesRead); 2311afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn } 23235871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } finally { 23335871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey out.flush(); 2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 23535871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey out.getFD().sync(); 23635871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } catch (IOException e) { 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 23835871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey out.close(); 2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Check if a filename is "safe" (no metacharacters or spaces). 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param file The file to check 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static boolean isFilenameSafe(File file) { 2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Note, we check whether it matches what's known to be safe, 2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // rather than what's known to be unsafe. Non-ASCII, control 2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // characters, etc. are all unsafe by default. 2500693fd85f69db553204168eeb342e6bf0b5fe7b5Andreas Gampe return NoImagePreloadHolder.SAFE_FILENAME_PATTERN.matcher(file.getPath()).matches(); 2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project /** 2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Read a text file into a String, optionally limiting the length. 2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param file to read (will not seek, so things like /proc files are OK) 2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param max length (positive for head, negative of tail, 0 for no limit) 2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @param ellipsis to add of the file was truncated (can be null) 2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @return the contents of the file, possibly truncated 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * @throws IOException if something goes wrong reading the file 2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public static String readTextFile(File file, int max, String ellipsis) throws IOException { 2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project InputStream input = new FileInputStream(file); 26390619816d99154d504a14774c6f2d5f4254ff780Guang Zhu // wrapping a BufferedInputStream around it because when reading /proc with unbuffered 26490619816d99154d504a14774c6f2d5f4254ff780Guang Zhu // input stream, bytes read not equal to buffer size is not necessarily the correct 26590619816d99154d504a14774c6f2d5f4254ff780Guang Zhu // indication for EOF; but it is true for BufferedInputStream due to its implementation. 26690619816d99154d504a14774c6f2d5f4254ff780Guang Zhu BufferedInputStream bis = new BufferedInputStream(input); 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project try { 26842471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor long size = file.length(); 26942471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor if (max > 0 || (size > 0 && max == 0)) { // "head" mode: read the first N bytes 27042471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor if (size > 0 && (max == 0 || size < max)) max = (int) size; 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] data = new byte[max + 1]; 27290619816d99154d504a14774c6f2d5f4254ff780Guang Zhu int length = bis.read(data); 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (length <= 0) return ""; 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (length <= max) return new String(data, 0, length); 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ellipsis == null) return new String(data, 0, max); 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return new String(data, 0, max) + ellipsis; 27742471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor } else if (max < 0) { // "tail" mode: keep the last N 2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len; 2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean rolled = false; 280d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey byte[] last = null; 281d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey byte[] data = null; 2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project do { 2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (last != null) rolled = true; 2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] tmp = last; last = data; data = tmp; 2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (data == null) data = new byte[-max]; 28690619816d99154d504a14774c6f2d5f4254ff780Guang Zhu len = bis.read(data); 2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } while (len == data.length); 2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (last == null && len <= 0) return ""; 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (last == null) return new String(data, 0, len); 2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (len > 0) { 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project rolled = true; 2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project System.arraycopy(last, len, last, 0, last.length - len); 2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project System.arraycopy(data, 0, last, last.length - len, len); 2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (ellipsis == null || !rolled) return new String(last); 2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ellipsis + new String(last); 29842471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor } else { // "cat" mode: size unknown, read it all in streaming fashion 2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ByteArrayOutputStream contents = new ByteArrayOutputStream(); 3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int len; 3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project byte[] data = new byte[1024]; 3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project do { 30390619816d99154d504a14774c6f2d5f4254ff780Guang Zhu len = bis.read(data); 3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (len > 0) contents.write(data, 0, len); 3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } while (len == data.length); 3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return contents.toString(); 3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } finally { 30990619816d99154d504a14774c6f2d5f4254ff780Guang Zhu bis.close(); 3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project input.close(); 3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 313da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood 3142271ba3627d18b65ed5ea63218cee7f9562acd31Jeff Sharkey public static void stringToFile(File file, String string) throws IOException { 3152271ba3627d18b65ed5ea63218cee7f9562acd31Jeff Sharkey stringToFile(file.getAbsolutePath(), string); 3162271ba3627d18b65ed5ea63218cee7f9562acd31Jeff Sharkey } 3172271ba3627d18b65ed5ea63218cee7f9562acd31Jeff Sharkey 3186d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath /* 3196d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath * Writes the bytes given in {@code content} to the file whose absolute path 3206d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath * is {@code filename}. 3216d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath */ 3226d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath public static void bytesToFile(String filename, byte[] content) throws IOException { 3236d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath try (FileOutputStream fos = new FileOutputStream(filename)) { 3246d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath fos.write(content); 3256d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath } 3266d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath } 3276d051fc68d1b1f2c09eb2e58c26821e1e13935dcNarayan Kamath 3282271ba3627d18b65ed5ea63218cee7f9562acd31Jeff Sharkey /** 329da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * Writes string to file. Basically same as "echo -n $string > $filename" 330da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * 331da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * @param filename 332da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * @param string 333da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood * @throws IOException 334da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood */ 335da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood public static void stringToFile(String filename, String string) throws IOException { 336032c08ad6bf21c26347b4acca4c5ca6e1557249eJeff Sharkey bytesToFile(filename, string.getBytes(StandardCharsets.UTF_8)); 337da8bb74b9d9ffcb095815db800d0816c411f1fbaMike Lockwood } 3381b9a6a6e58fd73b5d1b6a434d17f0a69806858ecWink Saville 3396d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville /** 3406d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * Computes the checksum of a file using the CRC32 checksum routine. 3416d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * The value of the checksum is returned. 3426d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * 3436d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * @param file the file to checksum, must not be null 3446d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville * @return the checksum value or an exception is thrown. 3456d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville */ 3466d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville public static long checksumCrc32(File file) throws FileNotFoundException, IOException { 3476d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville CRC32 checkSummer = new CRC32(); 3486d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville CheckedInputStream cis = null; 3496d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville 3506d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville try { 3516d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville cis = new CheckedInputStream( new FileInputStream(file), checkSummer); 3526d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville byte[] buf = new byte[128]; 3536d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville while(cis.read(buf) >= 0) { 3546d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville // Just read for checksum to get calculated. 3556d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 3566d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville return checkSummer.getValue(); 3576d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } finally { 3586d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville if (cis != null) { 3596d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville try { 3606d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville cis.close(); 3616d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } catch (IOException e) { 3626d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 3636d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 3646d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 3656d25a990afffd5eb385aba3043d5dfad36f1539aWink Saville } 366d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 367d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey /** 368d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * Delete older files in a directory until only those matching the given 369d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * constraints remain. 370d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * 371d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * @param minCount Always keep at least this many files. 372d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey * @param minAge Always keep files younger than this age. 373ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey * @return if any files were deleted. 374d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey */ 375ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey public static boolean deleteOlderFiles(File dir, int minCount, long minAge) { 376d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey if (minCount < 0 || minAge < 0) { 377d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey throw new IllegalArgumentException("Constraints must be positive or 0"); 378d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 379d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 380d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey final File[] files = dir.listFiles(); 381ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey if (files == null) return false; 382d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 383d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey // Sort with newest files first 384d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey Arrays.sort(files, new Comparator<File>() { 385d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey @Override 386d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey public int compare(File lhs, File rhs) { 387660e6def20d1b7b9b6edb8e70ec04d17b069af5bIan Rogers return Long.compare(rhs.lastModified(), lhs.lastModified()); 388d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 389d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey }); 390d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 391d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey // Keep at least minCount files 392ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey boolean deleted = false; 393d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey for (int i = minCount; i < files.length; i++) { 394d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey final File file = files[i]; 395d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey 396d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey // Keep files newer than minAge 397d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey final long age = System.currentTimeMillis() - file.lastModified(); 398d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey if (age > minAge) { 399ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey if (file.delete()) { 400ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey Log.d(TAG, "Deleted old file " + file); 401ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey deleted = true; 402ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey } 403d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 404d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 405ebf8ad5d91b22eb4359c75711a5b70ddcce0723dJeff Sharkey return deleted; 406d9526907d1a51ef0b35bfbbeee43fa209d8b5bbfJeff Sharkey } 4074ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey 4084ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey /** 4094ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * Test if a file lives under the given directory, either as a direct child 4104ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * or a distant grandchild. 4114ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * <p> 4124ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * Both files <em>must</em> have been resolved using 4134ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * {@link File#getCanonicalFile()} to avoid symlink or path traversal 4144ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey * attacks. 4154ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey */ 4164887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey public static boolean contains(File[] dirs, File file) { 4174887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey for (File dir : dirs) { 4184887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey if (contains(dir, file)) { 4194887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey return true; 4204887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey } 4214887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey } 4224887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey return false; 4234887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey } 4244887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey 4254887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey /** 4264887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey * Test if a file lives under the given directory, either as a direct child 4274887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey * or a distant grandchild. 4284887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey * <p> 4294887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey * Both files <em>must</em> have been resolved using 4304887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey * {@link File#getCanonicalFile()} to avoid symlink or path traversal 4314887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey * attacks. 4324887789e44cdb16b042a35e8ec03983213e88ac6Jeff Sharkey */ 4334ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey public static boolean contains(File dir, File file) { 43450a05454795c93ac483f5cb6819e74cb17be1b5bJeff Sharkey if (dir == null || file == null) return false; 435d5d5e926eb8ff2541a845d6a5657cee5e5c6e7b6Jeff Sharkey return contains(dir.getAbsolutePath(), file.getAbsolutePath()); 436d5d5e926eb8ff2541a845d6a5657cee5e5c6e7b6Jeff Sharkey } 437d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey 438d5d5e926eb8ff2541a845d6a5657cee5e5c6e7b6Jeff Sharkey public static boolean contains(String dirPath, String filePath) { 4394ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey if (dirPath.equals(filePath)) { 4404ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey return true; 4414ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey } 4424ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey if (!dirPath.endsWith("/")) { 4434ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey dirPath += "/"; 4444ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey } 4454ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey return filePath.startsWith(dirPath); 4464ca728c064aeab644f6d044e0285eaa056818b8aJeff Sharkey } 4473a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey 448fcf1e55821b694df3b8434f40aa3b6d3c3e7ea50Jeff Sharkey public static boolean deleteContentsAndDir(File dir) { 449fcf1e55821b694df3b8434f40aa3b6d3c3e7ea50Jeff Sharkey if (deleteContents(dir)) { 450fcf1e55821b694df3b8434f40aa3b6d3c3e7ea50Jeff Sharkey return dir.delete(); 451fcf1e55821b694df3b8434f40aa3b6d3c3e7ea50Jeff Sharkey } else { 452fcf1e55821b694df3b8434f40aa3b6d3c3e7ea50Jeff Sharkey return false; 453fcf1e55821b694df3b8434f40aa3b6d3c3e7ea50Jeff Sharkey } 454fcf1e55821b694df3b8434f40aa3b6d3c3e7ea50Jeff Sharkey } 455fcf1e55821b694df3b8434f40aa3b6d3c3e7ea50Jeff Sharkey 45657dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey public static boolean deleteContents(File dir) { 4573a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey File[] files = dir.listFiles(); 45857dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey boolean success = true; 4593a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey if (files != null) { 4603a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey for (File file : files) { 4613a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey if (file.isDirectory()) { 46257dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey success &= deleteContents(file); 4633a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 46473767b9d607d99b3a027619b5c6b7f1a09b7673dJeff Sharkey if (!file.delete()) { 46573767b9d607d99b3a027619b5c6b7f1a09b7673dJeff Sharkey Log.w(TAG, "Failed to delete " + file); 46673767b9d607d99b3a027619b5c6b7f1a09b7673dJeff Sharkey success = false; 46773767b9d607d99b3a027619b5c6b7f1a09b7673dJeff Sharkey } 4683a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 4693a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 47057dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey return success; 4713a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 4723a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey 4730cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey private static boolean isValidExtFilenameChar(char c) { 4740cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey switch (c) { 4750cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '\0': 4760cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '/': 4770cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return false; 4780cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey default: 4790cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return true; 4800cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4810cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4820cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 4833a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey /** 4840cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * Check if given filename is valid for an ext4 filesystem. 4853a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey */ 4863a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey public static boolean isValidExtFilename(String name) { 4870cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return (name != null) && name.equals(buildValidExtFilename(name)); 4880cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 4890cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 4900cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey /** 4910cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * Mutate the given filename to make it valid for an ext4 filesystem, 4920cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * replacing any invalid characters with "_". 4930cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey */ 4940cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey public static String buildValidExtFilename(String name) { 4953a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) { 4960cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return "(invalid)"; 4973a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 4980cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey final StringBuilder res = new StringBuilder(name.length()); 4993a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey for (int i = 0; i < name.length(); i++) { 5003a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey final char c = name.charAt(i); 5010cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey if (isValidExtFilenameChar(c)) { 5020cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey res.append(c); 5030cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } else { 5040cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey res.append('_'); 5050cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 5060cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 5074f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey trimFilename(res, 255); 5080cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return res.toString(); 5090cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 5100cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 5110cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey private static boolean isValidFatFilenameChar(char c) { 5120cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey if ((0x00 <= c && c <= 0x1f)) { 5130cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return false; 5140cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 5150cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey switch (c) { 5160cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '"': 5170cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '*': 5180cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '/': 5190cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case ':': 5200cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '<': 5210cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '>': 5220cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '?': 5230cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '\\': 5240cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case '|': 5250cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey case 0x7F: 5263a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey return false; 5270cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey default: 5280cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return true; 5290cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 5300cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 5310cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 5320cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey /** 5330cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * Check if given filename is valid for a FAT filesystem. 5340cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey */ 5350cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey public static boolean isValidFatFilename(String name) { 5360cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return (name != null) && name.equals(buildValidFatFilename(name)); 5370cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 5380cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey 5390cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey /** 5400cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * Mutate the given filename to make it valid for a FAT filesystem, 5410cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey * replacing any invalid characters with "_". 5420cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey */ 5430cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey public static String buildValidFatFilename(String name) { 5440cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) { 5450cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return "(invalid)"; 5460cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } 5470cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey final StringBuilder res = new StringBuilder(name.length()); 5480cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey for (int i = 0; i < name.length(); i++) { 5490cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey final char c = name.charAt(i); 5500cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey if (isValidFatFilenameChar(c)) { 5510cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey res.append(c); 5520cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey } else { 5530cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey res.append('_'); 5543a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 5553a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 5564f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey // Even though vfat allows 255 UCS-2 chars, we might eventually write to 5574f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey // ext4 through a FUSE layer, so use that limit. 5584f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey trimFilename(res, 255); 5594f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey return res.toString(); 5604f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey } 5614f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey 5624f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey @VisibleForTesting 5634f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey public static String trimFilename(String str, int maxBytes) { 5644f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey final StringBuilder res = new StringBuilder(str); 5654f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey trimFilename(res, maxBytes); 5660cce5355b45d835f95a8918b8b803fd977d374e4Jeff Sharkey return res.toString(); 5673a44f3f1b446315ef894e01d2ab9b5388c2bd8c4Jeff Sharkey } 56857dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey 5694f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey private static void trimFilename(StringBuilder res, int maxBytes) { 5704f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey byte[] raw = res.toString().getBytes(StandardCharsets.UTF_8); 5714f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey if (raw.length > maxBytes) { 5724f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey maxBytes -= 3; 5734f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey while (raw.length > maxBytes) { 5744f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey res.deleteCharAt(res.length() / 2); 5754f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey raw = res.toString().getBytes(StandardCharsets.UTF_8); 5764f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey } 5774f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey res.insert(res.length() / 2, "..."); 5784f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey } 5794f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey } 5804f5e8b3ca489245005b76176ac6d28f5f184f3feJeff Sharkey 58157dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey public static String rewriteAfterRename(File beforeDir, File afterDir, String path) { 582d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey if (path == null) return null; 58357dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey final File result = rewriteAfterRename(beforeDir, afterDir, new File(path)); 58457dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey return (result != null) ? result.getAbsolutePath() : null; 58557dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey } 58657dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey 587d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey public static String[] rewriteAfterRename(File beforeDir, File afterDir, String[] paths) { 588d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey if (paths == null) return null; 589d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey final String[] result = new String[paths.length]; 590d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey for (int i = 0; i < paths.length; i++) { 591d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey result[i] = rewriteAfterRename(beforeDir, afterDir, paths[i]); 592d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey } 593d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey return result; 594d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey } 595d746057f2414cba2bdc69257cc5be8cb681bb592Jeff Sharkey 59657dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey /** 59757dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey * Given a path under the "before" directory, rewrite it to live under the 59857dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey * "after" directory. For example, {@code /before/foo/bar.txt} would become 59957dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey * {@code /after/foo/bar.txt}. 60057dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey */ 60157dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey public static File rewriteAfterRename(File beforeDir, File afterDir, File file) { 60241be35dd011dbd7ed3a9d08e8fe3eb85ea0e5b64Jeff Sharkey if (file == null || beforeDir == null || afterDir == null) return null; 60357dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey if (contains(beforeDir, file)) { 60457dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey final String splice = file.getAbsolutePath().substring( 60557dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey beforeDir.getAbsolutePath().length()); 60657dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey return new File(afterDir, splice); 60757dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey } 60857dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey return null; 60957dcf5b177b56195421535938544f32d8b591b42Jeff Sharkey } 61062539a220c6810f66b63060326bd1668f7d6b029Ben Kwa 611aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey private static File buildUniqueFileWithExtension(File parent, String name, String ext) 612aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey throws FileNotFoundException { 613aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey File file = buildFile(parent, name, ext); 614aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey 615aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey // If conflicting file, try adding counter suffix 616aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey int n = 0; 617aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey while (file.exists()) { 618aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey if (n++ >= 32) { 619aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey throw new FileNotFoundException("Failed to create unique file"); 620aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey } 621aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey file = buildFile(parent, name + " (" + n + ")", ext); 622aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey } 623aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey 624aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey return file; 625aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey } 626aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey 62762539a220c6810f66b63060326bd1668f7d6b029Ben Kwa /** 62862539a220c6810f66b63060326bd1668f7d6b029Ben Kwa * Generates a unique file name under the given parent directory. If the display name doesn't 62962539a220c6810f66b63060326bd1668f7d6b029Ben Kwa * have an extension that matches the requested MIME type, the default extension for that MIME 63062539a220c6810f66b63060326bd1668f7d6b029Ben Kwa * type is appended. If a file already exists, the name is appended with a numerical value to 63162539a220c6810f66b63060326bd1668f7d6b029Ben Kwa * make it unique. 63262539a220c6810f66b63060326bd1668f7d6b029Ben Kwa * 63362539a220c6810f66b63060326bd1668f7d6b029Ben Kwa * For example, the display name 'example' with 'text/plain' MIME might produce 63462539a220c6810f66b63060326bd1668f7d6b029Ben Kwa * 'example.txt' or 'example (1).txt', etc. 63562539a220c6810f66b63060326bd1668f7d6b029Ben Kwa * 63662539a220c6810f66b63060326bd1668f7d6b029Ben Kwa * @throws FileNotFoundException 63762539a220c6810f66b63060326bd1668f7d6b029Ben Kwa */ 63862539a220c6810f66b63060326bd1668f7d6b029Ben Kwa public static File buildUniqueFile(File parent, String mimeType, String displayName) 63962539a220c6810f66b63060326bd1668f7d6b029Ben Kwa throws FileNotFoundException { 640fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono final String[] parts = splitFileName(mimeType, displayName); 641aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey return buildUniqueFileWithExtension(parent, parts[0], parts[1]); 642aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey } 643fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono 644aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey /** 645aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey * Generates a unique file name under the given parent directory, keeping 646aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey * any extension intact. 647aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey */ 648aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey public static File buildUniqueFile(File parent, String displayName) 649aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey throws FileNotFoundException { 650aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey final String name; 651aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey final String ext; 652aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey 653aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey // Extract requested extension from display name 654aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey final int lastDot = displayName.lastIndexOf('.'); 655aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey if (lastDot >= 0) { 656aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey name = displayName.substring(0, lastDot); 657aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey ext = displayName.substring(lastDot + 1); 658aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey } else { 659aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey name = displayName; 660aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey ext = null; 661fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono } 662fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono 663aa44476f64ed5d47b21bc313d065964b065ce918Jeff Sharkey return buildUniqueFileWithExtension(parent, name, ext); 664fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono } 665fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono 666fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono /** 667fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono * Splits file name into base name and extension. 668fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono * If the display name doesn't have an extension that matches the requested MIME type, the 669fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono * extension is regarded as a part of filename and default extension for that MIME type is 670fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono * appended. 671fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono */ 672fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono public static String[] splitFileName(String mimeType, String displayName) { 67362539a220c6810f66b63060326bd1668f7d6b029Ben Kwa String name; 67462539a220c6810f66b63060326bd1668f7d6b029Ben Kwa String ext; 67562539a220c6810f66b63060326bd1668f7d6b029Ben Kwa 67662539a220c6810f66b63060326bd1668f7d6b029Ben Kwa if (Document.MIME_TYPE_DIR.equals(mimeType)) { 67762539a220c6810f66b63060326bd1668f7d6b029Ben Kwa name = displayName; 67862539a220c6810f66b63060326bd1668f7d6b029Ben Kwa ext = null; 67962539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } else { 68062539a220c6810f66b63060326bd1668f7d6b029Ben Kwa String mimeTypeFromExt; 68162539a220c6810f66b63060326bd1668f7d6b029Ben Kwa 68262539a220c6810f66b63060326bd1668f7d6b029Ben Kwa // Extract requested extension from display name 68362539a220c6810f66b63060326bd1668f7d6b029Ben Kwa final int lastDot = displayName.lastIndexOf('.'); 68462539a220c6810f66b63060326bd1668f7d6b029Ben Kwa if (lastDot >= 0) { 68562539a220c6810f66b63060326bd1668f7d6b029Ben Kwa name = displayName.substring(0, lastDot); 68662539a220c6810f66b63060326bd1668f7d6b029Ben Kwa ext = displayName.substring(lastDot + 1); 68762539a220c6810f66b63060326bd1668f7d6b029Ben Kwa mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension( 68862539a220c6810f66b63060326bd1668f7d6b029Ben Kwa ext.toLowerCase()); 68962539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } else { 69062539a220c6810f66b63060326bd1668f7d6b029Ben Kwa name = displayName; 69162539a220c6810f66b63060326bd1668f7d6b029Ben Kwa ext = null; 69262539a220c6810f66b63060326bd1668f7d6b029Ben Kwa mimeTypeFromExt = null; 69362539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } 69462539a220c6810f66b63060326bd1668f7d6b029Ben Kwa 69562539a220c6810f66b63060326bd1668f7d6b029Ben Kwa if (mimeTypeFromExt == null) { 69662539a220c6810f66b63060326bd1668f7d6b029Ben Kwa mimeTypeFromExt = "application/octet-stream"; 69762539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } 69862539a220c6810f66b63060326bd1668f7d6b029Ben Kwa 69962539a220c6810f66b63060326bd1668f7d6b029Ben Kwa final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType( 70062539a220c6810f66b63060326bd1668f7d6b029Ben Kwa mimeType); 70162539a220c6810f66b63060326bd1668f7d6b029Ben Kwa if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) { 70262539a220c6810f66b63060326bd1668f7d6b029Ben Kwa // Extension maps back to requested MIME type; allow it 70362539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } else { 70462539a220c6810f66b63060326bd1668f7d6b029Ben Kwa // No match; insist that create file matches requested MIME 70562539a220c6810f66b63060326bd1668f7d6b029Ben Kwa name = displayName; 70662539a220c6810f66b63060326bd1668f7d6b029Ben Kwa ext = extFromMimeType; 70762539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } 70862539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } 70962539a220c6810f66b63060326bd1668f7d6b029Ben Kwa 710fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono if (ext == null) { 711fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono ext = ""; 71262539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } 71362539a220c6810f66b63060326bd1668f7d6b029Ben Kwa 714fc7fb7533f46b53247d1e6e6edca6e6c9ac676feDaichi Hirono return new String[] { name, ext }; 71562539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } 71662539a220c6810f66b63060326bd1668f7d6b029Ben Kwa 71762539a220c6810f66b63060326bd1668f7d6b029Ben Kwa private static File buildFile(File parent, String name, String ext) { 71862539a220c6810f66b63060326bd1668f7d6b029Ben Kwa if (TextUtils.isEmpty(ext)) { 71962539a220c6810f66b63060326bd1668f7d6b029Ben Kwa return new File(parent, name); 72062539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } else { 72162539a220c6810f66b63060326bd1668f7d6b029Ben Kwa return new File(parent, name + "." + ext); 72262539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } 72362539a220c6810f66b63060326bd1668f7d6b029Ben Kwa } 72485ced632680642fce680d141ddd10299ff849233Jeff Sharkey 725c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey public static @NonNull String[] listOrEmpty(@Nullable File dir) { 726c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey if (dir == null) return EmptyArray.STRING; 727c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey final String[] res = dir.list(); 728c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey if (res != null) { 729c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey return res; 730c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey } else { 731c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey return EmptyArray.STRING; 732c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey } 733c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey } 734c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey 735c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey public static @NonNull File[] listFilesOrEmpty(@Nullable File dir) { 736c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey if (dir == null) return EMPTY; 737c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey final File[] res = dir.listFiles(); 73885ced632680642fce680d141ddd10299ff849233Jeff Sharkey if (res != null) { 73985ced632680642fce680d141ddd10299ff849233Jeff Sharkey return res; 74085ced632680642fce680d141ddd10299ff849233Jeff Sharkey } else { 74185ced632680642fce680d141ddd10299ff849233Jeff Sharkey return EMPTY; 74285ced632680642fce680d141ddd10299ff849233Jeff Sharkey } 74385ced632680642fce680d141ddd10299ff849233Jeff Sharkey } 74415447798a38d2b5acb1998731340255f4203f294Jeff Sharkey 745c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey public static @NonNull File[] listFilesOrEmpty(@Nullable File dir, FilenameFilter filter) { 746c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey if (dir == null) return EMPTY; 747c4bab9843ab936f9a9f134e2088a5bd891eb55c2Jeff Sharkey final File[] res = dir.listFiles(filter); 74835871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey if (res != null) { 74935871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey return res; 75035871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } else { 75135871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey return EMPTY; 75235871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } 75335871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey } 75435871f2c2bb114806b4e3f109960b7f863d7885cJeff Sharkey 75515447798a38d2b5acb1998731340255f4203f294Jeff Sharkey public static @Nullable File newFileOrNull(@Nullable String path) { 75615447798a38d2b5acb1998731340255f4203f294Jeff Sharkey return (path != null) ? new File(path) : null; 75715447798a38d2b5acb1998731340255f4203f294Jeff Sharkey } 7585c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath 7595c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath /** 7605c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath * Creates a directory with name {@code name} under an existing directory {@code baseDir}. 7615c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath * Returns a {@code File} object representing the directory on success, {@code null} on 7625c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath * failure. 7635c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath */ 7645c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath public static @Nullable File createDir(File baseDir, String name) { 7655c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath final File dir = new File(baseDir, name); 7665c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath 7675c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath if (dir.exists()) { 7685c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath return dir.isDirectory() ? dir : null; 7695c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath } 7705c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath 7715c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath return dir.mkdir() ? dir : null; 7725c50e8630164d7d9a1a097f70d2f8bcbf1bd854fNarayan Kamath } 773373d01766f27476e81a174727dcfeee406742417Jeff Sharkey 774373d01766f27476e81a174727dcfeee406742417Jeff Sharkey /** 775373d01766f27476e81a174727dcfeee406742417Jeff Sharkey * Round the given size of a storage device to a nice round power-of-two 776373d01766f27476e81a174727dcfeee406742417Jeff Sharkey * value, such as 256MB or 32GB. This avoids showing weird values like 777373d01766f27476e81a174727dcfeee406742417Jeff Sharkey * "29.5GB" in UI. 778373d01766f27476e81a174727dcfeee406742417Jeff Sharkey */ 779373d01766f27476e81a174727dcfeee406742417Jeff Sharkey public static long roundStorageSize(long size) { 78009734df8bc5a2b788c923ec1a8b78e4cb67f5e70Jeff Sharkey long val = 1; 78109734df8bc5a2b788c923ec1a8b78e4cb67f5e70Jeff Sharkey long pow = 1; 78209734df8bc5a2b788c923ec1a8b78e4cb67f5e70Jeff Sharkey while ((val * pow) < size) { 78309734df8bc5a2b788c923ec1a8b78e4cb67f5e70Jeff Sharkey val <<= 1; 78409734df8bc5a2b788c923ec1a8b78e4cb67f5e70Jeff Sharkey if (val > 512) { 78509734df8bc5a2b788c923ec1a8b78e4cb67f5e70Jeff Sharkey val = 1; 78609734df8bc5a2b788c923ec1a8b78e4cb67f5e70Jeff Sharkey pow *= 1000; 78709734df8bc5a2b788c923ec1a8b78e4cb67f5e70Jeff Sharkey } 788373d01766f27476e81a174727dcfeee406742417Jeff Sharkey } 78909734df8bc5a2b788c923ec1a8b78e4cb67f5e70Jeff Sharkey return val * pow; 790373d01766f27476e81a174727dcfeee406742417Jeff Sharkey } 7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 792