1 /* 2 * Copyright 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 17 package com.android.server.media; 18 19 import android.media.MediaController2; 20 import android.media.Session2CommandGroup; 21 import android.media.Session2Token; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.os.ResultReceiver; 25 import android.os.UserHandle; 26 import android.util.Log; 27 import android.view.KeyEvent; 28 29 import com.android.internal.annotations.GuardedBy; 30 31 import java.io.PrintWriter; 32 33 /** 34 * Keeps the record of {@link Session2Token} to help send command to the corresponding session. 35 */ 36 // TODO(jaewan): Do not call service method directly -- introduce listener instead. 37 public class MediaSession2Record implements MediaSessionRecordImpl { 38 private static final String TAG = "MediaSession2Record"; 39 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 40 private final Object mLock = new Object(); 41 42 @GuardedBy("mLock") 43 private final Session2Token mSessionToken; 44 @GuardedBy("mLock") 45 private final HandlerExecutor mHandlerExecutor; 46 @GuardedBy("mLock") 47 private final MediaController2 mController; 48 @GuardedBy("mLock") 49 private final MediaSessionService mService; 50 @GuardedBy("mLock") 51 private boolean mIsConnected; 52 @GuardedBy("mLock") 53 private int mPolicies; 54 @GuardedBy("mLock") 55 private boolean mIsClosed; 56 MediaSession2Record(Session2Token sessionToken, MediaSessionService service, Looper handlerLooper, int policies)57 public MediaSession2Record(Session2Token sessionToken, MediaSessionService service, 58 Looper handlerLooper, int policies) { 59 // The lock is required to prevent `Controller2Callback` from using partially initialized 60 // `MediaSession2Record.this`. 61 synchronized (mLock) { 62 mSessionToken = sessionToken; 63 mService = service; 64 mHandlerExecutor = new HandlerExecutor(new Handler(handlerLooper)); 65 mController = new MediaController2.Builder(service.getContext(), sessionToken) 66 .setControllerCallback(mHandlerExecutor, new Controller2Callback()) 67 .build(); 68 mPolicies = policies; 69 } 70 } 71 72 @Override getPackageName()73 public String getPackageName() { 74 return mSessionToken.getPackageName(); 75 } 76 getSession2Token()77 public Session2Token getSession2Token() { 78 return mSessionToken; 79 } 80 81 @Override getUid()82 public int getUid() { 83 return mSessionToken.getUid(); 84 } 85 86 @Override getUserId()87 public int getUserId() { 88 return UserHandle.getUserHandleForUid(mSessionToken.getUid()).getIdentifier(); 89 } 90 91 @Override isSystemPriority()92 public boolean isSystemPriority() { 93 // System priority session is currently only allowed for telephony, so it's OK to stick to 94 // the media1 API at this moment. 95 return false; 96 } 97 98 @Override adjustVolume(String packageName, String opPackageName, int pid, int uid, boolean asSystemService, int direction, int flags, boolean useSuggested)99 public void adjustVolume(String packageName, String opPackageName, int pid, int uid, 100 boolean asSystemService, int direction, int flags, boolean useSuggested) { 101 // TODO(jaewan): Add API to adjust volume. 102 } 103 104 @Override isActive()105 public boolean isActive() { 106 synchronized (mLock) { 107 return mIsConnected; 108 } 109 } 110 111 @Override checkPlaybackActiveState(boolean expected)112 public boolean checkPlaybackActiveState(boolean expected) { 113 synchronized (mLock) { 114 return mIsConnected && mController.isPlaybackActive() == expected; 115 } 116 } 117 118 @Override isPlaybackTypeLocal()119 public boolean isPlaybackTypeLocal() { 120 // TODO(jaewan): Implement -- need API to know whether the playback is remote or local. 121 return true; 122 } 123 124 @Override close()125 public void close() { 126 synchronized (mLock) { 127 mIsClosed = true; 128 // Call close regardless of the mIsConnected. This may be called when it's not yet 129 // connected. 130 mController.close(); 131 } 132 } 133 134 @Override isClosed()135 public boolean isClosed() { 136 synchronized (mLock) { 137 return mIsClosed; 138 } 139 } 140 141 @Override sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, KeyEvent ke, int sequenceId, ResultReceiver cb)142 public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, 143 KeyEvent ke, int sequenceId, ResultReceiver cb) { 144 // TODO(jaewan): Implement. 145 return false; 146 } 147 148 @Override canHandleVolumeKey()149 public boolean canHandleVolumeKey() { 150 // TODO: Implement when MediaSession2 starts to get key events. 151 return false; 152 } 153 154 @Override getSessionPolicies()155 public int getSessionPolicies() { 156 synchronized (mLock) { 157 return mPolicies; 158 } 159 } 160 161 @Override setSessionPolicies(int policies)162 public void setSessionPolicies(int policies) { 163 synchronized (mLock) { 164 mPolicies = policies; 165 } 166 } 167 168 @Override dump(PrintWriter pw, String prefix)169 public void dump(PrintWriter pw, String prefix) { 170 pw.println(prefix + "token=" + mSessionToken); 171 pw.println(prefix + "controller=" + mController); 172 173 final String indent = prefix + " "; 174 pw.println(indent + "playbackActive=" + mController.isPlaybackActive()); 175 } 176 177 @Override toString()178 public String toString() { 179 // TODO(jaewan): Also add getId(). 180 return getPackageName() + " (userId=" + getUserId() + ")"; 181 } 182 183 private class Controller2Callback extends MediaController2.ControllerCallback { 184 @Override onConnected(MediaController2 controller, Session2CommandGroup allowedCommands)185 public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) { 186 if (DEBUG) { 187 Log.d(TAG, "connected to " + mSessionToken + ", allowed=" + allowedCommands); 188 } 189 MediaSessionService service; 190 synchronized (mLock) { 191 mIsConnected = true; 192 service = mService; 193 } 194 service.onSessionActiveStateChanged(MediaSession2Record.this); 195 } 196 197 @Override onDisconnected(MediaController2 controller)198 public void onDisconnected(MediaController2 controller) { 199 if (DEBUG) { 200 Log.d(TAG, "disconnected from " + mSessionToken); 201 } 202 MediaSessionService service; 203 synchronized (mLock) { 204 mIsConnected = false; 205 service = mService; 206 } 207 service.onSessionDied(MediaSession2Record.this); 208 } 209 210 @Override onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive)211 public void onPlaybackActiveChanged(MediaController2 controller, boolean playbackActive) { 212 if (DEBUG) { 213 Log.d(TAG, "playback active changed, " + mSessionToken + ", active=" 214 + playbackActive); 215 } 216 MediaSessionService service; 217 synchronized (mLock) { 218 service = mService; 219 } 220 service.onSessionPlaybackStateChanged(MediaSession2Record.this, playbackActive); 221 } 222 } 223 } 224