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