1 /* 2 * Copyright (C) 2019 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 package android.car.media; 17 18 import android.annotation.IntDef; 19 import android.annotation.NonNull; 20 import android.annotation.RequiresPermission; 21 import android.annotation.SystemApi; 22 import android.annotation.TestApi; 23 import android.car.Car; 24 import android.car.CarManagerBase; 25 import android.car.annotation.AddedInOrBefore; 26 import android.content.ComponentName; 27 import android.os.IBinder; 28 import android.os.RemoteException; 29 30 import com.android.internal.annotations.GuardedBy; 31 32 import java.lang.annotation.Retention; 33 import java.lang.annotation.RetentionPolicy; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 38 /** 39 * API for updating and receiving updates to the primary media source in the car. 40 * @hide 41 */ 42 @SystemApi 43 public final class CarMediaManager extends CarManagerBase { 44 45 @AddedInOrBefore(majorVersion = 33) 46 public static final int MEDIA_SOURCE_MODE_PLAYBACK = 0; 47 @AddedInOrBefore(majorVersion = 33) 48 public static final int MEDIA_SOURCE_MODE_BROWSE = 1; 49 50 /** @hide */ 51 @IntDef(prefix = { "MEDIA_SOURCE_MODE_" }, value = { 52 MEDIA_SOURCE_MODE_PLAYBACK, 53 MEDIA_SOURCE_MODE_BROWSE 54 }) 55 @Retention(RetentionPolicy.SOURCE) 56 public @interface MediaSourceMode {} 57 58 private final Object mLock = new Object(); 59 60 private final ICarMedia mService; 61 @GuardedBy("mLock") 62 private Map<MediaSourceChangedListener, ICarMediaSourceListener> mCallbackMap = new HashMap(); 63 64 /** 65 * Get an instance of the CarMediaManager. 66 * 67 * Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead. 68 * @hide 69 */ CarMediaManager(Car car, IBinder service)70 public CarMediaManager(Car car, IBinder service) { 71 super(car); 72 mService = ICarMedia.Stub.asInterface(service); 73 } 74 75 /** 76 * Listener for updates to the primary media source 77 */ 78 public interface MediaSourceChangedListener { 79 80 /** 81 * Called when the primary media source is changed 82 */ 83 @AddedInOrBefore(majorVersion = 33) onMediaSourceChanged(@onNull ComponentName componentName)84 void onMediaSourceChanged(@NonNull ComponentName componentName); 85 } 86 87 /** 88 * Gets the currently active media source for the provided mode 89 * 90 * @param mode the mode (playback or browse) for which the media source is active in. 91 * @return the active media source in the provided mode, will be non-{@code null}. 92 */ 93 @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) 94 @AddedInOrBefore(majorVersion = 33) getMediaSource(@ediaSourceMode int mode)95 public @NonNull ComponentName getMediaSource(@MediaSourceMode int mode) { 96 try { 97 return mService.getMediaSource(mode); 98 } catch (RemoteException e) { 99 return handleRemoteExceptionFromCarService(e, null); 100 } 101 } 102 103 /** 104 * Sets the currently active media source for the provided mode 105 * 106 * @param mode the mode (playback or browse) for which the media source is active in. 107 */ 108 @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) 109 @AddedInOrBefore(majorVersion = 33) setMediaSource(@onNull ComponentName componentName, @MediaSourceMode int mode)110 public void setMediaSource(@NonNull ComponentName componentName, @MediaSourceMode int mode) { 111 try { 112 mService.setMediaSource(componentName, mode); 113 } catch (RemoteException e) { 114 handleRemoteExceptionFromCarService(e); 115 } 116 } 117 118 /** 119 * Register a callback that receives updates to the active media source. 120 * 121 * @param callback the callback to receive active media source updates. 122 * @param mode the mode to receive updates for. 123 */ 124 @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) 125 @AddedInOrBefore(majorVersion = 33) addMediaSourceListener(@onNull MediaSourceChangedListener callback, @MediaSourceMode int mode)126 public void addMediaSourceListener(@NonNull MediaSourceChangedListener callback, 127 @MediaSourceMode int mode) { 128 try { 129 ICarMediaSourceListener binderCallback = new ICarMediaSourceListener.Stub() { 130 @Override 131 public void onMediaSourceChanged(ComponentName componentName) { 132 callback.onMediaSourceChanged(componentName); 133 } 134 }; 135 synchronized (mLock) { 136 mCallbackMap.put(callback, binderCallback); 137 } 138 mService.registerMediaSourceListener(binderCallback, mode); 139 } catch (RemoteException e) { 140 handleRemoteExceptionFromCarService(e); 141 } 142 } 143 144 /** 145 * Unregister a callback that receives updates to the active media source. 146 * 147 * @param callback the callback to be unregistered. 148 * @param mode the mode that the callback was registered to receive updates for. 149 */ 150 @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) 151 @AddedInOrBefore(majorVersion = 33) removeMediaSourceListener(@onNull MediaSourceChangedListener callback, @MediaSourceMode int mode)152 public void removeMediaSourceListener(@NonNull MediaSourceChangedListener callback, 153 @MediaSourceMode int mode) { 154 try { 155 synchronized (mLock) { 156 ICarMediaSourceListener binderCallback = mCallbackMap.remove(callback); 157 mService.unregisterMediaSourceListener(binderCallback, mode); 158 } 159 } catch (RemoteException e) { 160 handleRemoteExceptionFromCarService(e); 161 } 162 } 163 164 /** 165 * Retrieve a list of media sources, ordered by most recently used. 166 * 167 * @param mode the mode (playback or browse) for which to retrieve media sources from. 168 * @return non-{@code null} list of media sources, ordered by most recently used 169 */ 170 @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) 171 @AddedInOrBefore(majorVersion = 33) getLastMediaSources(@ediaSourceMode int mode)172 public @NonNull List<ComponentName> getLastMediaSources(@MediaSourceMode int mode) { 173 try { 174 return mService.getLastMediaSources(mode); 175 } catch (RemoteException e) { 176 return handleRemoteExceptionFromCarService(e, null); 177 } 178 } 179 180 /** @hide */ 181 @Override 182 @AddedInOrBefore(majorVersion = 33) onCarDisconnected()183 public void onCarDisconnected() { 184 synchronized (mLock) { 185 mCallbackMap.clear(); 186 } 187 } 188 189 /** 190 * Returns whether the browse and playback sources can be changed independently. 191 * @return true if the browse and playback sources can be changed independently, false if it 192 * isn't or if the value could not be determined. 193 * @hide 194 */ 195 @TestApi 196 @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) 197 @AddedInOrBefore(majorVersion = 33) isIndependentPlaybackConfig()198 public boolean isIndependentPlaybackConfig() { 199 try { 200 return mService.isIndependentPlaybackConfig(); 201 } catch (RemoteException e) { 202 return handleRemoteExceptionFromCarService(e, false); 203 } 204 } 205 206 /** 207 * Sets whether the browse and playback sources can be changed independently. 208 * @param independent whether the browse and playback sources can be changed independently. 209 * @hide 210 */ 211 @TestApi 212 @RequiresPermission(value = android.Manifest.permission.MEDIA_CONTENT_CONTROL) 213 @AddedInOrBefore(majorVersion = 33) setIndependentPlaybackConfig(boolean independent)214 public void setIndependentPlaybackConfig(boolean independent) { 215 try { 216 mService.setIndependentPlaybackConfig(independent); 217 } catch (RemoteException e) { 218 handleRemoteExceptionFromCarService(e); 219 } 220 } 221 } 222