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