Merge "AudioService: remove dead code"
diff --git a/Android.mk b/Android.mk
index 1eff5cc..7ca04a5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -344,8 +344,6 @@
media/java/android/media/IMediaRouterService.aidl \
media/java/android/media/IMediaScannerListener.aidl \
media/java/android/media/IMediaScannerService.aidl \
- media/java/android/media/IRemoteControlClient.aidl \
- media/java/android/media/IRemoteControlDisplay.aidl \
media/java/android/media/IRemoteDisplayCallback.aidl \
media/java/android/media/IRemoteDisplayProvider.aidl \
media/java/android/media/IRemoteVolumeController.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 6e44d77..40908f1 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -236,6 +236,8 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/target/common/obj/framework.aidl)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/DocumentsUI_intermediates)
+$(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IRemoteControlClient.*)
+$(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IRemoteControlDisplay.*)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 50df556..c658675 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2658,107 +2658,6 @@
rctlr.stopListeningToSessions();
}
- /**
- * @hide
- * Registers a remote control display that will be sent information by remote control clients.
- * Use this method if your IRemoteControlDisplay is not going to display artwork, otherwise
- * use {@link #registerRemoteControlDisplay(IRemoteControlDisplay, int, int)} to pass the
- * artwork size directly, or
- * {@link #remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay, int, int)} later if artwork
- * is not yet needed.
- * <p>Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission.
- * @param rcd the IRemoteControlDisplay
- */
- public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
- // passing a negative value for art work width and height as they are unknown at this stage
- registerRemoteControlDisplay(rcd, /*w*/-1, /*h*/ -1);
- }
-
- /**
- * @hide
- * Registers a remote control display that will be sent information by remote control clients.
- * <p>Registration requires the {@link Manifest.permission#MEDIA_CONTENT_CONTROL} permission.
- * @param rcd
- * @param w the maximum width of the expected bitmap. Negative values indicate it is
- * useless to send artwork.
- * @param h the maximum height of the expected bitmap. Negative values indicate it is
- * useless to send artwork.
- */
- public void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
- if (rcd == null) {
- return;
- }
- IAudioService service = getService();
- try {
- service.registerRemoteControlDisplay(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in registerRemoteControlDisplay " + e);
- }
- }
-
- /**
- * @hide
- * Unregisters a remote control display that was sent information by remote control clients.
- * @param rcd
- */
- public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
- if (rcd == null) {
- return;
- }
- IAudioService service = getService();
- try {
- service.unregisterRemoteControlDisplay(rcd);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
- }
- }
-
- /**
- * @hide
- * Sets the artwork size a remote control display expects when receiving bitmaps.
- * @param rcd
- * @param w the maximum width of the expected bitmap. Negative values indicate it is
- * useless to send artwork.
- * @param h the maximum height of the expected bitmap. Negative values indicate it is
- * useless to send artwork.
- */
- public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
- if (rcd == null) {
- return;
- }
- IAudioService service = getService();
- try {
- service.remoteControlDisplayUsesBitmapSize(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in remoteControlDisplayUsesBitmapSize " + e);
- }
- }
-
- /**
- * @hide
- * Controls whether a remote control display needs periodic checks of the RemoteControlClient
- * playback position to verify that the estimated position has not drifted from the actual
- * position. By default the check is not performed.
- * The IRemoteControlDisplay must have been previously registered for this to have any effect.
- * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
- * or disabled. No effect is null.
- * @param wantsSync if true, RemoteControlClient instances which expose their playback position
- * to the framework will regularly compare the estimated playback position with the actual
- * position, and will update the IRemoteControlDisplay implementation whenever a drift is
- * detected.
- */
- public void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
- boolean wantsSync) {
- if (rcd == null) {
- return;
- }
- IAudioService service = getService();
- try {
- service.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in remoteControlDisplayWantsPlaybackPositionSync " + e);
- }
- }
/**
* @hide
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8aebe11..693a519 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -23,9 +23,6 @@
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
-import android.media.IRemoteControlClient;
-import android.media.IRemoteControlDisplay;
-import android.media.IRemoteVolumeObserver;
import android.media.IRingtonePlayer;
import android.media.IVolumeController;
import android.media.Rating;
@@ -47,8 +44,6 @@
void setStreamVolume(int streamType, int index, int flags, String callingPackage);
- oneway void setRemoteStreamVolume(int index);
-
boolean isStreamMute(int streamType);
void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);
@@ -121,59 +116,6 @@
int getCurrentAudioFocus();
- /**
- * Register an IRemoteControlDisplay.
- * Success of registration is subject to a check on
- * the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission.
- * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
- * at the top of the stack to update the new display with its information.
- * @param rcd the IRemoteControlDisplay to register. No effect if null.
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- */
- boolean registerRemoteControlDisplay(in IRemoteControlDisplay rcd, int w, int h);
-
- /**
- * Like registerRemoteControlDisplay, but with success being subject to a check on
- * the android.Manifest.permission.MEDIA_CONTENT_CONTROL permission, and if it fails,
- * success is subject to listenerComp being one of the ENABLED_NOTIFICATION_LISTENERS
- * components.
- */
- boolean registerRemoteController(in IRemoteControlDisplay rcd, int w, int h,
- in ComponentName listenerComp);
-
- /**
- * Unregister an IRemoteControlDisplay.
- * No effect if the IRemoteControlDisplay hasn't been successfully registered.
- * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
- */
- oneway void unregisterRemoteControlDisplay(in IRemoteControlDisplay rcd);
- /**
- * Update the size of the artwork used by an IRemoteControlDisplay.
- * @param rcd the IRemoteControlDisplay with the new artwork size requirement
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- */
- oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
- /**
- * Controls whether a remote control display needs periodic checks of the RemoteControlClient
- * playback position to verify that the estimated position has not drifted from the actual
- * position. By default the check is not performed.
- * The IRemoteControlDisplay must have been previously registered for this to have any effect.
- * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
- * or disabled. Not null.
- * @param wantsSync if true, RemoteControlClient instances which expose their playback position
- * to the framework will regularly compare the estimated playback position with the actual
- * position, and will update the IRemoteControlDisplay implementation whenever a drift is
- * detected.
- */
- oneway void remoteControlDisplayWantsPlaybackPositionSync(in IRemoteControlDisplay rcd,
- boolean wantsSync);
-
void startBluetoothSco(IBinder cb, int targetSdkVersion);
void startBluetoothScoVirtualCall(IBinder cb);
void stopBluetoothSco(IBinder cb);
diff --git a/media/java/android/media/IRemoteControlClient.aidl b/media/java/android/media/IRemoteControlClient.aidl
deleted file mode 100644
index aa142d6..0000000
--- a/media/java/android/media/IRemoteControlClient.aidl
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.graphics.Bitmap;
-import android.media.IRemoteControlDisplay;
-import android.media.Rating;
-
-/**
- * @hide
- * Interface registered by AudioManager to notify a source of remote control information
- * that information is requested to be displayed on the remote control (through
- * IRemoteControlDisplay).
- * {@see AudioManager#registerRemoteControlClient(RemoteControlClient)}.
- */
-oneway interface IRemoteControlClient
-{
- /**
- * Notifies a remote control client that information for the given generation ID is
- * requested. If the flags contains
- * {@link RemoteControlClient#FLAG_INFORMATION_REQUESTED_ALBUM_ART} then the width and height
- * parameters are valid.
- * @param generationId
- * @param infoFlags
- * FIXME: is infoFlags required? since the RCC pushes info, this might always be called
- * with RC_INFO_ALL
- */
- void onInformationRequested(int generationId, int infoFlags);
-
- /**
- * Notifies a remote control client that information for the given generation ID is
- * requested for the given IRemoteControlDisplay alone.
- * @param rcd the display to which current info should be sent
- */
- void informationRequestForDisplay(IRemoteControlDisplay rcd, int w, int h);
-
- /**
- * Sets the generation counter of the current client that is displayed on the remote control.
- */
- void setCurrentClientGenerationId(int clientGeneration);
-
- void plugRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h);
- void unplugRemoteControlDisplay(IRemoteControlDisplay rcd);
- void setBitmapSizeForDisplay(IRemoteControlDisplay rcd, int w, int h);
- void setWantsSyncForDisplay(IRemoteControlDisplay rcd, boolean wantsSync);
- void enableRemoteControlDisplay(IRemoteControlDisplay rcd, boolean enabled);
- void seekTo(int clientGeneration, long timeMs);
- void updateMetadata(int clientGeneration, int key, in Rating value);
-}
\ No newline at end of file
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
deleted file mode 100644
index 1609030..0000000
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.graphics.Bitmap;
-import android.os.Bundle;
-
-/**
- * @hide
- * Interface registered through AudioManager of an object that displays information
- * received from a remote control client.
- * {@see AudioManager#registerRemoteControlDisplay(IRemoteControlDisplay)}.
- */
-oneway interface IRemoteControlDisplay
-{
- /**
- * Sets the generation counter of the current client that is displayed on the remote control.
- * @param clientGeneration the new RemoteControlClient generation
- * @param clientMediaIntent the PendingIntent associated with the client.
- * May be null, which implies there is no registered media button event receiver.
- * @param clearing true if the new client generation value maps to a remote control update
- * where the display should be cleared.
- */
- void setCurrentClientId(int clientGeneration, in PendingIntent clientMediaIntent,
- boolean clearing);
-
- /**
- * Sets whether the controls of this display are enabled
- * @param if false, the display shouldn't any commands
- */
- void setEnabled(boolean enabled);
-
- /**
- * Sets the playback information (state, position and speed) of a client.
- * @param generationId the current generation ID as known by this client
- * @param state the current playback state, one of the following values:
- * {@link RemoteControlClient#PLAYSTATE_STOPPED},
- * {@link RemoteControlClient#PLAYSTATE_PAUSED},
- * {@link RemoteControlClient#PLAYSTATE_PLAYING},
- * {@link RemoteControlClient#PLAYSTATE_FAST_FORWARDING},
- * {@link RemoteControlClient#PLAYSTATE_REWINDING},
- * {@link RemoteControlClient#PLAYSTATE_SKIPPING_FORWARDS},
- * {@link RemoteControlClient#PLAYSTATE_SKIPPING_BACKWARDS},
- * {@link RemoteControlClient#PLAYSTATE_BUFFERING},
- * {@link RemoteControlClient#PLAYSTATE_ERROR}.
- * @param stateChangeTimeMs the time at which the client reported the playback information
- * @param currentPosMs a 0 or positive value for the current media position expressed in ms
- * Strictly negative values imply that position is not known:
- * a value of {@link RemoteControlClient#PLAYBACK_POSITION_INVALID} is intended to express
- * that an application doesn't know the position (e.g. listening to a live stream of a radio)
- * or that the position information is not applicable (e.g. when state
- * is {@link RemoteControlClient#PLAYSTATE_BUFFERING} and nothing had played yet);
- * a value of {@link RemoteControlClient#PLAYBACK_POSITION_ALWAYS_UNKNOWN} implies that the
- * application uses {@link RemoteControlClient#setPlaybackState(int)} (legacy API) and will
- * never pass a playback position.
- * @param speed a value expressed as a ratio of 1x playback: 1.0f is normal playback,
- * 2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is
- * playing (e.g. when state is {@link RemoteControlClient#PLAYSTATE_ERROR}).
- */
- void setPlaybackState(int generationId, int state, long stateChangeTimeMs, long currentPosMs,
- float speed);
-
- /**
- * Sets the transport control flags and playback position capabilities of a client.
- * @param generationId the current generation ID as known by this client
- * @param transportControlFlags bitmask of the transport controls this client supports, see
- * {@link RemoteControlClient#setTransportControlFlags(int)}
- * @param posCapabilities a bit mask for playback position capabilities, see
- * {@link RemoteControlClient#MEDIA_POSITION_READABLE} and
- * {@link RemoteControlClient#MEDIA_POSITION_WRITABLE}
- */
- void setTransportControlInfo(int generationId, int transportControlFlags, int posCapabilities);
-
- void setMetadata(int generationId, in Bundle metadata);
-
- void setArtwork(int generationId, in Bitmap artwork);
-
- /**
- * To combine metadata text and artwork in one binder call
- */
- void setAllMetadata(int generationId, in Bundle metadata, in Bitmap artwork);
-}
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index c9a86d8..6d32eff 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -349,16 +349,6 @@
*/
public RemoteControlClient(PendingIntent mediaButtonIntent) {
mRcMediaIntent = mediaButtonIntent;
-
- Looper looper;
- if ((looper = Looper.myLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else if ((looper = Looper.getMainLooper()) != null) {
- mEventHandler = new EventHandler(this, looper);
- } else {
- mEventHandler = null;
- Log.e(TAG, "RemoteControlClient() couldn't find main application thread");
- }
}
/**
@@ -378,8 +368,6 @@
*/
public RemoteControlClient(PendingIntent mediaButtonIntent, Looper looper) {
mRcMediaIntent = mediaButtonIntent;
-
- mEventHandler = new EventHandler(this, looper);
}
/**
@@ -707,39 +695,6 @@
}
}
- // TODO investigate if we still need position drift checking
- private void onPositionDriftCheck() {
- if (DEBUG) { Log.d(TAG, "onPositionDriftCheck()"); }
- synchronized(mCacheLock) {
- if ((mEventHandler == null) || (mPositionProvider == null) || !mNeedsPositionSync) {
- return;
- }
- if ((mPlaybackPositionMs < 0) || (mPlaybackSpeed == 0.0f)) {
- if (DEBUG) { Log.d(TAG, " no valid position or 0 speed, no check needed"); }
- return;
- }
- long estPos = mPlaybackPositionMs + (long)
- ((SystemClock.elapsedRealtime() - mPlaybackStateChangeTimeMs) / mPlaybackSpeed);
- long actPos = mPositionProvider.onGetPlaybackPosition();
- if (actPos >= 0) {
- if (Math.abs(estPos - actPos) > POSITION_DRIFT_MAX_MS) {
- // drift happened, report the new position
- if (DEBUG) { Log.w(TAG, " drift detected: actual=" +actPos +" est=" +estPos); }
- setPlaybackState(mPlaybackState, actPos, mPlaybackSpeed);
- } else {
- if (DEBUG) { Log.d(TAG, " no drift: actual=" + actPos +" est=" + estPos); }
- // no drift, schedule the next drift check
- mEventHandler.sendMessageDelayed(
- mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
- getCheckPeriodFromSpeed(mPlaybackSpeed));
- }
- } else {
- // invalid position (negative value), can't check for drift
- mEventHandler.removeMessages(MSG_POSITION_DRIFT_CHECK);
- }
- }
- }
-
/**
* Sets the flags for the media transport control buttons that this client supports.
* @param transportControlFlags A combination of the following flags:
@@ -856,14 +811,6 @@
public void setOnGetPlaybackPositionListener(OnGetPlaybackPositionListener l) {
synchronized(mCacheLock) {
mPositionProvider = l;
- if ((mPositionProvider != null) && (mEventHandler != null)
- && playbackPositionShouldMove(mPlaybackState)) {
- // playback position is already moving, but now we have a position provider,
- // so schedule a drift check right now
- mEventHandler.sendMessageDelayed(
- mEventHandler.obtainMessage(MSG_POSITION_DRIFT_CHECK),
- 0 /*check now*/);
- }
}
}
@@ -1001,26 +948,6 @@
}
};
- private EventHandler mEventHandler;
- private final static int MSG_POSITION_DRIFT_CHECK = 11;
-
- private class EventHandler extends Handler {
- public EventHandler(RemoteControlClient rcc, Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case MSG_POSITION_DRIFT_CHECK:
- onPositionDriftCheck();
- break;
- default:
- Log.e(TAG, "Unknown event " + msg.what + " in RemoteControlClient handler");
- }
- }
- }
-
//===========================================================
// Message handlers
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index d84cf30..90f2163 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -17,8 +17,6 @@
package android.media;
import android.app.ActivityManager;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -32,7 +30,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -61,15 +58,10 @@
@Deprecated public final class RemoteController
{
private final static int MAX_BITMAP_DIMENSION = 512;
- private final static int TRANSPORT_UNKNOWN = 0;
private final static String TAG = "RemoteController";
private final static boolean DEBUG = false;
- private final static boolean USE_SESSIONS = true;
- private final static Object mGenLock = new Object();
private final static Object mInfoLock = new Object();
- private final RcDisplay mRcd;
private final Context mContext;
- private final AudioManager mAudioManager;
private final int mMaxBitmapDimension;
private MetadataEditor mMetadataEditor;
@@ -78,15 +70,9 @@
private MediaController.Callback mSessionCb = new MediaControllerCallback();
/**
- * Synchronized on mGenLock
- */
- private int mClientGenerationIdCurrent = 0;
-
- /**
* Synchronized on mInfoLock
*/
private boolean mIsRegistered = false;
- private PendingIntent mClientPendingIntentCurrent;
private OnClientUpdateListener mOnClientUpdateListener;
private PlaybackInfo mLastPlaybackInfo;
private int mArtworkWidth = -1;
@@ -136,8 +122,6 @@
}
mOnClientUpdateListener = updateListener;
mContext = context;
- mRcd = new RcDisplay(this);
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mSessionManager = (MediaSessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
mSessionListener = new TopTransportSessionListener();
@@ -207,22 +191,6 @@
public void onClientMetadataUpdate(MetadataEditor metadataEditor);
};
-
- /**
- * @hide
- */
- public String getRemoteControlClientPackageName() {
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- return mCurrentSession != null ? mCurrentSession.getPackageName()
- : null;
- }
- } else {
- return mClientPendingIntentCurrent != null ?
- mClientPendingIntentCurrent.getCreatorPackage() : null;
- }
- }
-
/**
* Return the estimated playback position of the current media track or a negative value
* if not available.
@@ -240,38 +208,13 @@
* @see OnClientUpdateListener#onClientPlaybackStateUpdate(int, long, long, float)
*/
public long getEstimatedMediaPosition() {
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- if (mCurrentSession != null) {
- PlaybackState state = mCurrentSession.getPlaybackState();
- if (state != null) {
- return state.getPosition();
- }
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ PlaybackState state = mCurrentSession.getPlaybackState();
+ if (state != null) {
+ return state.getPosition();
}
}
- } else {
- final PlaybackInfo lastPlaybackInfo;
- synchronized (mInfoLock) {
- lastPlaybackInfo = mLastPlaybackInfo;
- }
- if (lastPlaybackInfo != null) {
- if (!RemoteControlClient.playbackPositionShouldMove(lastPlaybackInfo.mState)) {
- return lastPlaybackInfo.mCurrentPosMs;
- }
-
- // Take the current position at the time of state change and
- // estimate.
- final long thenPos = lastPlaybackInfo.mCurrentPosMs;
- if (thenPos < 0) {
- return -1;
- }
-
- final long now = SystemClock.elapsedRealtime();
- final long then = lastPlaybackInfo.mStateChangeTimeMs;
- final long sinceThen = now - then;
- final long scaledSinceThen = (long) (sinceThen * lastPlaybackInfo.mSpeed);
- return thenPos + scaledSinceThen;
- }
}
return -1;
}
@@ -308,42 +251,12 @@
if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
throw new IllegalArgumentException("not a media key event");
}
- if (USE_SESSIONS) {
- synchronized (mInfoLock) {
- if (mCurrentSession != null) {
- return mCurrentSession.dispatchMediaButtonEvent(keyEvent);
- }
- return false;
+ synchronized (mInfoLock) {
+ if (mCurrentSession != null) {
+ return mCurrentSession.dispatchMediaButtonEvent(keyEvent);
}
- } else {
- final PendingIntent pi;
- synchronized (mInfoLock) {
- if (!mIsRegistered) {
- Log.e(TAG,
- "Cannot use sendMediaKeyEvent() from an unregistered RemoteController");
- return false;
- }
- if (!mEnabled) {
- Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController");
- return false;
- }
- pi = mClientPendingIntentCurrent;
- }
- if (pi != null) {
- Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- try {
- pi.send(mContext, 0, intent);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending intent for media button down: ", e);
- return false;
- }
- } else {
- Log.i(TAG, "No-op when sending key click, no receiver right now");
- return false;
- }
+ return false;
}
- return true;
}
@@ -453,8 +366,7 @@
Log.e(TAG, "Cannot set synchronization mode on an unregistered RemoteController");
return false;
}
- mAudioManager.remoteControlDisplayWantsPlaybackPositionSync(mRcd,
- POSITION_SYNCHRONIZATION_CHECK == sync);
+ // deprecated, no-op
return true;
}
@@ -541,154 +453,6 @@
}
-
- //==================================================
- // Implementation of IRemoteControlDisplay interface
- private static class RcDisplay extends IRemoteControlDisplay.Stub {
- private final WeakReference<RemoteController> mController;
-
- RcDisplay(RemoteController rc) {
- mController = new WeakReference<RemoteController>(rc);
- }
-
- public void setCurrentClientId(int genId, PendingIntent clientMediaIntent,
- boolean clearing) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- boolean isNew = false;
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- rc.mClientGenerationIdCurrent = genId;
- isNew = true;
- }
- }
- if (clientMediaIntent != null) {
- sendMsg(rc.mEventHandler, MSG_NEW_PENDING_INTENT, SENDMSG_REPLACE,
- genId /*arg1*/, 0, clientMediaIntent /*obj*/, 0 /*delay*/);
- }
- if (isNew || clearing) {
- sendMsg(rc.mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
- genId /*arg1*/, clearing ? 1 : 0, null /*obj*/, 0 /*delay*/);
- }
- }
-
- public void setEnabled(boolean enabled) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- sendMsg(rc.mEventHandler, MSG_DISPLAY_ENABLE, SENDMSG_REPLACE,
- enabled ? 1 : 0 /*arg1*/, 0, null /*obj*/, 0 /*delay*/);
- }
-
- public void setPlaybackState(int genId, int state,
- long stateChangeTimeMs, long currentPosMs, float speed) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "> new playback state: genId="+genId
- + " state="+ state
- + " changeTime="+ stateChangeTimeMs
- + " pos=" + currentPosMs
- + "ms speed=" + speed);
- }
-
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- final PlaybackInfo playbackInfo =
- new PlaybackInfo(state, stateChangeTimeMs, currentPosMs, speed);
- sendMsg(rc.mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE,
- genId /*arg1*/, 0, playbackInfo /*obj*/, 0 /*delay*/);
-
- }
-
- public void setTransportControlInfo(int genId, int transportControlFlags,
- int posCapabilities) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- sendMsg(rc.mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE,
- genId /*arg1*/, transportControlFlags /*arg2*/,
- null /*obj*/, 0 /*delay*/);
- }
-
- public void setMetadata(int genId, Bundle metadata) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- if (DEBUG) { Log.e(TAG, "setMetadata("+genId+")"); }
- if (metadata == null) {
- return;
- }
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
- genId /*arg1*/, 0 /*arg2*/,
- metadata /*obj*/, 0 /*delay*/);
- }
-
- public void setArtwork(int genId, Bitmap artwork) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- if (DEBUG) { Log.v(TAG, "setArtwork("+genId+")"); }
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- Bundle metadata = new Bundle(1);
- metadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK), artwork);
- sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
- genId /*arg1*/, 0 /*arg2*/,
- metadata /*obj*/, 0 /*delay*/);
- }
-
- public void setAllMetadata(int genId, Bundle metadata, Bitmap artwork) {
- final RemoteController rc = mController.get();
- if (rc == null) {
- return;
- }
- if (DEBUG) { Log.e(TAG, "setAllMetadata("+genId+")"); }
- if ((metadata == null) && (artwork == null)) {
- return;
- }
- synchronized(mGenLock) {
- if (rc.mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- if (metadata == null) {
- metadata = new Bundle(1);
- }
- if (artwork != null) {
- metadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
- artwork);
- }
- sendMsg(rc.mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
- genId /*arg1*/, 0 /*arg2*/,
- metadata /*obj*/, 0 /*delay*/);
- }
- }
-
/**
* This receives updates when the current session changes. This is
* registered to receive the updates on the handler thread so it can call
@@ -734,14 +498,9 @@
//==================================================
// Event handling
private final EventHandler mEventHandler;
- private final static int MSG_NEW_PENDING_INTENT = 0;
- private final static int MSG_NEW_PLAYBACK_INFO = 1;
- private final static int MSG_NEW_TRANSPORT_INFO = 2;
- private final static int MSG_NEW_METADATA = 3; // msg always has non-null obj parameter
- private final static int MSG_CLIENT_CHANGE = 4;
- private final static int MSG_DISPLAY_ENABLE = 5;
- private final static int MSG_NEW_PLAYBACK_STATE = 6;
- private final static int MSG_NEW_MEDIA_METADATA = 7;
+ private final static int MSG_CLIENT_CHANGE = 0;
+ private final static int MSG_NEW_PLAYBACK_STATE = 1;
+ private final static int MSG_NEW_MEDIA_METADATA = 2;
private class EventHandler extends Handler {
@@ -752,26 +511,10 @@
@Override
public void handleMessage(Message msg) {
switch(msg.what) {
- case MSG_NEW_PENDING_INTENT:
- onNewPendingIntent(msg.arg1, (PendingIntent) msg.obj);
- break;
- case MSG_NEW_PLAYBACK_INFO:
- onNewPlaybackInfo(msg.arg1, (PlaybackInfo) msg.obj);
- break;
- case MSG_NEW_TRANSPORT_INFO:
- onNewTransportInfo(msg.arg1, msg.arg2);
- break;
- case MSG_NEW_METADATA:
- onNewMetadata(msg.arg1, (Bundle)msg.obj);
- break;
case MSG_CLIENT_CHANGE:
- onClientChange(msg.arg1, msg.arg2 == 1);
- break;
- case MSG_DISPLAY_ENABLE:
- onDisplayEnable(msg.arg1 == 1);
+ onClientChange(msg.arg2 == 1);
break;
case MSG_NEW_PLAYBACK_STATE:
- // same as new playback info but using new apis
onNewPlaybackState((PlaybackState) msg.obj);
break;
case MSG_NEW_MEDIA_METADATA:
@@ -835,100 +578,7 @@
handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delayMs);
}
- ///////////// These calls are used by the old APIs with RCC and RCD //////////////////////
- private void onNewPendingIntent(int genId, PendingIntent pi) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- synchronized(mInfoLock) {
- mClientPendingIntentCurrent = pi;
- }
- }
-
- private void onNewPlaybackInfo(int genId, PlaybackInfo pi) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- final OnClientUpdateListener l;
- synchronized(mInfoLock) {
- l = this.mOnClientUpdateListener;
- mLastPlaybackInfo = pi;
- }
- if (l != null) {
- if (pi.mCurrentPosMs == RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
- l.onClientPlaybackStateUpdate(pi.mState);
- } else {
- l.onClientPlaybackStateUpdate(pi.mState, pi.mStateChangeTimeMs, pi.mCurrentPosMs,
- pi.mSpeed);
- }
- }
- }
-
- private void onNewTransportInfo(int genId, int transportControlFlags) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- final OnClientUpdateListener l;
- synchronized(mInfoLock) {
- l = mOnClientUpdateListener;
- }
- if (l != null) {
- l.onClientTransportControlUpdate(transportControlFlags);
- }
- }
-
- /**
- * @param genId
- * @param metadata guaranteed to be always non-null
- */
- private void onNewMetadata(int genId, Bundle metadata) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
- final OnClientUpdateListener l;
- final MetadataEditor metadataEditor;
- // prepare the received Bundle to be used inside a MetadataEditor
- final long editableKeys = metadata.getLong(
- String.valueOf(MediaMetadataEditor.KEY_EDITABLE_MASK), 0);
- if (editableKeys != 0) {
- metadata.remove(String.valueOf(MediaMetadataEditor.KEY_EDITABLE_MASK));
- }
- synchronized(mInfoLock) {
- l = mOnClientUpdateListener;
- if ((mMetadataEditor != null) && (mMetadataEditor.mEditorMetadata != null)) {
- if (mMetadataEditor.mEditorMetadata != metadata) {
- // existing metadata, merge existing and new
- mMetadataEditor.mEditorMetadata.putAll(metadata);
- }
-
- mMetadataEditor.putBitmap(MediaMetadataEditor.BITMAP_KEY_ARTWORK,
- (Bitmap)metadata.getParcelable(
- String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK)));
- mMetadataEditor.cleanupBitmapFromBundle(MediaMetadataEditor.BITMAP_KEY_ARTWORK);
- } else {
- mMetadataEditor = new MetadataEditor(metadata, editableKeys);
- }
- metadataEditor = mMetadataEditor;
- }
- if (l != null) {
- l.onClientMetadataUpdate(metadataEditor);
- }
- }
-
- private void onClientChange(int genId, boolean clearing) {
- synchronized(mGenLock) {
- if (mClientGenerationIdCurrent != genId) {
- return;
- }
- }
+ private void onClientChange(boolean clearing) {
final OnClientUpdateListener l;
synchronized(mInfoLock) {
l = mOnClientUpdateListener;
@@ -939,39 +589,6 @@
}
}
- private void onDisplayEnable(boolean enabled) {
- final OnClientUpdateListener l;
- synchronized(mInfoLock) {
- mEnabled = enabled;
- l = this.mOnClientUpdateListener;
- }
- if (!enabled) {
- // when disabling, reset all info sent to the user
- final int genId;
- synchronized (mGenLock) {
- genId = mClientGenerationIdCurrent;
- }
- // send "stopped" state, happened "now", playback position is 0, speed 0.0f
- final PlaybackInfo pi = new PlaybackInfo(RemoteControlClient.PLAYSTATE_STOPPED,
- SystemClock.elapsedRealtime() /*stateChangeTimeMs*/,
- 0 /*currentPosMs*/, 0.0f /*speed*/);
- sendMsg(mEventHandler, MSG_NEW_PLAYBACK_INFO, SENDMSG_REPLACE,
- genId /*arg1*/, 0 /*arg2, ignored*/, pi /*obj*/, 0 /*delay*/);
- // send "blank" transport control info: no controls are supported
- sendMsg(mEventHandler, MSG_NEW_TRANSPORT_INFO, SENDMSG_REPLACE,
- genId /*arg1*/, 0 /*arg2, no flags*/,
- null /*obj, ignored*/, 0 /*delay*/);
- // send dummy metadata with empty string for title and artist, duration of 0
- Bundle metadata = new Bundle(3);
- metadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_TITLE), "");
- metadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ARTIST), "");
- metadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DURATION), 0);
- sendMsg(mEventHandler, MSG_NEW_METADATA, SENDMSG_QUEUE,
- genId /*arg1*/, 0 /*arg2, ignored*/, metadata /*obj*/, 0 /*delay*/);
- }
- }
-
- ///////////// These calls are used by the new APIs with Sessions //////////////////////
private void updateController(MediaController controller) {
if (DEBUG) {
Log.d(TAG, "Updating controller to " + controller + " previous controller is "
@@ -983,7 +600,7 @@
mCurrentSession.unregisterCallback(mSessionCb);
mCurrentSession = null;
sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
- 0 /* genId */, 1 /* clearing */, null /* obj */, 0 /* delay */);
+ 0 /* arg1 ignored */, 1 /* clearing */, null /* obj */, 0 /* delay */);
}
} else if (mCurrentSession == null
|| !controller.getSessionToken()
@@ -992,17 +609,17 @@
mCurrentSession.unregisterCallback(mSessionCb);
}
sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
- 0 /* genId */, 0 /* clearing */, null /* obj */, 0 /* delay */);
+ 0 /* arg1 ignored */, 0 /* clearing */, null /* obj */, 0 /* delay */);
mCurrentSession = controller;
mCurrentSession.registerCallback(mSessionCb, mEventHandler);
PlaybackState state = controller.getPlaybackState();
sendMsg(mEventHandler, MSG_NEW_PLAYBACK_STATE, SENDMSG_REPLACE,
- 0 /* genId */, 0, state /* obj */, 0 /* delay */);
+ 0 /* arg1 ignored */, 0 /* arg2 ignored */, state /* obj */, 0 /* delay */);
MediaMetadata metadata = controller.getMetadata();
sendMsg(mEventHandler, MSG_NEW_MEDIA_METADATA, SENDMSG_REPLACE,
- 0 /* arg1 */, 0 /* arg2 */, metadata /* obj */, 0 /* delay */);
+ 0 /* arg1 ignored */, 0 /* arg2 ignored*/, metadata /* obj */, 0 /*delay*/);
}
// else same controller, no need to update
}
@@ -1069,38 +686,6 @@
/**
* @hide
- * Used by AudioManager to mark this instance as registered.
- * @param registered
- */
- void setIsRegistered(boolean registered) {
- synchronized (mInfoLock) {
- mIsRegistered = registered;
- }
- }
-
- /**
- * @hide
- * Used by AudioManager to access binder to be registered/unregistered inside MediaFocusControl
- * @return
- */
- RcDisplay getRcDisplay() {
- return mRcd;
- }
-
- /**
- * @hide
- * Used by AudioManager to read the current artwork dimension
- * @return array containing width (index 0) and height (index 1) of currently set artwork size
- */
- int[] getArtworkSize() {
- synchronized (mInfoLock) {
- int[] size = { mArtworkWidth, mArtworkHeight };
- return size;
- }
- }
-
- /**
- * @hide
* Used by AudioManager to access user listener receiving the client update notifications
* @return
*/
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 75886aa..4f2f486 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -62,7 +62,6 @@
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
-import android.media.IRemoteControlDisplay;
import android.media.IRingtonePlayer;
import android.media.IVolumeController;
import android.media.MediaPlayer;
@@ -667,8 +666,7 @@
mSettingsObserver = new SettingsObserver();
createStreamStates();
- mMediaFocusControl = new MediaFocusControl(mAudioHandler.getLooper(),
- mContext, mVolumeController, this);
+ mMediaFocusControl = new MediaFocusControl(mContext);
readAndSetLowRamDevice();
@@ -5235,36 +5233,6 @@
}
}
- //==========================================================================================
- // RemoteControlDisplay / RemoteControlClient / Remote info
- //==========================================================================================
- public boolean registerRemoteController(IRemoteControlDisplay rcd, int w, int h,
- ComponentName listenerComp) {
- return mMediaFocusControl.registerRemoteController(rcd, w, h, listenerComp);
- }
-
- public boolean registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
- return mMediaFocusControl.registerRemoteControlDisplay(rcd, w, h);
- }
-
- public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
- mMediaFocusControl.unregisterRemoteControlDisplay(rcd);
- }
-
- public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
- mMediaFocusControl.remoteControlDisplayUsesBitmapSize(rcd, w, h);
- }
-
- public void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
- boolean wantsSync) {
- mMediaFocusControl.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
- }
-
- @Override
- public void setRemoteStreamVolume(int index) {
- enforceVolumeController("set the remote stream volume");
- mMediaFocusControl.setRemoteStreamVolume(index);
- }
//==========================================================================================
// Audio Focus
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index f72b598..d44d89d 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -16,52 +16,18 @@
package com.android.server.audio;
-import android.app.Activity;
-import android.app.ActivityManager;
import android.app.AppOpsManager;
-import android.app.KeyguardManager;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.app.PendingIntent.OnFinished;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
import android.media.AudioAttributes;
import android.media.AudioFocusInfo;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.IAudioFocusDispatcher;
-import android.media.IRemoteControlClient;
-import android.media.IRemoteControlDisplay;
-import android.media.IRemoteVolumeObserver;
-import android.media.RemoteControlClient;
import android.media.audiopolicy.IAudioPolicyCallback;
-import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.IDeviceIdleController;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.speech.RecognizerIntent;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
import android.util.Log;
-import android.util.Slog;
-import android.view.KeyEvent;
-
-import com.android.server.audio.PlayerRecord.RemotePlaybackState;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -74,329 +40,22 @@
* @hide
*
*/
-public class MediaFocusControl implements OnFinished {
+public class MediaFocusControl {
private static final String TAG = "MediaFocusControl";
- /** Debug remote control client/display feature */
- protected static final boolean DEBUG_RC = false;
- /** Debug volumes */
- protected static final boolean DEBUG_VOL = false;
-
- /** Used to alter media button redirection when the phone is ringing. */
- private boolean mIsRinging = false;
-
- private final PowerManager.WakeLock mMediaEventWakeLock;
- private final MediaEventHandler mEventHandler;
private final Context mContext;
- private final ContentResolver mContentResolver;
- private final AudioService.VolumeController mVolumeController;
private final AppOpsManager mAppOps;
- private final KeyguardManager mKeyguardManager;
- private final AudioService mAudioService;
- private final NotificationListenerObserver mNotifListenerObserver;
- protected MediaFocusControl(Looper looper, Context cntxt,
- AudioService.VolumeController volumeCtrl, AudioService as) {
- mEventHandler = new MediaEventHandler(looper);
+ protected MediaFocusControl(Context cntxt) {
mContext = cntxt;
- mContentResolver = mContext.getContentResolver();
- mVolumeController = volumeCtrl;
- mAudioService = as;
-
- PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
- int maxMusicLevel = as.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- mMainRemote = new RemotePlaybackState(-1, maxMusicLevel, maxMusicLevel);
-
- // Register for phone state monitoring
- TelephonyManager tmgr = (TelephonyManager)
- mContext.getSystemService(Context.TELEPHONY_SERVICE);
- tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
-
mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
- mKeyguardManager =
- (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mNotifListenerObserver = new NotificationListenerObserver();
-
- mHasRemotePlayback = false;
- mMainRemoteIsActive = false;
-
- PlayerRecord.setMediaFocusControl(this);
-
- postReevaluateRemote();
}
protected void dump(PrintWriter pw) {
pw.println("\nMediaFocusControl dump time: "
+ DateFormat.getTimeInstance().format(new Date()));
dumpFocusStack(pw);
- dumpRCStack(pw);
- dumpRCCStack(pw);
- dumpRCDList(pw);
- }
-
- //==========================================================================================
- // Management of RemoteControlDisplay registration permissions
- //==========================================================================================
- private final static Uri ENABLED_NOTIFICATION_LISTENERS_URI =
- Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
-
- private class NotificationListenerObserver extends ContentObserver {
-
- NotificationListenerObserver() {
- super(mEventHandler);
- mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS), false, this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (!ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri) || selfChange) {
- return;
- }
- if (DEBUG_RC) { Log.d(TAG, "NotificationListenerObserver.onChange()"); }
- postReevaluateRemoteControlDisplays();
- }
- }
-
- private final static int RCD_REG_FAILURE = 0;
- private final static int RCD_REG_SUCCESS_PERMISSION = 1;
- private final static int RCD_REG_SUCCESS_ENABLED_NOTIF = 2;
-
- /**
- * Checks a caller's authorization to register an IRemoteControlDisplay.
- * Authorization is granted if one of the following is true:
- * <ul>
- * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL permission</li>
- * <li>the caller's listener is one of the enabled notification listeners</li>
- * </ul>
- * @return RCD_REG_FAILURE if it's not safe to proceed with the IRemoteControlDisplay
- * registration.
- */
- private int checkRcdRegistrationAuthorization(ComponentName listenerComp) {
- // MEDIA_CONTENT_CONTROL permission check
- if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MEDIA_CONTENT_CONTROL)) {
- if (DEBUG_RC) { Log.d(TAG, "ok to register Rcd: has MEDIA_CONTENT_CONTROL permission");}
- return RCD_REG_SUCCESS_PERMISSION;
- }
-
- // ENABLED_NOTIFICATION_LISTENERS settings check
- if (listenerComp != null) {
- // this call is coming from an app, can't use its identity to read secure settings
- final long ident = Binder.clearCallingIdentity();
- try {
- final int currentUser = ActivityManager.getCurrentUser();
- final String enabledNotifListeners = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- currentUser);
- if (enabledNotifListeners != null) {
- final String[] components = enabledNotifListeners.split(":");
- for (int i=0; i<components.length; i++) {
- final ComponentName component =
- ComponentName.unflattenFromString(components[i]);
- if (component != null) {
- if (listenerComp.equals(component)) {
- if (DEBUG_RC) { Log.d(TAG, "ok to register RCC: " + component +
- " is authorized notification listener"); }
- return RCD_REG_SUCCESS_ENABLED_NOTIF;
- }
- }
- }
- }
- if (DEBUG_RC) { Log.d(TAG, "not ok to register RCD, " + listenerComp +
- " is not in list of ENABLED_NOTIFICATION_LISTENERS"); }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- return RCD_REG_FAILURE;
- }
-
- protected boolean registerRemoteController(IRemoteControlDisplay rcd, int w, int h,
- ComponentName listenerComp) {
- int reg = checkRcdRegistrationAuthorization(listenerComp);
- if (reg != RCD_REG_FAILURE) {
- registerRemoteControlDisplay_int(rcd, w, h, listenerComp);
- return true;
- } else {
- Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() +
- ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL +
- " or be an enabled NotificationListenerService for registerRemoteController");
- return false;
- }
- }
-
- protected boolean registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
- int reg = checkRcdRegistrationAuthorization(null);
- if (reg != RCD_REG_FAILURE) {
- registerRemoteControlDisplay_int(rcd, w, h, null);
- return true;
- } else {
- Slog.w(TAG, "Access denied to process: " + Binder.getCallingPid() +
- ", must have permission " + android.Manifest.permission.MEDIA_CONTENT_CONTROL +
- " to register IRemoteControlDisplay");
- return false;
- }
- }
-
- private void postReevaluateRemoteControlDisplays() {
- sendMsg(mEventHandler, MSG_REEVALUATE_RCD, SENDMSG_QUEUE, 0, 0, null, 0);
- }
-
- private void onReevaluateRemoteControlDisplays() {
- if (DEBUG_RC) { Log.d(TAG, "onReevaluateRemoteControlDisplays()"); }
- // read which components are enabled notification listeners
- final int currentUser = ActivityManager.getCurrentUser();
- final String enabledNotifListeners = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
- currentUser);
- if (DEBUG_RC) { Log.d(TAG, " > enabled list: " + enabledNotifListeners); }
- synchronized(mAudioFocusLock) {
- synchronized(mPRStack) {
- // check whether the "enable" status of each RCD with a notification listener
- // has changed
- final String[] enabledComponents;
- if (enabledNotifListeners == null) {
- enabledComponents = null;
- } else {
- enabledComponents = enabledNotifListeners.split(":");
- }
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di =
- displayIterator.next();
- if (di.mClientNotifListComp != null) {
- boolean wasEnabled = di.mEnabled;
- di.mEnabled = isComponentInStringArray(di.mClientNotifListComp,
- enabledComponents);
- if (wasEnabled != di.mEnabled){
- try {
- // tell the RCD whether it's enabled
- di.mRcDisplay.setEnabled(di.mEnabled);
- // tell the RCCs about the change for this RCD
- enableRemoteControlDisplayForClient_syncRcStack(
- di.mRcDisplay, di.mEnabled);
- // when enabling, refresh the information on the display
- if (di.mEnabled) {
- sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE,
- di.mArtworkExpectedWidth /*arg1*/,
- di.mArtworkExpectedHeight/*arg2*/,
- di.mRcDisplay /*obj*/, 0/*delay*/);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error en/disabling RCD: ", e);
- }
- }
- }
- }
- }
- }
- }
-
- /**
- * @param comp a non-null ComponentName
- * @param enabledArray may be null
- * @return
- */
- private boolean isComponentInStringArray(ComponentName comp, String[] enabledArray) {
- if (enabledArray == null || enabledArray.length == 0) {
- if (DEBUG_RC) { Log.d(TAG, " > " + comp + " is NOT enabled"); }
- return false;
- }
- final String compString = comp.flattenToString();
- for (int i=0; i<enabledArray.length; i++) {
- if (compString.equals(enabledArray[i])) {
- if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is enabled"); }
- return true;
- }
- }
- if (DEBUG_RC) { Log.d(TAG, " > " + compString + " is NOT enabled"); }
- return false;
- }
-
- //==========================================================================================
- // Internal event handling
- //==========================================================================================
-
- // event handler messages
- private static final int MSG_RCDISPLAY_CLEAR = 1;
- private static final int MSG_RCDISPLAY_UPDATE = 2;
- private static final int MSG_REEVALUATE_REMOTE = 3;
- private static final int MSG_RCC_NEW_PLAYBACK_INFO = 4;
- private static final int MSG_RCC_NEW_VOLUME_OBS = 5;
- private static final int MSG_RCC_NEW_PLAYBACK_STATE = 6;
- private static final int MSG_RCC_SEEK_REQUEST = 7;
- private static final int MSG_RCC_UPDATE_METADATA = 8;
- private static final int MSG_RCDISPLAY_INIT_INFO = 9;
- private static final int MSG_REEVALUATE_RCD = 10;
- private static final int MSG_UNREGISTER_MEDIABUTTONINTENT = 11;
-
- // sendMsg() flags
- /** If the msg is already queued, replace it with this one. */
- private static final int SENDMSG_REPLACE = 0;
- /** If the msg is already queued, ignore this one and leave the old. */
- private static final int SENDMSG_NOOP = 1;
- /** If the msg is already queued, queue this one and leave the old. */
- private static final int SENDMSG_QUEUE = 2;
-
- private static void sendMsg(Handler handler, int msg,
- int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
-
- if (existingMsgPolicy == SENDMSG_REPLACE) {
- handler.removeMessages(msg);
- } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
- return;
- }
-
- handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
- }
-
- private class MediaEventHandler extends Handler {
- MediaEventHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case MSG_RCDISPLAY_CLEAR:
- onRcDisplayClear();
- break;
-
- case MSG_RCDISPLAY_UPDATE:
- // msg.obj is guaranteed to be non null
- onRcDisplayUpdate( (PlayerRecord) msg.obj, msg.arg1);
- break;
-
- case MSG_REEVALUATE_REMOTE:
- onReevaluateRemote();
- break;
-
- case MSG_RCC_NEW_VOLUME_OBS:
- onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
- (IRemoteVolumeObserver)msg.obj /* rvo */);
- break;
-
- case MSG_RCDISPLAY_INIT_INFO:
- // msg.obj is guaranteed to be non null
- onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
- msg.arg1/*w*/, msg.arg2/*h*/);
- break;
-
- case MSG_REEVALUATE_RCD:
- onReevaluateRemoteControlDisplays();
- break;
-
- case MSG_UNREGISTER_MEDIABUTTONINTENT:
- unregisterMediaButtonIntent( (PendingIntent) msg.obj );
- break;
- }
- }
}
@@ -406,25 +65,6 @@
private final static Object mAudioFocusLock = new Object();
- private final static Object mRingingLock = new Object();
-
- private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (state == TelephonyManager.CALL_STATE_RINGING) {
- //Log.v(TAG, " CALL_STATE_RINGING");
- synchronized(mRingingLock) {
- mIsRinging = true;
- }
- } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
- || (state == TelephonyManager.CALL_STATE_IDLE)) {
- synchronized(mRingingLock) {
- mIsRinging = false;
- }
- }
- }
- };
-
/**
* Discard the current audio focus owner.
* Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
@@ -865,1374 +505,4 @@
}
}
-
- //==========================================================================================
- // RemoteControl
- //==========================================================================================
- /**
- * No-op if the key code for keyEvent is not a valid media key
- * (see {@link #isValidMediaKeyEvent(KeyEvent)})
- * @param keyEvent the key event to send
- */
- protected void dispatchMediaKeyEvent(KeyEvent keyEvent) {
- filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
- }
-
- /**
- * No-op if the key code for keyEvent is not a valid media key
- * (see {@link #isValidMediaKeyEvent(KeyEvent)})
- * @param keyEvent the key event to send
- */
- protected void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
- filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
- }
-
- private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
- // sanity check on the incoming key event
- if (!isValidMediaKeyEvent(keyEvent)) {
- Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
- return;
- }
- // event filtering for telephony
- synchronized(mRingingLock) {
- synchronized(mPRStack) {
- if ((mMediaReceiverForCalls != null) &&
- (mIsRinging || (mAudioService.getMode() == AudioSystem.MODE_IN_CALL))) {
- dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
- return;
- }
- }
- }
- // event filtering based on voice-based interactions
- if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
- filterVoiceInputKeyEvent(keyEvent, needWakeLock);
- } else {
- dispatchMediaKeyEvent(keyEvent, needWakeLock);
- }
- }
-
- /**
- * Handles the dispatching of the media button events to the telephony package.
- * Precondition: mMediaReceiverForCalls != null
- * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
- * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
- * is dispatched.
- */
- private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
- Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
- keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
- null, mKeyEventDone, mEventHandler, Activity.RESULT_OK, null, null);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /**
- * Handles the dispatching of the media button events to one of the registered listeners,
- * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
- * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
- * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
- * is dispatched.
- */
- private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- }
- Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
- keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- synchronized(mPRStack) {
- if (!mPRStack.empty()) {
- // send the intent that was registered by the client
- try {
- mPRStack.peek().getMediaButtonIntent().send(mContext,
- needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
- keyIntent, this, mEventHandler);
- } catch (CanceledException e) {
- Log.e(TAG, "Error sending pending intent " + mPRStack.peek());
- e.printStackTrace();
- }
- } else {
- // legacy behavior when nobody registered their media button event receiver
- // through AudioManager
- if (needWakeLock) {
- keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
- null, mKeyEventDone,
- mEventHandler, Activity.RESULT_OK, null, null);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
- }
-
- /**
- * The different actions performed in response to a voice button key event.
- */
- private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
- private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
- private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
-
- private final Object mVoiceEventLock = new Object();
- private boolean mVoiceButtonDown;
- private boolean mVoiceButtonHandled;
-
- /**
- * Filter key events that may be used for voice-based interactions
- * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
- * media buttons that can be used to trigger voice-based interactions.
- * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
- * is dispatched.
- */
- private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
- if (DEBUG_RC) {
- Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
- }
-
- int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
- int keyAction = keyEvent.getAction();
- synchronized (mVoiceEventLock) {
- if (keyAction == KeyEvent.ACTION_DOWN) {
- if (keyEvent.getRepeatCount() == 0) {
- // initial down
- mVoiceButtonDown = true;
- mVoiceButtonHandled = false;
- } else if (mVoiceButtonDown && !mVoiceButtonHandled
- && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
- // long-press, start voice-based interactions
- mVoiceButtonHandled = true;
- voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
- }
- } else if (keyAction == KeyEvent.ACTION_UP) {
- if (mVoiceButtonDown) {
- // voice button up
- mVoiceButtonDown = false;
- if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
- voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
- }
- }
- }
- }//synchronized (mVoiceEventLock)
-
- // take action after media button event filtering for voice-based interactions
- switch (voiceButtonAction) {
- case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
- if (DEBUG_RC) Log.v(TAG, " ignore key event");
- break;
- case VOICEBUTTON_ACTION_START_VOICE_INPUT:
- if (DEBUG_RC) Log.v(TAG, " start voice-based interactions");
- // then start the voice-based interactions
- startVoiceBasedInteractions(needWakeLock);
- break;
- case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
- if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock);
- sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
- break;
- }
- }
-
- private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
- // send DOWN event
- KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
- dispatchMediaKeyEvent(keyEvent, needWakeLock);
- // send UP event
- keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
- dispatchMediaKeyEvent(keyEvent, needWakeLock);
-
- }
-
- private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
- if (keyEvent == null) {
- return false;
- }
- return KeyEvent.isMediaKey(keyEvent.getKeyCode());
- }
-
- /**
- * Checks whether the given key code is one that can trigger the launch of voice-based
- * interactions.
- * @param keyCode the key code associated with the key event
- * @return true if the key is one of the supported voice-based interaction triggers
- */
- private static boolean isValidVoiceInputKeyCode(int keyCode) {
- if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Tell the system to start voice-based interactions / voice commands
- */
- private void startVoiceBasedInteractions(boolean needWakeLock) {
- Intent voiceIntent = null;
- // select which type of search to launch:
- // - screen on and device unlocked: action is ACTION_WEB_SEARCH
- // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
- // with EXTRA_SECURE set to true if the device is securely locked
- PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
- boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
- if (!isLocked && pm.isScreenOn()) {
- voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
- Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
- } else {
- IDeviceIdleController dic = IDeviceIdleController.Stub.asInterface(
- ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- if (dic != null) {
- try {
- dic.exitIdle("voice-search");
- } catch (RemoteException e) {
- }
- }
- voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
- voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
- isLocked && mKeyguardManager.isKeyguardSecure());
- Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
- }
- // start the search activity
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- }
- final long identity = Binder.clearCallingIdentity();
- try {
- if (voiceIntent != null) {
- voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- mContext.startActivityAsUser(voiceIntent, UserHandle.CURRENT);
- }
- } catch (ActivityNotFoundException e) {
- Log.w(TAG, "No activity for search: " + e);
- } finally {
- Binder.restoreCallingIdentity(identity);
- if (needWakeLock) {
- mMediaEventWakeLock.release();
- }
- }
- }
-
- private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
-
- // only set when wakelock was acquired, no need to check value when received
- private static final String EXTRA_WAKELOCK_ACQUIRED =
- "android.media.AudioService.WAKELOCK_ACQUIRED";
-
- public void onSendFinished(PendingIntent pendingIntent, Intent intent,
- int resultCode, String resultData, Bundle resultExtras) {
- if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
- mMediaEventWakeLock.release();
- }
- }
-
- BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- if (intent == null) {
- return;
- }
- Bundle extras = intent.getExtras();
- if (extras == null) {
- return;
- }
- if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
- mMediaEventWakeLock.release();
- }
- }
- };
-
- /**
- * Synchronization on mCurrentRcLock always inside a block synchronized on mPRStack
- */
- private final Object mCurrentRcLock = new Object();
- /**
- * The one remote control client which will receive a request for display information.
- * This object may be null.
- * Access protected by mCurrentRcLock.
- */
- private IRemoteControlClient mCurrentRcClient = null;
- /**
- * The PendingIntent associated with mCurrentRcClient. Its value is irrelevant
- * if mCurrentRcClient is null
- */
- private PendingIntent mCurrentRcClientIntent = null;
-
- private final static int RC_INFO_NONE = 0;
- private final static int RC_INFO_ALL =
- RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
- RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
- RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
- RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
-
- /**
- * A monotonically increasing generation counter for mCurrentRcClient.
- * Only accessed with a lock on mCurrentRcLock.
- * No value wrap-around issues as we only act on equal values.
- */
- private int mCurrentRcClientGen = 0;
-
-
- /**
- * Internal cache for the playback information of the RemoteControlClient whose volume gets to
- * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
- * every time we need this info.
- */
- private RemotePlaybackState mMainRemote;
- /**
- * Indicates whether the "main" RemoteControlClient is considered active.
- * Use synchronized on mMainRemote.
- */
- private boolean mMainRemoteIsActive;
- /**
- * Indicates whether there is remote playback going on. True even if there is no "active"
- * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
- * handles remote playback.
- * Use synchronized on mMainRemote.
- */
- private boolean mHasRemotePlayback;
-
- /**
- * The stack of remote control event receivers.
- * All read and write operations on mPRStack are synchronized.
- */
- private final Stack<PlayerRecord> mPRStack = new Stack<PlayerRecord>();
-
- /**
- * The component the telephony package can register so telephony calls have priority to
- * handle media button events
- */
- private ComponentName mMediaReceiverForCalls = null;
-
- /**
- * Helper function:
- * Display in the log the current entries in the remote control focus stack
- */
- private void dumpRCStack(PrintWriter pw) {
- pw.println("\nRemote Control stack entries (last is top of stack):");
- synchronized(mPRStack) {
- Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- stackIterator.next().dump(pw, true);
- }
- }
- }
-
- /**
- * Helper function:
- * Display in the log the current entries in the remote control stack, focusing
- * on RemoteControlClient data
- */
- private void dumpRCCStack(PrintWriter pw) {
- pw.println("\nRemote Control Client stack entries (last is top of stack):");
- synchronized(mPRStack) {
- Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- stackIterator.next().dump(pw, false);
- }
- synchronized(mCurrentRcLock) {
- pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
- }
- }
- synchronized (mMainRemote) {
- pw.println("\nRemote Volume State:");
- pw.println(" has remote: " + mHasRemotePlayback);
- pw.println(" is remote active: " + mMainRemoteIsActive);
- pw.println(" rccId: " + mMainRemote.mRccId);
- pw.println(" volume handling: "
- + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
- "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
- pw.println(" volume: " + mMainRemote.mVolume);
- pw.println(" volume steps: " + mMainRemote.mVolumeMax);
- }
- }
-
- /**
- * Helper function:
- * Display in the log the current entries in the list of remote control displays
- */
- private void dumpRCDList(PrintWriter pw) {
- pw.println("\nRemote Control Display list entries:");
- synchronized(mPRStack) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- pw.println(" IRCD: " + di.mRcDisplay +
- " -- w:" + di.mArtworkExpectedWidth +
- " -- h:" + di.mArtworkExpectedHeight +
- " -- wantsPosSync:" + di.mWantsPositionSync +
- " -- " + (di.mEnabled ? "enabled" : "disabled"));
- }
- }
- }
-
- /**
- * Helper function:
- * Push the new media button receiver "near" the top of the PlayerRecord stack.
- * "Near the top" is defined as:
- * - at the top if the current PlayerRecord at the top is not playing
- * - below the entries at the top of the stack that correspond to the playing PlayerRecord
- * otherwise
- * Called synchronized on mPRStack
- * precondition: mediaIntent != null
- * @return true if the top of mPRStack was changed, false otherwise
- */
- private boolean pushMediaButtonReceiver_syncPrs(PendingIntent mediaIntent,
- ComponentName target, IBinder token) {
- if (mPRStack.empty()) {
- mPRStack.push(new PlayerRecord(mediaIntent, target, token));
- return true;
- } else if (mPRStack.peek().hasMatchingMediaButtonIntent(mediaIntent)) {
- // already at top of stack
- return false;
- }
- if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(),
- mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) {
- return false;
- }
- PlayerRecord oldTopPrse = mPRStack.lastElement(); // top of the stack before any changes
- boolean topChanged = false;
- PlayerRecord prse = null;
- int lastPlayingIndex = mPRStack.size();
- int inStackIndex = -1;
- try {
- // go through the stack from the top to figure out who's playing, and the position
- // of this media button receiver (note that it may not be in the stack)
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- prse = mPRStack.elementAt(index);
- if (prse.isPlaybackActive()) {
- lastPlayingIndex = index;
- }
- if (prse.hasMatchingMediaButtonIntent(mediaIntent)) {
- inStackIndex = index;
- }
- }
-
- if (inStackIndex == -1) {
- // is not in stack
- prse = new PlayerRecord(mediaIntent, target, token);
- // it's new so it's not playing (no RemoteControlClient to give a playstate),
- // therefore it goes after the ones with active playback
- mPRStack.add(lastPlayingIndex, prse);
- } else {
- // is in the stack
- if (mPRStack.size() > 1) { // no need to remove and add if stack contains only 1
- prse = mPRStack.elementAt(inStackIndex);
- // remove it from its old location in the stack
- mPRStack.removeElementAt(inStackIndex);
- if (prse.isPlaybackActive()) {
- // and put it at the top
- mPRStack.push(prse);
- } else {
- // and put it after the ones with active playback
- if (inStackIndex > lastPlayingIndex) {
- mPRStack.add(lastPlayingIndex, prse);
- } else {
- mPRStack.add(lastPlayingIndex - 1, prse);
- }
- }
- }
- }
-
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification or bad index
- Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
- + " size=" + mPRStack.size()
- + " accessing media button stack", e);
- }
-
- return (topChanged);
- }
-
- /**
- * Helper function:
- * Remove the remote control receiver from the RC focus stack.
- * Called synchronized on mPRStack
- * precondition: pi != null
- */
- private void removeMediaButtonReceiver_syncPrs(PendingIntent pi) {
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if (prse.hasMatchingMediaButtonIntent(pi)) {
- prse.destroy();
- // ok to remove element while traversing the stack since we're leaving the loop
- mPRStack.removeElementAt(index);
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
-
- /**
- * Helper function:
- * Called synchronized on mPRStack
- */
- private boolean isCurrentRcController(PendingIntent pi) {
- if (!mPRStack.empty() && mPRStack.peek().hasMatchingMediaButtonIntent(pi)) {
- return true;
- }
- return false;
- }
-
- //==========================================================================================
- // Remote control display / client
- //==========================================================================================
- /**
- * Update the remote control displays with the new "focused" client generation
- */
- private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
- PendingIntent newMediaIntent, boolean clearing) {
- synchronized(mPRStack) {
- if (mRcDisplays.size() > 0) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- try {
- di.mRcDisplay.setCurrentClientId(
- newClientGeneration, newMediaIntent, clearing);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
- di.release();
- displayIterator.remove();
- }
- }
- }
- }
- }
-
- /**
- * Update the remote control clients with the new "focused" client generation
- */
- private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
- // (using an iterator on the stack so we can safely remove an entry if needed,
- // traversal order doesn't matter here as we update all entries)
- Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- PlayerRecord se = stackIterator.next();
- if ((se != null) && (se.getRcc() != null)) {
- try {
- se.getRcc().setCurrentClientGenerationId(newClientGeneration);
- } catch (RemoteException e) {
- Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
- stackIterator.remove();
- se.unlinkToRcClientDeath();
- }
- }
- }
- }
-
- /**
- * Update the displays and clients with the new "focused" client generation and name
- * @param newClientGeneration the new generation value matching a client update
- * @param newMediaIntent the media button event receiver associated with the client.
- * May be null, which implies there is no registered media button event receiver.
- * @param clearing true if the new client generation value maps to a remote control update
- * where the display should be cleared.
- */
- private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
- PendingIntent newMediaIntent, boolean clearing) {
- // send the new valid client generation ID to all displays
- setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
- // send the new valid client generation ID to all clients
- setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
- }
-
- /**
- * Called when processing MSG_RCDISPLAY_CLEAR event
- */
- private void onRcDisplayClear() {
- if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
-
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- mCurrentRcClientGen++;
- // synchronously update the displays and clients with the new client generation
- setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
- null /*newMediaIntent*/, true /*clearing*/);
- }
- }
- }
-
- /**
- * Called when processing MSG_RCDISPLAY_UPDATE event
- */
- private void onRcDisplayUpdate(PlayerRecord prse, int flags /* USED ?*/) {
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(prse.getRcc()))) {
- if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
-
- mCurrentRcClientGen++;
- // synchronously update the displays and clients with
- // the new client generation
- setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
- prse.getMediaButtonIntent() /*newMediaIntent*/,
- false /*clearing*/);
-
- // tell the current client that it needs to send info
- try {
- //TODO change name to informationRequestForAllDisplays()
- mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
- } catch (RemoteException e) {
- Log.e(TAG, "Current valid remote client is dead: "+e);
- mCurrentRcClient = null;
- }
- } else {
- // the remote control display owner has changed between the
- // the message to update the display was sent, and the time it
- // gets to be processed (now)
- }
- }
- }
- }
-
- /**
- * Called when processing MSG_RCDISPLAY_INIT_INFO event
- * Causes the current RemoteControlClient to send its info (metadata, playstate...) to
- * a single RemoteControlDisplay, NOT all of them, as with MSG_RCDISPLAY_UPDATE.
- */
- private void onRcDisplayInitInfo(IRemoteControlDisplay newRcd, int w, int h) {
- synchronized(mPRStack) {
- synchronized(mCurrentRcLock) {
- if (mCurrentRcClient != null) {
- if (DEBUG_RC) { Log.i(TAG, "Init RCD with current info"); }
- try {
- // synchronously update the new RCD with the current client generation
- // and matching PendingIntent
- newRcd.setCurrentClientId(mCurrentRcClientGen, mCurrentRcClientIntent,
- false);
-
- // tell the current RCC that it needs to send info, but only to the new RCD
- try {
- mCurrentRcClient.informationRequestForDisplay(newRcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Current valid remote client is dead: ", e);
- mCurrentRcClient = null;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Dead display in onRcDisplayInitInfo()", e);
- }
- }
- }
- }
- }
-
- /**
- * Helper function:
- * Called synchronized on mPRStack
- */
- private void clearRemoteControlDisplay_syncPrs() {
- synchronized(mCurrentRcLock) {
- mCurrentRcClient = null;
- }
- // will cause onRcDisplayClear() to be called in AudioService's handler thread
- mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
- }
-
- /**
- * Helper function for code readability: only to be called from
- * checkUpdateRemoteControlDisplay_syncPrs() which checks the preconditions for
- * this method.
- * Preconditions:
- * - called synchronized on mPRStack
- * - mPRStack.isEmpty() is false
- */
- private void updateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
- PlayerRecord prse = mPRStack.peek();
- int infoFlagsAboutToBeUsed = infoChangedFlags;
- // this is where we enforce opt-in for information display on the remote controls
- // with the new AudioManager.registerRemoteControlClient() API
- if (prse.getRcc() == null) {
- //Log.w(TAG, "Can't update remote control display with null remote control client");
- clearRemoteControlDisplay_syncPrs();
- return;
- }
- synchronized(mCurrentRcLock) {
- if (!prse.getRcc().equals(mCurrentRcClient)) {
- // new RC client, assume every type of information shall be queried
- infoFlagsAboutToBeUsed = RC_INFO_ALL;
- }
- mCurrentRcClient = prse.getRcc();
- mCurrentRcClientIntent = prse.getMediaButtonIntent();
- }
- // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
- mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
- infoFlagsAboutToBeUsed /* arg1 */, 0, prse /* obj, != null */) );
- }
-
- /**
- * Helper function:
- * Called synchronized on mPRStack
- * Check whether the remote control display should be updated, triggers the update if required
- * @param infoChangedFlags the flags corresponding to the remote control client information
- * that has changed, if applicable (checking for the update conditions might trigger a
- * clear, rather than an update event).
- */
- private void checkUpdateRemoteControlDisplay_syncPrs(int infoChangedFlags) {
- // determine whether the remote control display should be refreshed
- // if the player record stack is empty, there is nothing to display, so clear the RC display
- if (mPRStack.isEmpty()) {
- clearRemoteControlDisplay_syncPrs();
- return;
- }
-
- // this is where more rules for refresh go
-
- // refresh conditions were verified: update the remote controls
- // ok to call: synchronized on mPRStack, mPRStack is not empty
- updateRemoteControlDisplay_syncPrs(infoChangedFlags);
- }
-
- /**
- * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
- * precondition: mediaIntent != null
- */
- protected void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
- IBinder token) {
- Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
-
- synchronized(mPRStack) {
- if (pushMediaButtonReceiver_syncPrs(mediaIntent, eventReceiver, token)) {
- // new RC client, assume every type of information shall be queried
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }
- }
-
- /**
- * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
- * precondition: mediaIntent != null, eventReceiver != null
- */
- protected void unregisterMediaButtonIntent(PendingIntent mediaIntent)
- {
- Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent);
-
- synchronized(mPRStack) {
- boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
- removeMediaButtonReceiver_syncPrs(mediaIntent);
- if (topOfStackWillChange) {
- // current RC client will change, assume every type of info needs to be queried
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }
- }
-
- protected void unregisterMediaButtonIntentAsync(final PendingIntent mediaIntent) {
- mEventHandler.sendMessage(
- mEventHandler.obtainMessage(MSG_UNREGISTER_MEDIABUTTONINTENT, 0, 0,
- mediaIntent));
- }
-
- /**
- * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
- * precondition: c != null
- */
- protected void registerMediaButtonEventReceiverForCalls(ComponentName c) {
- if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
- != PackageManager.PERMISSION_GRANTED) {
- Log.e(TAG, "Invalid permissions to register media button receiver for calls");
- return;
- }
- synchronized(mPRStack) {
- mMediaReceiverForCalls = c;
- }
- }
-
- /**
- * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
- */
- protected void unregisterMediaButtonEventReceiverForCalls() {
- if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
- != PackageManager.PERMISSION_GRANTED) {
- Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
- return;
- }
- synchronized(mPRStack) {
- mMediaReceiverForCalls = null;
- }
- }
-
- /**
- * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
- * @return the unique ID of the PlayerRecord associated with the RemoteControlClient
- * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
- * without modifying the RC stack, but while still causing the display to refresh (will
- * become blank as a result of this)
- */
- protected int registerRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient, String callingPackageName) {
- if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
- int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
- synchronized(mPRStack) {
- // store the new display information
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if(prse.hasMatchingMediaButtonIntent(mediaIntent)) {
- prse.resetControllerInfoForRcc(rcClient, callingPackageName,
- Binder.getCallingUid());
-
- if (rcClient == null) {
- break;
- }
-
- rccId = prse.getRccId();
-
- // there is a new (non-null) client:
- // give the new client the displays (if any)
- if (mRcDisplays.size() > 0) {
- plugRemoteControlDisplaysIntoClient_syncPrs(prse.getRcc());
- }
- break;
- }
- }//for
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
-
- // if the eventReceiver is at the top of the stack
- // then check for potential refresh of the remote controls
- if (isCurrentRcController(mediaIntent)) {
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }//synchronized(mPRStack)
- return rccId;
- }
-
- /**
- * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
- * rcClient is guaranteed non-null
- */
- protected void unregisterRemoteControlClient(PendingIntent mediaIntent,
- IRemoteControlClient rcClient) {
- if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
- synchronized(mPRStack) {
- boolean topRccChange = false;
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if ((prse.hasMatchingMediaButtonIntent(mediaIntent))
- && rcClient.equals(prse.getRcc())) {
- // we found the IRemoteControlClient to unregister
- prse.resetControllerInfoForNoRcc();
- topRccChange = (index == mPRStack.size()-1);
- // there can only be one matching RCC in the RC stack, we're done
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
- if (topRccChange) {
- // no more RCC for the RCD, check for potential refresh of the remote controls
- checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
- }
- }
- }
-
-
- /**
- * A class to encapsulate all the information about a remote control display.
- * After instanciation, init() must always be called before the object is added in the list
- * of displays.
- * Before being removed from the list of displays, release() must always be called (otherwise
- * it will leak death handlers).
- */
- private class DisplayInfoForServer implements IBinder.DeathRecipient {
- /** may never be null */
- private final IRemoteControlDisplay mRcDisplay;
- private final IBinder mRcDisplayBinder;
- private int mArtworkExpectedWidth = -1;
- private int mArtworkExpectedHeight = -1;
- private boolean mWantsPositionSync = false;
- private ComponentName mClientNotifListComp;
- private boolean mEnabled = true;
-
- public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
- if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
- mRcDisplay = rcd;
- mRcDisplayBinder = rcd.asBinder();
- mArtworkExpectedWidth = w;
- mArtworkExpectedHeight = h;
- }
-
- public boolean init() {
- try {
- mRcDisplayBinder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- // remote control display is DOA, disqualify it
- Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
- return false;
- }
- return true;
- }
-
- public void release() {
- try {
- mRcDisplayBinder.unlinkToDeath(this, 0);
- } catch (java.util.NoSuchElementException e) {
- // not much we can do here, the display should have been unregistered anyway
- Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
- }
- }
-
- public void binderDied() {
- synchronized(mPRStack) {
- Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
- // remove the display from the list
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay == mRcDisplay) {
- if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
- displayIterator.remove();
- return;
- }
- }
- }
- }
- }
-
- /**
- * The remote control displays.
- * Access synchronized on mPRStack
- */
- private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
-
- /**
- * Plug each registered display into the specified client
- * @param rcc, guaranteed non null
- */
- private void plugRemoteControlDisplaysIntoClient_syncPrs(IRemoteControlClient rcc) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- try {
- rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
- di.mArtworkExpectedHeight);
- if (di.mWantsPositionSync) {
- rcc.setWantsSyncForDisplay(di.mRcDisplay, true);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
- }
- }
- }
-
- private void enableRemoteControlDisplayForClient_syncRcStack(IRemoteControlDisplay rcd,
- boolean enabled) {
- // let all the remote control clients know whether the given display is enabled
- // (so the remote control stack traversal order doesn't matter).
- final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- PlayerRecord prse = stackIterator.next();
- if(prse.getRcc() != null) {
- try {
- prse.getRcc().enableRemoteControlDisplay(rcd, enabled);
- } catch (RemoteException e) {
- Log.e(TAG, "Error connecting RCD to client: ", e);
- }
- }
- }
- }
-
- /**
- * Is the remote control display interface already registered
- * @param rcd
- * @return true if the IRemoteControlDisplay is already in the list of displays
- */
- private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Register an IRemoteControlDisplay.
- * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
- * at the top of the stack to update the new display with its information.
- * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
- * @param rcd the IRemoteControlDisplay to register. No effect if null.
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param listenerComp the component for the listener interface, may be null if it's not needed
- * to verify it belongs to one of the enabled notification listeners
- */
- private void registerRemoteControlDisplay_int(IRemoteControlDisplay rcd, int w, int h,
- ComponentName listenerComp) {
- if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
- synchronized(mAudioFocusLock) {
- synchronized(mPRStack) {
- if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
- return;
- }
- DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
- di.mEnabled = true;
- di.mClientNotifListComp = listenerComp;
- if (!di.init()) {
- if (DEBUG_RC) Log.e(TAG, " error registering RCD");
- return;
- }
- // add RCD to list of displays
- mRcDisplays.add(di);
-
- // let all the remote control clients know there is a new display (so the remote
- // control stack traversal order doesn't matter).
- Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- PlayerRecord prse = stackIterator.next();
- if(prse.getRcc() != null) {
- try {
- prse.getRcc().plugRemoteControlDisplay(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Error connecting RCD to client: ", e);
- }
- }
- }
-
- // we have a new display, of which all the clients are now aware: have it be
- // initialized wih the current gen ID and the current client info, do not
- // reset the information for the other (existing) displays
- sendMsg(mEventHandler, MSG_RCDISPLAY_INIT_INFO, SENDMSG_QUEUE,
- w /*arg1*/, h /*arg2*/,
- rcd /*obj*/, 0/*delay*/);
- }
- }
- }
-
- /**
- * Unregister an IRemoteControlDisplay.
- * No effect if the IRemoteControlDisplay hasn't been successfully registered.
- * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
- * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
- */
- protected void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
- if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
- synchronized(mPRStack) {
- if (rcd == null) {
- return;
- }
-
- boolean displayWasPluggedIn = false;
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext() && !displayWasPluggedIn) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- displayWasPluggedIn = true;
- di.release();
- displayIterator.remove();
- }
- }
-
- if (displayWasPluggedIn) {
- // disconnect this remote control display from all the clients, so the remote
- // control stack traversal order doesn't matter
- final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- final PlayerRecord prse = stackIterator.next();
- if(prse.getRcc() != null) {
- try {
- prse.getRcc().unplugRemoteControlDisplay(rcd);
- } catch (RemoteException e) {
- Log.e(TAG, "Error disconnecting remote control display to client: ", e);
- }
- }
- }
- } else {
- if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD");
- }
- }
- }
-
- /**
- * Update the size of the artwork used by an IRemoteControlDisplay.
- * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
- * @param rcd the IRemoteControlDisplay with the new artwork size requirement
- * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
- * display doesn't need to receive artwork.
- */
- protected void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
- synchronized(mPRStack) {
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- boolean artworkSizeUpdate = false;
- while (displayIterator.hasNext() && !artworkSizeUpdate) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
- di.mArtworkExpectedWidth = w;
- di.mArtworkExpectedHeight = h;
- artworkSizeUpdate = true;
- }
- }
- }
- if (artworkSizeUpdate) {
- // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
- // stack traversal order doesn't matter
- final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while(stackIterator.hasNext()) {
- final PlayerRecord prse = stackIterator.next();
- if(prse.getRcc() != null) {
- try {
- prse.getRcc().setBitmapSizeForDisplay(rcd, w, h);
- } catch (RemoteException e) {
- Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
- }
- }
- }
- }
- }
- }
-
- /**
- * Controls whether a remote control display needs periodic checks of the RemoteControlClient
- * playback position to verify that the estimated position has not drifted from the actual
- * position. By default the check is not performed.
- * The IRemoteControlDisplay must have been previously registered for this to have any effect.
- * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
- * or disabled. Not null.
- * @param wantsSync if true, RemoteControlClient instances which expose their playback position
- * to the framework will regularly compare the estimated playback position with the actual
- * position, and will update the IRemoteControlDisplay implementation whenever a drift is
- * detected.
- */
- protected void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
- boolean wantsSync) {
- synchronized(mPRStack) {
- boolean rcdRegistered = false;
- // store the information about this display
- // (display stack traversal order doesn't matter).
- final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
- while (displayIterator.hasNext()) {
- final DisplayInfoForServer di = displayIterator.next();
- if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
- di.mWantsPositionSync = wantsSync;
- rcdRegistered = true;
- break;
- }
- }
- if (!rcdRegistered) {
- return;
- }
- // notify all current RemoteControlClients
- // (stack traversal order doesn't matter as we notify all RCCs)
- final Iterator<PlayerRecord> stackIterator = mPRStack.iterator();
- while (stackIterator.hasNext()) {
- final PlayerRecord prse = stackIterator.next();
- if (prse.getRcc() != null) {
- try {
- prse.getRcc().setWantsSyncForDisplay(rcd, wantsSync);
- } catch (RemoteException e) {
- Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e);
- }
- }
- }
- }
- }
-
- // handler for MSG_RCC_NEW_VOLUME_OBS
- private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
- synchronized(mPRStack) {
- // The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, but the matching ID is more likely at the top of the stack, so
- // start iterating from the top.
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if (prse.getRccId() == rccId) {
- prse.mRemoteVolumeObs = rvo;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
- }
-
- /**
- * Checks if a remote client is active on the supplied stream type. Update the remote stream
- * volume state if found and playing
- * @param streamType
- * @return false if no remote playing is currently playing
- */
- protected boolean checkUpdateRemoteStateIfActive(int streamType) {
- synchronized(mPRStack) {
- // iterating from top of stack as active playback is more likely on entries at the top
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- if ((prse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
- && isPlaystateActive(prse.mPlaybackState.mState)
- && (prse.mPlaybackStream == streamType)) {
- if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
- + ", vol =" + prse.mPlaybackVolume);
- synchronized (mMainRemote) {
- mMainRemote.mRccId = prse.getRccId();
- mMainRemote.mVolume = prse.mPlaybackVolume;
- mMainRemote.mVolumeMax = prse.mPlaybackVolumeMax;
- mMainRemote.mVolumeHandling = prse.mPlaybackVolumeHandling;
- mMainRemoteIsActive = true;
- }
- return true;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
- }
- }
- synchronized (mMainRemote) {
- mMainRemoteIsActive = false;
- }
- return false;
- }
-
- /**
- * Returns true if the given playback state is considered "active", i.e. it describes a state
- * where playback is happening, or about to
- * @param playState the playback state to evaluate
- * @return true if active, false otherwise (inactive or unknown)
- */
- protected static boolean isPlaystateActive(int playState) {
- switch (playState) {
- case RemoteControlClient.PLAYSTATE_PLAYING:
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- case RemoteControlClient.PLAYSTATE_REWINDING:
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- return true;
- default:
- return false;
- }
- }
-
- private void sendVolumeUpdateToRemote(int rccId, int direction) {
- if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
- if (direction == 0) {
- // only handling discrete events
- return;
- }
- IRemoteVolumeObserver rvo = null;
- synchronized (mPRStack) {
- // The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, but the matching ID is more likely at the top of the stack, so
- // start iterating from the top.
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
- if (prse.getRccId() == rccId) {
- rvo = prse.mRemoteVolumeObs;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
- if (rvo != null) {
- try {
- rvo.dispatchRemoteVolumeUpdate(direction, -1);
- } catch (RemoteException e) {
- Log.e(TAG, "Error dispatching relative volume update", e);
- }
- }
- }
-
- protected int getRemoteStreamMaxVolume() {
- synchronized (mMainRemote) {
- if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
- return 0;
- }
- return mMainRemote.mVolumeMax;
- }
- }
-
- protected int getRemoteStreamVolume() {
- synchronized (mMainRemote) {
- if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
- return 0;
- }
- return mMainRemote.mVolume;
- }
- }
-
- protected void setRemoteStreamVolume(int vol) {
- if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
- int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
- synchronized (mMainRemote) {
- if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
- return;
- }
- rccId = mMainRemote.mRccId;
- }
- IRemoteVolumeObserver rvo = null;
- synchronized (mPRStack) {
- // The stack traversal order doesn't matter because there is only one stack entry
- // with this RCC ID, but the matching ID is more likely at the top of the stack, so
- // start iterating from the top.
- try {
- for (int index = mPRStack.size()-1; index >= 0; index--) {
- final PlayerRecord prse = mPRStack.elementAt(index);
- //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
- if (prse.getRccId() == rccId) {
- rvo = prse.mRemoteVolumeObs;
- break;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- // not expected to happen, indicates improper concurrent modification
- Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
- }
- }
- if (rvo != null) {
- try {
- rvo.dispatchRemoteVolumeUpdate(0, vol);
- } catch (RemoteException e) {
- Log.e(TAG, "Error dispatching absolute volume update", e);
- }
- }
- }
-
- /**
- * Call to make AudioService reevaluate whether it's in a mode where remote players should
- * have their volume controlled. In this implementation this is only to reset whether
- * VolumePanel should display remote volumes
- */
- protected void postReevaluateRemote() {
- sendMsg(mEventHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
- }
-
- private void onReevaluateRemote() {
- // TODO This was used to notify VolumePanel if there was remote playback
- // in the stack. This is now in MediaSessionService. More code should be
- // removed.
- }
-
}
diff --git a/services/core/java/com/android/server/audio/PlayerRecord.java b/services/core/java/com/android/server/audio/PlayerRecord.java
deleted file mode 100644
index e98f12e..0000000
--- a/services/core/java/com/android/server/audio/PlayerRecord.java
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.audio;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.media.AudioManager;
-import android.media.IRemoteControlClient;
-import android.media.IRemoteVolumeObserver;
-import android.media.RemoteControlClient;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.io.PrintWriter;
-
-/**
- * @hide
- * Class to handle all the information about a media player, encapsulating information
- * about its use RemoteControlClient, playback type and volume... The lifecycle of each
- * instance is managed by android.media.MediaFocusControl, from its addition to the player stack
- * stack to its release.
- */
-class PlayerRecord implements DeathRecipient {
-
- // on purpose not using this classe's name, as it will only be used from MediaFocusControl
- private static final String TAG = "MediaFocusControl";
- private static final boolean DEBUG = false;
-
- /**
- * A global counter for RemoteControlClient identifiers
- */
- private static int sLastRccId = 0;
-
- public static MediaFocusControl sController;
-
- /**
- * The target for the ACTION_MEDIA_BUTTON events.
- * Always non null. //FIXME verify
- */
- final private PendingIntent mMediaIntent;
- /**
- * The registered media button event receiver.
- */
- final private ComponentName mReceiverComponent;
-
- private int mRccId = -1;
-
- /**
- * A non-null token implies this record tracks a "live" player whose death is being monitored.
- */
- private IBinder mToken;
- private String mCallingPackageName;
- private int mCallingUid;
- /**
- * Provides access to the information to display on the remote control.
- * May be null (when a media button event receiver is registered,
- * but no remote control client has been registered) */
- private IRemoteControlClient mRcClient;
- private RcClientDeathHandler mRcClientDeathHandler;
- /**
- * Information only used for non-local playback
- */
- //FIXME private?
- public int mPlaybackType;
- public int mPlaybackVolume;
- public int mPlaybackVolumeMax;
- public int mPlaybackVolumeHandling;
- public int mPlaybackStream;
- public RccPlaybackState mPlaybackState;
- public IRemoteVolumeObserver mRemoteVolumeObs;
-
-
- protected static class RccPlaybackState {
- public int mState;
- public long mPositionMs;
- public float mSpeed;
-
- public RccPlaybackState(int state, long positionMs, float speed) {
- mState = state;
- mPositionMs = positionMs;
- mSpeed = speed;
- }
-
- public void reset() {
- mState = RemoteControlClient.PLAYSTATE_STOPPED;
- mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
- mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
- }
-
- @Override
- public String toString() {
- return stateToString() + ", " + posToString() + ", " + mSpeed + "X";
- }
-
- private String posToString() {
- if (mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) {
- return "PLAYBACK_POSITION_INVALID";
- } else if (mPositionMs == RemoteControlClient.PLAYBACK_POSITION_ALWAYS_UNKNOWN) {
- return "PLAYBACK_POSITION_ALWAYS_UNKNOWN";
- } else {
- return (String.valueOf(mPositionMs) + "ms");
- }
- }
-
- private String stateToString() {
- switch (mState) {
- case RemoteControlClient.PLAYSTATE_NONE:
- return "PLAYSTATE_NONE";
- case RemoteControlClient.PLAYSTATE_STOPPED:
- return "PLAYSTATE_STOPPED";
- case RemoteControlClient.PLAYSTATE_PAUSED:
- return "PLAYSTATE_PAUSED";
- case RemoteControlClient.PLAYSTATE_PLAYING:
- return "PLAYSTATE_PLAYING";
- case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
- return "PLAYSTATE_FAST_FORWARDING";
- case RemoteControlClient.PLAYSTATE_REWINDING:
- return "PLAYSTATE_REWINDING";
- case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
- return "PLAYSTATE_SKIPPING_FORWARDS";
- case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
- return "PLAYSTATE_SKIPPING_BACKWARDS";
- case RemoteControlClient.PLAYSTATE_BUFFERING:
- return "PLAYSTATE_BUFFERING";
- case RemoteControlClient.PLAYSTATE_ERROR:
- return "PLAYSTATE_ERROR";
- default:
- return "[invalid playstate]";
- }
- }
- }
-
-
- /**
- * Inner class to monitor remote control client deaths, and remove the client for the
- * remote control stack if necessary.
- */
- private class RcClientDeathHandler implements IBinder.DeathRecipient {
- final private IBinder mCb; // To be notified of client's death
- //FIXME needed?
- final private PendingIntent mMediaIntent;
-
- RcClientDeathHandler(IBinder cb, PendingIntent pi) {
- mCb = cb;
- mMediaIntent = pi;
- }
-
- public void binderDied() {
- Log.w(TAG, " RemoteControlClient died");
- // remote control client died, make sure the displays don't use it anymore
- // by setting its remote control client to null
- sController.registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
- // the dead client was maybe handling remote playback, the controller should reevaluate
- sController.postReevaluateRemote();
- }
-
- public IBinder getBinder() {
- return mCb;
- }
- }
-
-
- protected static class RemotePlaybackState {
- int mRccId;
- int mVolume;
- int mVolumeMax;
- int mVolumeHandling;
-
- protected RemotePlaybackState(int id, int vol, int volMax) {
- mRccId = id;
- mVolume = vol;
- mVolumeMax = volMax;
- mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
- }
- }
-
-
- void dump(PrintWriter pw, boolean registrationInfo) {
- if (registrationInfo) {
- pw.println(" pi: " + mMediaIntent +
- " -- pack: " + mCallingPackageName +
- " -- ercvr: " + mReceiverComponent +
- " -- client: " + mRcClient +
- " -- uid: " + mCallingUid +
- " -- type: " + mPlaybackType +
- " state: " + mPlaybackState);
- } else {
- // emphasis on state
- pw.println(" uid: " + mCallingUid +
- " -- id: " + mRccId +
- " -- type: " + mPlaybackType +
- " -- state: " + mPlaybackState +
- " -- vol handling: " + mPlaybackVolumeHandling +
- " -- vol: " + mPlaybackVolume +
- " -- volMax: " + mPlaybackVolumeMax +
- " -- volObs: " + mRemoteVolumeObs);
- }
- }
-
-
- static protected void setMediaFocusControl(MediaFocusControl mfc) {
- sController = mfc;
- }
-
- /** precondition: mediaIntent != null */
- protected PlayerRecord(PendingIntent mediaIntent, ComponentName eventReceiver, IBinder token)
- {
- mMediaIntent = mediaIntent;
- mReceiverComponent = eventReceiver;
- mToken = token;
- mCallingUid = -1;
- mRcClient = null;
- mRccId = ++sLastRccId;
- mPlaybackState = new RccPlaybackState(
- RemoteControlClient.PLAYSTATE_STOPPED,
- RemoteControlClient.PLAYBACK_POSITION_INVALID,
- RemoteControlClient.PLAYBACK_SPEED_1X);
-
- resetPlaybackInfo();
- if (mToken != null) {
- try {
- mToken.linkToDeath(this, 0);
- } catch (RemoteException e) {
- sController.unregisterMediaButtonIntentAsync(mMediaIntent);
- }
- }
- }
-
- //---------------------------------------------
- // Accessors
- protected int getRccId() {
- return mRccId;
- }
-
- protected IRemoteControlClient getRcc() {
- return mRcClient;
- }
-
- protected ComponentName getMediaButtonReceiver() {
- return mReceiverComponent;
- }
-
- protected PendingIntent getMediaButtonIntent() {
- return mMediaIntent;
- }
-
- protected boolean hasMatchingMediaButtonIntent(PendingIntent pi) {
- if (mToken != null) {
- return mMediaIntent.equals(pi);
- } else {
- if (mReceiverComponent != null) {
- return mReceiverComponent.equals(pi.getIntent().getComponent());
- } else {
- return false;
- }
- }
- }
-
- protected boolean isPlaybackActive() {
- return MediaFocusControl.isPlaystateActive(mPlaybackState.mState);
- }
-
- //---------------------------------------------
- // Modify the records stored in the instance
- protected void resetControllerInfoForRcc(IRemoteControlClient rcClient,
- String callingPackageName, int uid) {
- // already had a remote control client?
- if (mRcClientDeathHandler != null) {
- // stop monitoring the old client's death
- unlinkToRcClientDeath();
- }
- // save the new remote control client
- mRcClient = rcClient;
- mCallingPackageName = callingPackageName;
- mCallingUid = uid;
- if (rcClient == null) {
- // here mcse.mRcClientDeathHandler is null;
- resetPlaybackInfo();
- } else {
- IBinder b = mRcClient.asBinder();
- RcClientDeathHandler rcdh =
- new RcClientDeathHandler(b, mMediaIntent);
- try {
- b.linkToDeath(rcdh, 0);
- } catch (RemoteException e) {
- // remote control client is DOA, disqualify it
- Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
- mRcClient = null;
- }
- mRcClientDeathHandler = rcdh;
- }
- }
-
- protected void resetControllerInfoForNoRcc() {
- // stop monitoring the RCC death
- unlinkToRcClientDeath();
- // reset the RCC-related fields
- mRcClient = null;
- mCallingPackageName = null;
- }
-
- public void resetPlaybackInfo() {
- mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
- mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
- mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
- mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
- mPlaybackStream = AudioManager.STREAM_MUSIC;
- mPlaybackState.reset();
- mRemoteVolumeObs = null;
- }
-
- //---------------------------------------------
- public void unlinkToRcClientDeath() {
- if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
- try {
- mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
- mRcClientDeathHandler = null;
- } catch (java.util.NoSuchElementException e) {
- // not much we can do here
- Log.e(TAG, "Error in unlinkToRcClientDeath()", e);
- }
- }
- }
-
- // FIXME rename to "release"? (as in FocusRequester class)
- public void destroy() {
- unlinkToRcClientDeath();
- if (mToken != null) {
- mToken.unlinkToDeath(this, 0);
- mToken = null;
- }
- }
-
- @Override
- public void binderDied() {
- sController.unregisterMediaButtonIntentAsync(mMediaIntent);
- }
-
- @Override
- protected void finalize() throws Throwable {
- destroy(); // unlink exception handled inside method
- super.finalize();
- }
-}