• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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