1 /*
2  * Copyright (C) 2022 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;
18 
19 import android.content.UriMatcher;
20 import android.net.Uri;
21 
22 import com.android.providers.media.photopicker.v2.PickerUriResolverV2;
23 
24 class LocalUriMatcher {
25 
26     // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS
27     // are stored in the "files" table, so do not renumber them unless you also add
28     // a corresponding database upgrade step for it.
29     static final int IMAGES_MEDIA = 1;
30     static final int IMAGES_MEDIA_ID = 2;
31     static final int IMAGES_MEDIA_ID_THUMBNAIL = 3;
32     static final int IMAGES_THUMBNAILS = 4;
33     static final int IMAGES_THUMBNAILS_ID = 5;
34 
35     static final int AUDIO_MEDIA = 100;
36     static final int AUDIO_MEDIA_ID = 101;
37     static final int AUDIO_MEDIA_ID_GENRES = 102;
38     static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
39     static final int AUDIO_GENRES = 106;
40     static final int AUDIO_GENRES_ID = 107;
41     static final int AUDIO_GENRES_ID_MEMBERS = 108;
42     static final int AUDIO_GENRES_ALL_MEMBERS = 109;
43     static final int AUDIO_PLAYLISTS = 110;
44     static final int AUDIO_PLAYLISTS_ID = 111;
45     static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
46     static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
47     static final int AUDIO_ARTISTS = 114;
48     static final int AUDIO_ARTISTS_ID = 115;
49     static final int AUDIO_ALBUMS = 116;
50     static final int AUDIO_ALBUMS_ID = 117;
51     static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
52     static final int AUDIO_ALBUMART = 119;
53     static final int AUDIO_ALBUMART_ID = 120;
54     static final int AUDIO_ALBUMART_FILE_ID = 121;
55 
56     static final int VIDEO_MEDIA = 200;
57     static final int VIDEO_MEDIA_ID = 201;
58     static final int VIDEO_MEDIA_ID_THUMBNAIL = 202;
59     static final int VIDEO_THUMBNAILS = 203;
60     static final int VIDEO_THUMBNAILS_ID = 204;
61 
62     static final int VOLUMES = 300;
63     static final int VOLUMES_ID = 301;
64 
65     static final int MEDIA_SCANNER = 500;
66 
67     static final int FS_ID = 600;
68     static final int VERSION = 601;
69 
70     static final int FILES = 700;
71     static final int FILES_ID = 701;
72 
73     static final int DOWNLOADS = 800;
74     static final int DOWNLOADS_ID = 801;
75 
76     static final int PICKER = 900;
77     public static final int PICKER_ID = 901;
78     static final int PICKER_INTERNAL_MEDIA_ALL = 902;
79     static final int PICKER_INTERNAL_MEDIA_LOCAL = 903;
80     static final int PICKER_INTERNAL_ALBUMS_ALL = 904;
81     static final int PICKER_INTERNAL_ALBUMS_LOCAL = 905;
82     public static final int PICKER_GET_CONTENT_ID = 906;
83 
84     /**
85      * Picker V2 base URI. {@see PickerUriResolverV2}.
86      */
87     public static final int PICKER_INTERNAL_V2 = 908;
88 
89     public static final int MEDIA_GRANTS = 1000;
90 
91     // MediaProvider Command Line Interface
92     static final int CLI = 100_000;
93 
94     private final UriMatcher mPublic = new UriMatcher(UriMatcher.NO_MATCH);
95     private final UriMatcher mHidden = new UriMatcher(UriMatcher.NO_MATCH);
96 
97     /* Matcher for Picker V2 URI */
98     private final UriMatcher mPickerInternalV2 = new UriMatcher(UriMatcher.NO_MATCH);
99 
matchUri(Uri uri, boolean allowHidden)100     int matchUri(Uri uri, boolean allowHidden) {
101         return matchUri(uri, allowHidden, /* isCallerPhotoPicker */ false);
102     }
103 
matchUri(Uri uri, boolean allowHidden, boolean isCallerPhotoPicker)104     int matchUri(Uri uri, boolean allowHidden, boolean isCallerPhotoPicker) {
105         final int publicMatch = mPublic.match(uri);
106         if (publicMatch != UriMatcher.NO_MATCH) {
107             return publicMatch;
108         }
109 
110         if (isCallerPhotoPicker) {
111             final int pickerInternalMatch = mPickerInternalV2.match(uri);
112             if (pickerInternalMatch != UriMatcher.NO_MATCH) {
113                 return pickerInternalMatch;
114             }
115         }
116 
117         final int hiddenMatch = mHidden.match(uri);
118         if (hiddenMatch != UriMatcher.NO_MATCH) {
119             // Detect callers asking about hidden behavior by looking closer when
120             // the matchers diverge; we only care about apps that are explicitly
121             // targeting a specific public API level.
122             if (!allowHidden) {
123                 throw new IllegalStateException("Unknown URL: " + uri + " is hidden API");
124             }
125             return hiddenMatch;
126         }
127 
128         return UriMatcher.NO_MATCH;
129     }
130 
LocalUriMatcher(String auth)131     LocalUriMatcher(String auth) {
132         // Warning: Do not move these exact string matches below "*/.." matches.
133         // If "*/.." match is added to mPublic children before "picker/#/#", then while matching
134         // "picker/0/10", UriMatcher matches "*" node with "picker" and tries to match "0/10"
135         // with children of "*".
136         // UriMatcher does not look for exact "picker" string match if it finds * node before
137         // it. It finds the first best child match and proceeds the match from there without
138         // looking at other siblings.
139         mPublic.addURI(auth, "picker", PICKER);
140         // TODO(b/195009139): Remove after switching picker URI to new format
141         // content://media/picker/<user-id>/<media-id>
142         mPublic.addURI(auth, "picker/#/#", PICKER_ID);
143         // content://media/picker/<user-id>/<authority>/media/<media-id>
144         mPublic.addURI(auth, "picker/#/*/media/*", PICKER_ID);
145 
146         // content://media/picker_get_content/<user-id>/<media-id>
147         mPublic.addURI(auth, "picker_get_content/#/#", PICKER_GET_CONTENT_ID);
148         // content://media/picker_get_content/<user-id>/<authority>/media/<media-id>
149         mPublic.addURI(auth, "picker_get_content/#/*/media/*", PICKER_GET_CONTENT_ID);
150 
151         mPublic.addURI(auth, "cli", CLI);
152 
153         mPublic.addURI(auth, "*/images/media", IMAGES_MEDIA);
154         mPublic.addURI(auth, "*/images/media/#", IMAGES_MEDIA_ID);
155         mPublic.addURI(auth, "*/images/media/#/thumbnail", IMAGES_MEDIA_ID_THUMBNAIL);
156         mPublic.addURI(auth, "*/images/thumbnails", IMAGES_THUMBNAILS);
157         mPublic.addURI(auth, "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
158 
159         mPublic.addURI(auth, "*/audio/media", AUDIO_MEDIA);
160         mPublic.addURI(auth, "*/audio/media/#", AUDIO_MEDIA_ID);
161         mPublic.addURI(auth, "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
162         mPublic.addURI(auth, "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
163         mPublic.addURI(auth, "*/audio/genres", AUDIO_GENRES);
164         mPublic.addURI(auth, "*/audio/genres/#", AUDIO_GENRES_ID);
165         mPublic.addURI(auth, "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
166         // TODO: not actually defined in API, but CTS tested
167         mPublic.addURI(auth, "*/audio/genres/all/members", AUDIO_GENRES_ALL_MEMBERS);
168         mPublic.addURI(auth, "*/audio/playlists", AUDIO_PLAYLISTS);
169         mPublic.addURI(auth, "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
170         mPublic.addURI(auth, "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
171         mPublic.addURI(auth, "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
172         mPublic.addURI(auth, "*/audio/artists", AUDIO_ARTISTS);
173         mPublic.addURI(auth, "*/audio/artists/#", AUDIO_ARTISTS_ID);
174         mPublic.addURI(auth, "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
175         mPublic.addURI(auth, "*/audio/albums", AUDIO_ALBUMS);
176         mPublic.addURI(auth, "*/audio/albums/#", AUDIO_ALBUMS_ID);
177         // TODO: not actually defined in API, but CTS tested
178         mPublic.addURI(auth, "*/audio/albumart", AUDIO_ALBUMART);
179         // TODO: not actually defined in API, but CTS tested
180         mPublic.addURI(auth, "*/audio/albumart/#", AUDIO_ALBUMART_ID);
181         // TODO: not actually defined in API, but CTS tested
182         mPublic.addURI(auth, "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
183 
184         mPublic.addURI(auth, "*/video/media", VIDEO_MEDIA);
185         mPublic.addURI(auth, "*/video/media/#", VIDEO_MEDIA_ID);
186         mPublic.addURI(auth, "*/video/media/#/thumbnail", VIDEO_MEDIA_ID_THUMBNAIL);
187         mPublic.addURI(auth, "*/video/thumbnails", VIDEO_THUMBNAILS);
188         mPublic.addURI(auth, "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
189 
190         mPublic.addURI(auth, "*/media_scanner", MEDIA_SCANNER);
191 
192         // NOTE: technically hidden, since Uri is never exposed
193         mPublic.addURI(auth, "*/fs_id", FS_ID);
194         // NOTE: technically hidden, since Uri is never exposed
195         mPublic.addURI(auth, "*/version", VERSION);
196 
197         mHidden.addURI(auth, "picker_internal/media/all", PICKER_INTERNAL_MEDIA_ALL);
198         mHidden.addURI(auth, "picker_internal/media/local", PICKER_INTERNAL_MEDIA_LOCAL);
199         mHidden.addURI(auth, "picker_internal/albums/all", PICKER_INTERNAL_ALBUMS_ALL);
200         mHidden.addURI(auth, "picker_internal/albums/local", PICKER_INTERNAL_ALBUMS_LOCAL);
201 
202         mPickerInternalV2.addURI(
203                 auth, PickerUriResolverV2.BASE_PICKER_PATH + "*", PICKER_INTERNAL_V2);
204         mPickerInternalV2.addURI(
205                 auth, PickerUriResolverV2.BASE_PICKER_PATH + "*/*", PICKER_INTERNAL_V2);
206 
207         mHidden.addURI(auth, "media_grants", MEDIA_GRANTS);
208         mHidden.addURI(auth, "*", VOLUMES_ID);
209         mHidden.addURI(auth, null, VOLUMES);
210 
211         mPublic.addURI(auth, "*/file", FILES);
212         mPublic.addURI(auth, "*/file/#", FILES_ID);
213 
214         mPublic.addURI(auth, "*/downloads", DOWNLOADS);
215         mPublic.addURI(auth, "*/downloads/#", DOWNLOADS_ID);
216     }
217 }
218