• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 android.provider;
18 
19 
20 import android.annotation.FlaggedApi;
21 import android.annotation.NonNull;
22 import android.annotation.SystemApi;
23 import android.app.Service;
24 import android.content.Intent;
25 import android.os.Bundle;
26 import android.os.IBinder;
27 import android.os.ParcelFileDescriptor;
28 import android.os.RemoteCallback;
29 import android.util.Log;
30 
31 import androidx.annotation.Nullable;
32 
33 import com.android.providers.media.flags.Flags;
34 
35 import java.util.ArrayList;
36 import java.util.Map;
37 import java.util.Set;
38 
39 /**
40  * <p> Base class for a service which can be implemented by privileged APKs.
41  * This service gets request only from {@link com.android.providers.media.MediaProvider} to extract
42  * metadata from files. </p>
43  *
44  * <p>
45  * <h3>Manifest entry</h3>
46  * <p>OemMetadataService must require the permission
47  * "com.android.providers.media.permission.BIND_OEM_METADATA_SERVICE". Service will be ignored for
48  * binding if permission is missing. </p>
49  *
50  * <pre class="prettyprint">
51  * {@literal
52  * <service
53  *     android:name=".MyOemMetadataService"
54  *     android:exported="true"
55  *     android:permission="com.android.providers.media.permission.BIND_OEM_METADATA_SERVICE">
56  *     <intent-filter>
57  *         <action android:name="android.provider.OemMetadataService" />
58  *         <category android:name="android.intent.category.DEFAULT"/>
59  *     </intent-filter>
60  * </service>}
61  * </pre>
62  * </p>
63  *
64  * Only one instance of OemMetadataService will be in function at a time.
65  * OEMs can specify the default behavior through runtime resource overlay,
66  * by setting value of the resource {@code config_default_media_oem_metadata_service_package}.
67  * The overlayable subset which has this resource is {@code MediaProviderConfig}
68  *
69  * @hide
70  */
71 @SystemApi
72 @FlaggedApi(Flags.FLAG_ENABLE_OEM_METADATA)
73 public abstract class OemMetadataService extends Service {
74     /**
75      * @hide
76      */
77     private static final String TAG = "OemMetadataService";
78 
79     public static final String SERVICE_INTERFACE = "android.provider.OemMetadataService";
80 
81     /**
82      * @hide
83      */
84     public static final String EXTRA_OEM_SUPPORTED_MIME_TYPES =
85             "android.provider.extra.OEM_SUPPORTED_MIME_TYPES";
86 
87     /**
88      * @hide
89      */
90     public static final String EXTRA_OEM_DATA_KEYS = "android.provider.extra.OEM_DATA_KEYS";
91 
92     /**
93      * @hide
94      */
95     public static final String EXTRA_OEM_DATA_VALUES = "android.provider.extra.OEM_DATA_VALUES";
96 
97     /**
98      * Permission required to protect {@link OemMetadataService} instances. Implementation should
99      * require this in the {@code permission} attribute in their {@code <service>} tag.
100      */
101     public static final String BIND_OEM_METADATA_SERVICE_PERMISSION =
102             "com.android.providers.media.permission.BIND_OEM_METADATA_SERVICE";
103 
104     @Override
105     @NonNull
onBind(@ullable Intent intent)106     public final IBinder onBind(@Nullable Intent intent) {
107         if (!SERVICE_INTERFACE.equals(intent.getAction())) {
108             Log.w(TAG, "Unexpected action:" + intent.getAction());
109             return null;
110         }
111 
112         return mInterface.asBinder();
113     }
114 
115     /**
116      * Returns set of {@link MediaStore.Files.FileColumns#MIME_TYPE} for which OEMs wants to store
117      * custom metadata. OEM metadata will be requested for a file only if it has one of the
118      * supported mime types. Supported mime type can be any mime type and need not be a media mime
119      * type. Returns an empty set if no mime types are supported.
120      *
121      * @return set of {@link MediaStore.Files.FileColumns#MIME_TYPE}
122      */
123     @NonNull
onGetSupportedMimeTypes()124     public abstract Set<String> onGetSupportedMimeTypes();
125 
126     /**
127      * Returns a key-value {@link Map} of {@link String} which OEMs wants to store as custom
128      * metadata for a file. Returns an empty map if no custom data is present for the file.
129      *
130      * @param fd file descriptor of the file in lower file system
131      * @return map of key-value pairs of string
132      */
133     @NonNull
onGetOemCustomData(@onNull ParcelFileDescriptor fd)134     public abstract Map<String, String> onGetOemCustomData(@NonNull ParcelFileDescriptor fd);
135 
136 
137     private final IOemMetadataService mInterface = new IOemMetadataService.Stub() {
138         @Override
139         public void getSupportedMimeTypes(RemoteCallback callback) {
140             Set<String> supportedMimeTypes = onGetSupportedMimeTypes();
141             sendResultForSupportedMimeTypes(supportedMimeTypes, callback);
142         }
143 
144         @Override
145         public void getOemCustomData(ParcelFileDescriptor pfd, RemoteCallback callback) {
146             Map<String, String> oemCustomData = onGetOemCustomData(pfd);
147             sendResultForOemCustomData(oemCustomData, callback);
148         }
149 
150         private void sendResultForOemCustomData(Map<String, String> oemCustomData,
151                 RemoteCallback callback) {
152             Bundle bundle = new Bundle();
153             if (oemCustomData != null && !oemCustomData.isEmpty()) {
154                 ArrayList<String> keyList = new ArrayList<String>(oemCustomData.size());
155                 ArrayList<String> valueList = new ArrayList<String>(oemCustomData.size());
156                 for (String key : oemCustomData.keySet()) {
157                     keyList.add(key);
158                     valueList.add(oemCustomData.get(key));
159                 }
160                 bundle.putStringArrayList(EXTRA_OEM_DATA_KEYS, keyList);
161                 bundle.putStringArrayList(EXTRA_OEM_DATA_VALUES, valueList);
162             }
163 
164             callback.sendResult(bundle);
165         }
166 
167         private void sendResultForSupportedMimeTypes(Set<String> supportedMimeTypes,
168                 RemoteCallback callback) {
169             Bundle bundle = new Bundle();
170             if (supportedMimeTypes != null && !supportedMimeTypes.isEmpty()) {
171                 bundle.putStringArrayList(EXTRA_OEM_SUPPORTED_MIME_TYPES,
172                         new ArrayList<String>(supportedMimeTypes));
173             }
174             callback.sendResult(bundle);
175         }
176     };
177 }
178