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