1 /* 2 * Copyright 2018 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 androidx.media; 18 19 import android.content.Context; 20 import android.os.Build; 21 import android.support.v4.media.session.MediaControllerCompat; 22 import android.support.v4.media.session.MediaSessionCompat; 23 import android.util.Log; 24 25 import androidx.annotation.NonNull; 26 import androidx.annotation.Nullable; 27 import androidx.core.os.BuildCompat; 28 29 /** 30 * Provides support for interacting with {@link MediaSessionCompat media sessions} that 31 * applications have published to express their ongoing media playback state. 32 * 33 * @see MediaSessionCompat 34 * @see MediaControllerCompat 35 */ 36 public final class MediaSessionManager { 37 static final String TAG = "MediaSessionManager"; 38 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 39 40 private static final Object sLock = new Object(); 41 private static volatile MediaSessionManager sSessionManager; 42 43 MediaSessionManagerImpl mImpl; 44 45 /** 46 * Gets an instance of the media session manager associated with the context. 47 * 48 * @return The MediaSessionManager instance for this context. 49 */ getSessionManager(@onNull Context context)50 public static @NonNull MediaSessionManager getSessionManager(@NonNull Context context) { 51 MediaSessionManager manager = sSessionManager; 52 if (manager == null) { 53 synchronized (sLock) { 54 manager = sSessionManager; 55 if (manager == null) { 56 sSessionManager = new MediaSessionManager(context.getApplicationContext()); 57 manager = sSessionManager; 58 } 59 } 60 } 61 return manager; 62 } 63 MediaSessionManager(Context context)64 private MediaSessionManager(Context context) { 65 if (BuildCompat.isAtLeastP()) { 66 mImpl = new MediaSessionManagerImplApi28(context); 67 } else if (Build.VERSION.SDK_INT >= 21) { 68 mImpl = new MediaSessionManagerImplApi21(context); 69 } else { 70 mImpl = new MediaSessionManagerImplBase(context); 71 } 72 } 73 74 /** 75 * Checks whether the remote user is a trusted app. 76 * <p> 77 * An app is trusted if the app holds the android.Manifest.permission.MEDIA_CONTENT_CONTROL 78 * permission or has an enabled notification listener. 79 * 80 * @param userInfo The remote user info from either 81 * {@link MediaSessionCompat#getCurrentControllerInfo()} and 82 * {@link MediaBrowserServiceCompat#getCurrentBrowserInfo()}. 83 * @return {@code true} if the remote user is trusted and its package name matches with the UID. 84 * {@code false} otherwise. 85 */ isTrustedForMediaControl(@onNull RemoteUserInfo userInfo)86 public boolean isTrustedForMediaControl(@NonNull RemoteUserInfo userInfo) { 87 if (userInfo == null) { 88 throw new IllegalArgumentException("userInfo should not be null"); 89 } 90 return mImpl.isTrustedForMediaControl(userInfo.mImpl); 91 } 92 getContext()93 Context getContext() { 94 return mImpl.getContext(); 95 } 96 97 interface MediaSessionManagerImpl { getContext()98 Context getContext(); isTrustedForMediaControl(RemoteUserInfoImpl userInfo)99 boolean isTrustedForMediaControl(RemoteUserInfoImpl userInfo); 100 } 101 102 interface RemoteUserInfoImpl { getPackageName()103 String getPackageName(); getPid()104 int getPid(); getUid()105 int getUid(); 106 } 107 108 /** 109 * Information of a remote user of {@link android.support.v4.media.session.MediaSessionCompat} 110 * or {@link MediaBrowserServiceCompat}. 111 * This can be used to decide whether the remote user is trusted app. 112 * 113 * @see #isTrustedForMediaControl(RemoteUserInfo) 114 */ 115 public static final class RemoteUserInfo { 116 /** 117 * Used by {@link #getPackageName()} when the session is connected to the legacy controller 118 * whose exact package name cannot be obtained. 119 */ 120 public static String LEGACY_CONTROLLER = "android.media.session.MediaController"; 121 122 RemoteUserInfoImpl mImpl; 123 RemoteUserInfo(@onNull String packageName, int pid, int uid)124 public RemoteUserInfo(@NonNull String packageName, int pid, int uid) { 125 if (BuildCompat.isAtLeastP()) { 126 mImpl = new MediaSessionManagerImplApi28.RemoteUserInfo(packageName, pid, uid); 127 } else { 128 mImpl = new MediaSessionManagerImplBase.RemoteUserInfo(packageName, pid, uid); 129 } 130 } 131 132 /** 133 * @return package name of the controller. Can be {@link #LEGACY_CONTROLLER} if the package 134 * name cannot be obtained. 135 */ getPackageName()136 public @NonNull String getPackageName() { 137 return mImpl.getPackageName(); 138 } 139 140 /** 141 * @return pid of the controller 142 */ getPid()143 public int getPid() { 144 return mImpl.getPid(); 145 } 146 147 /** 148 * @return uid of the controller 149 */ getUid()150 public int getUid() { 151 return mImpl.getUid(); 152 } 153 154 @Override equals(@ullable Object obj)155 public boolean equals(@Nullable Object obj) { 156 return mImpl.equals(obj); 157 } 158 159 @Override hashCode()160 public int hashCode() { 161 return mImpl.hashCode(); 162 } 163 } 164 } 165