1 /* 2 * Copyright (C) 2014 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.audio; 18 19 import android.app.PendingIntent; 20 import android.content.ComponentName; 21 import android.media.AudioManager; 22 import android.media.IRemoteControlClient; 23 import android.media.IRemoteVolumeObserver; 24 import android.media.RemoteControlClient; 25 import android.os.IBinder; 26 import android.os.IBinder.DeathRecipient; 27 import android.os.RemoteException; 28 import android.util.Log; 29 30 import java.io.PrintWriter; 31 32 /** 33 * @hide 34 * Class to handle all the information about a media player, encapsulating information 35 * about its use RemoteControlClient, playback type and volume... The lifecycle of each 36 * instance is managed by android.media.MediaFocusControl, from its addition to the player stack 37 * stack to its release. 38 */ 39 class PlayerRecord implements DeathRecipient { 40 41 // on purpose not using this classe's name, as it will only be used from MediaFocusControl 42 private static final String TAG = "MediaFocusControl"; 43 private static final boolean DEBUG = false; 44 45 /** 46 * A global counter for RemoteControlClient identifiers 47 */ 48 private static int sLastRccId = 0; 49 50 public static MediaFocusControl sController; 51 52 /** 53 * The target for the ACTION_MEDIA_BUTTON events. 54 * Always non null. //FIXME verify 55 */ 56 final private PendingIntent mMediaIntent; 57 /** 58 * The registered media button event receiver. 59 */ 60 final private ComponentName mReceiverComponent; 61 62 private int mRccId = -1; 63 64 /** 65 * A non-null token implies this record tracks a "live" player whose death is being monitored. 66 */ 67 private IBinder mToken; 68 private String mCallingPackageName; 69 private int mCallingUid; 70 /** 71 * Provides access to the information to display on the remote control. 72 * May be null (when a media button event receiver is registered, 73 * but no remote control client has been registered) */ 74 private IRemoteControlClient mRcClient; 75 private RcClientDeathHandler mRcClientDeathHandler; 76 /** 77 * Information only used for non-local playback 78 */ 79 //FIXME private? 80 public int mPlaybackType; 81 public int mPlaybackVolume; 82 public int mPlaybackVolumeMax; 83 public int mPlaybackVolumeHandling; 84 public int mPlaybackStream; 85 public RccPlaybackState mPlaybackState; 86 public IRemoteVolumeObserver mRemoteVolumeObs; 87 88 89 protected static class RccPlaybackState { 90 public int mState; 91 public long mPositionMs; 92 public float mSpeed; 93 RccPlaybackState(int state, long positionMs, float speed)94 public RccPlaybackState(int state, long positionMs, float speed) { 95 mState = state; 96 mPositionMs = positionMs; 97 mSpeed = speed; 98 } 99 reset()100 public void reset() { 101 mState = RemoteControlClient.PLAYSTATE_STOPPED; 102 mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID; 103 mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X; 104 } 105 106 @Override toString()107 public String toString() { 108 return stateToString() + ", " + posToString() + ", " + mSpeed + "X"; 109 } 110 posToString()111 private String posToString() { 112 if (mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) { 113 return "PLAYBACK_POSITION_INVALID"; 114 } else if (mPositionMs == RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) { 115 return "PLAYBACK_POSITION_ALWAYS_UNKNOWN"; 116 } else { 117 return (String.valueOf(mPositionMs) + "ms"); 118 } 119 } 120 stateToString()121 private String stateToString() { 122 switch (mState) { 123 case RemoteControlClient.PLAYSTATE_NONE: 124 return "PLAYSTATE_NONE"; 125 case RemoteControlClient.PLAYSTATE_STOPPED: 126 return "PLAYSTATE_STOPPED"; 127 case RemoteControlClient.PLAYSTATE_PAUSED: 128 return "PLAYSTATE_PAUSED"; 129 case RemoteControlClient.PLAYSTATE_PLAYING: 130 return "PLAYSTATE_PLAYING"; 131 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING: 132 return "PLAYSTATE_FAST_FORWARDING"; 133 case RemoteControlClient.PLAYSTATE_REWINDING: 134 return "PLAYSTATE_REWINDING"; 135 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS: 136 return "PLAYSTATE_SKIPPING_FORWARDS"; 137 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS: 138 return "PLAYSTATE_SKIPPING_BACKWARDS"; 139 case RemoteControlClient.PLAYSTATE_BUFFERING: 140 return "PLAYSTATE_BUFFERING"; 141 case RemoteControlClient.PLAYSTATE_ERROR: 142 return "PLAYSTATE_ERROR"; 143 default: 144 return "[invalid playstate]"; 145 } 146 } 147 } 148 149 150 /** 151 * Inner class to monitor remote control client deaths, and remove the client for the 152 * remote control stack if necessary. 153 */ 154 private class RcClientDeathHandler implements IBinder.DeathRecipient { 155 final private IBinder mCb; // To be notified of client's death 156 //FIXME needed? 157 final private PendingIntent mMediaIntent; 158 RcClientDeathHandler(IBinder cb, PendingIntent pi)159 RcClientDeathHandler(IBinder cb, PendingIntent pi) { 160 mCb = cb; 161 mMediaIntent = pi; 162 } 163 binderDied()164 public void binderDied() { 165 Log.w(TAG, " RemoteControlClient died"); 166 // remote control client died, make sure the displays don't use it anymore 167 // by setting its remote control client to null 168 sController.registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/); 169 // the dead client was maybe handling remote playback, the controller should reevaluate 170 sController.postReevaluateRemote(); 171 } 172 getBinder()173 public IBinder getBinder() { 174 return mCb; 175 } 176 } 177 178 179 protected static class RemotePlaybackState { 180 int mRccId; 181 int mVolume; 182 int mVolumeMax; 183 int mVolumeHandling; 184 RemotePlaybackState(int id, int vol, int volMax)185 protected RemotePlaybackState(int id, int vol, int volMax) { 186 mRccId = id; 187 mVolume = vol; 188 mVolumeMax = volMax; 189 mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; 190 } 191 } 192 193 dump(PrintWriter pw, boolean registrationInfo)194 void dump(PrintWriter pw, boolean registrationInfo) { 195 if (registrationInfo) { 196 pw.println(" pi: " + mMediaIntent + 197 " -- pack: " + mCallingPackageName + 198 " -- ercvr: " + mReceiverComponent + 199 " -- client: " + mRcClient + 200 " -- uid: " + mCallingUid + 201 " -- type: " + mPlaybackType + 202 " state: " + mPlaybackState); 203 } else { 204 // emphasis on state 205 pw.println(" uid: " + mCallingUid + 206 " -- id: " + mRccId + 207 " -- type: " + mPlaybackType + 208 " -- state: " + mPlaybackState + 209 " -- vol handling: " + mPlaybackVolumeHandling + 210 " -- vol: " + mPlaybackVolume + 211 " -- volMax: " + mPlaybackVolumeMax + 212 " -- volObs: " + mRemoteVolumeObs); 213 } 214 } 215 216 setMediaFocusControl(MediaFocusControl mfc)217 static protected void setMediaFocusControl(MediaFocusControl mfc) { 218 sController = mfc; 219 } 220 221 /** precondition: mediaIntent != null */ PlayerRecord(PendingIntent mediaIntent, ComponentName eventReceiver, IBinder token)222 protected PlayerRecord(PendingIntent mediaIntent, ComponentName eventReceiver, IBinder token) 223 { 224 mMediaIntent = mediaIntent; 225 mReceiverComponent = eventReceiver; 226 mToken = token; 227 mCallingUid = -1; 228 mRcClient = null; 229 mRccId = ++sLastRccId; 230 mPlaybackState = new RccPlaybackState( 231 RemoteControlClient.PLAYSTATE_STOPPED, 232 RemoteControlClient.PLAYBACK_POSITION_INVALID, 233 RemoteControlClient.PLAYBACK_SPEED_1X); 234 235 resetPlaybackInfo(); 236 if (mToken != null) { 237 try { 238 mToken.linkToDeath(this, 0); 239 } catch (RemoteException e) { 240 sController.unregisterMediaButtonIntentAsync(mMediaIntent); 241 } 242 } 243 } 244 245 //--------------------------------------------- 246 // Accessors getRccId()247 protected int getRccId() { 248 return mRccId; 249 } 250 getRcc()251 protected IRemoteControlClient getRcc() { 252 return mRcClient; 253 } 254 getMediaButtonReceiver()255 protected ComponentName getMediaButtonReceiver() { 256 return mReceiverComponent; 257 } 258 getMediaButtonIntent()259 protected PendingIntent getMediaButtonIntent() { 260 return mMediaIntent; 261 } 262 hasMatchingMediaButtonIntent(PendingIntent pi)263 protected boolean hasMatchingMediaButtonIntent(PendingIntent pi) { 264 if (mToken != null) { 265 return mMediaIntent.equals(pi); 266 } else { 267 if (mReceiverComponent != null) { 268 return mReceiverComponent.equals(pi.getIntent().getComponent()); 269 } else { 270 return false; 271 } 272 } 273 } 274 isPlaybackActive()275 protected boolean isPlaybackActive() { 276 return MediaFocusControl.isPlaystateActive(mPlaybackState.mState); 277 } 278 279 //--------------------------------------------- 280 // Modify the records stored in the instance resetControllerInfoForRcc(IRemoteControlClient rcClient, String callingPackageName, int uid)281 protected void resetControllerInfoForRcc(IRemoteControlClient rcClient, 282 String callingPackageName, int uid) { 283 // already had a remote control client? 284 if (mRcClientDeathHandler != null) { 285 // stop monitoring the old client's death 286 unlinkToRcClientDeath(); 287 } 288 // save the new remote control client 289 mRcClient = rcClient; 290 mCallingPackageName = callingPackageName; 291 mCallingUid = uid; 292 if (rcClient == null) { 293 // here mcse.mRcClientDeathHandler is null; 294 resetPlaybackInfo(); 295 } else { 296 IBinder b = mRcClient.asBinder(); 297 RcClientDeathHandler rcdh = 298 new RcClientDeathHandler(b, mMediaIntent); 299 try { 300 b.linkToDeath(rcdh, 0); 301 } catch (RemoteException e) { 302 // remote control client is DOA, disqualify it 303 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b); 304 mRcClient = null; 305 } 306 mRcClientDeathHandler = rcdh; 307 } 308 } 309 resetControllerInfoForNoRcc()310 protected void resetControllerInfoForNoRcc() { 311 // stop monitoring the RCC death 312 unlinkToRcClientDeath(); 313 // reset the RCC-related fields 314 mRcClient = null; 315 mCallingPackageName = null; 316 } 317 resetPlaybackInfo()318 public void resetPlaybackInfo() { 319 mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL; 320 mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; 321 mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; 322 mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; 323 mPlaybackStream = AudioManager.STREAM_MUSIC; 324 mPlaybackState.reset(); 325 mRemoteVolumeObs = null; 326 } 327 328 //--------------------------------------------- unlinkToRcClientDeath()329 public void unlinkToRcClientDeath() { 330 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) { 331 try { 332 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0); 333 mRcClientDeathHandler = null; 334 } catch (java.util.NoSuchElementException e) { 335 // not much we can do here 336 Log.e(TAG, "Error in unlinkToRcClientDeath()", e); 337 } 338 } 339 } 340 341 // FIXME rename to "release"? (as in FocusRequester class) destroy()342 public void destroy() { 343 unlinkToRcClientDeath(); 344 if (mToken != null) { 345 mToken.unlinkToDeath(this, 0); 346 mToken = null; 347 } 348 } 349 350 @Override binderDied()351 public void binderDied() { 352 sController.unregisterMediaButtonIntentAsync(mMediaIntent); 353 } 354 355 @Override finalize()356 protected void finalize() throws Throwable { 357 destroy(); // unlink exception handled inside method 358 super.finalize(); 359 } 360 } 361