1 /* 2 * Copyright (C) 2021 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 com.android.providers.media.util; 18 19 import static com.android.providers.media.util.FileUtils.buildPath; 20 import static com.android.providers.media.util.FileUtils.buildPrimaryVolumeFile; 21 import static com.android.providers.media.util.FileUtils.extractFileName; 22 23 import androidx.annotation.VisibleForTesting; 24 import android.text.TextUtils; 25 import android.util.Log; 26 27 import java.io.File; 28 import java.io.IOException; 29 import java.io.RandomAccessFile; 30 import java.util.ArrayList; 31 import java.util.List; 32 import java.util.Locale; 33 34 public final class SyntheticPathUtils { 35 private static final String TAG = "SyntheticPathUtils"; 36 37 private static final String TRANSFORMS_DIR = ".transforms"; 38 private static final String SYNTHETIC_DIR = "synthetic"; 39 private static final String REDACTED_DIR = "redacted"; 40 private static final String PICKER_DIR = "picker"; 41 42 public static final String REDACTED_URI_ID_PREFIX = "RUID"; 43 public static final int REDACTED_URI_ID_SIZE = 36; 44 SyntheticPathUtils()45 private SyntheticPathUtils() {} 46 getRedactedRelativePath()47 public static String getRedactedRelativePath() { 48 return buildPath(/* base */ null, TRANSFORMS_DIR, SYNTHETIC_DIR, REDACTED_DIR).getPath(); 49 } 50 getPickerRelativePath()51 public static String getPickerRelativePath() { 52 return buildPath(/* base */ null, TRANSFORMS_DIR, SYNTHETIC_DIR, PICKER_DIR).getPath(); 53 } 54 isRedactedPath(String path, int userId)55 public static boolean isRedactedPath(String path, int userId) { 56 if (path == null) return false; 57 58 final String redactedDir = buildPrimaryVolumeFile(userId, getRedactedRelativePath()) 59 .getAbsolutePath(); 60 final String fileName = extractFileName(path); 61 62 return fileName != null 63 && startsWith(path, redactedDir) 64 && startsWith(fileName, REDACTED_URI_ID_PREFIX) 65 && fileName.length() == REDACTED_URI_ID_SIZE; 66 } 67 isPickerPath(String path, int userId)68 public static boolean isPickerPath(String path, int userId) { 69 final String pickerDir = buildPrimaryVolumeFile(userId, getPickerRelativePath()) 70 .getAbsolutePath(); 71 72 return path != null && startsWith(path, pickerDir); 73 } 74 isSyntheticPath(String path, int userId)75 public static boolean isSyntheticPath(String path, int userId) { 76 final String syntheticDir = buildPrimaryVolumeFile(userId, getSyntheticRelativePath()) 77 .getAbsolutePath(); 78 79 return path != null && startsWith(path, syntheticDir); 80 } 81 extractSyntheticRelativePathSegements(String path, int userId)82 public static List<String> extractSyntheticRelativePathSegements(String path, int userId) { 83 final List<String> segments = new ArrayList<>(); 84 final String syntheticDir = buildPrimaryVolumeFile(userId, 85 getSyntheticRelativePath()).getAbsolutePath(); 86 87 if (path.toLowerCase(Locale.ROOT).indexOf(syntheticDir.toLowerCase(Locale.ROOT)) < 0) { 88 return segments; 89 } 90 91 final String[] segmentArray = path.substring(syntheticDir.length()).split("/"); 92 for (String segment : segmentArray) { 93 if (TextUtils.isEmpty(segment)) { 94 continue; 95 } 96 segments.add(segment); 97 } 98 99 return segments; 100 } 101 createSparseFile(File file, long size)102 public static boolean createSparseFile(File file, long size) { 103 if (size < 0) { 104 return false; 105 } 106 107 try (RandomAccessFile raf = new RandomAccessFile(file, "rw")) { 108 raf.setLength(size); 109 return true; 110 } catch (IOException e) { 111 Log.e(TAG, "Failed to create sparse file: " + file, e); 112 file.delete(); 113 return false; 114 } 115 } 116 117 @VisibleForTesting getSyntheticRelativePath()118 static String getSyntheticRelativePath() { 119 return buildPath(/* base */ null, TRANSFORMS_DIR, SYNTHETIC_DIR).getPath(); 120 } 121 startsWith(String value, String prefix)122 private static boolean startsWith(String value, String prefix) { 123 return value.toLowerCase(Locale.ROOT).startsWith(prefix.toLowerCase(Locale.ROOT)); 124 } 125 } 126