summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/java/android/media/session/ISessionCallback.aidl2
-rw-r--r--media/java/android/media/session/MediaSession.java22
-rw-r--r--media/java/android/media/session/MediaSessionLegacyHelper.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java38
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java10
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java202
6 files changed, 216 insertions, 60 deletions
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 7b0412e..103c3f1 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -28,7 +28,7 @@ import android.os.ResultReceiver;
*/
oneway interface ISessionCallback {
void onCommand(String command, in Bundle extras, in ResultReceiver cb);
- void onMediaButton(in Intent mediaButtonIntent, in ResultReceiver cb);
+ void onMediaButton(in Intent mediaButtonIntent, int sequenceNumber, in ResultReceiver cb);
void onRequestRouteChange(in RouteInfo route);
void onRouteConnected(in RouteInfo route, in RouteOptions options);
void onRouteDisconnected(in RouteInfo route, int reason);
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 5b9adaa..6a62dc2 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -119,13 +119,6 @@ public final class MediaSession {
*/
public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
- /**
- * Status code indicating the call was handled.
- *
- * @hide
- */
- public static final int RESULT_SUCCESS = 0;
-
private static final int MSG_MEDIA_BUTTON = 1;
private static final int MSG_COMMAND = 2;
private static final int MSG_ROUTE_CHANGE = 3;
@@ -563,14 +556,17 @@ public final class MediaSession {
}
@Override
- public void onMediaButton(Intent mediaButtonIntent, ResultReceiver cb)
+ public void onMediaButton(Intent mediaButtonIntent, int sequenceNumber, ResultReceiver cb)
throws RemoteException {
MediaSession session = mMediaSession.get();
- if (session != null) {
- session.postMediaButton(mediaButtonIntent);
- }
- if (cb != null) {
- cb.send(RESULT_SUCCESS, null);
+ try {
+ if (session != null) {
+ session.postMediaButton(mediaButtonIntent);
+ }
+ } finally {
+ if (cb != null) {
+ cb.send(sequenceNumber, null);
+ }
}
}
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 2e02a66..249b9c4 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -137,6 +137,8 @@ public class MediaSessionLegacyHelper {
return;
}
holder.mMediaButtonListener = new MediaButtonListener(pi, context);
+ // TODO determine if handling transport performer commands should also
+ // set this flag
holder.mFlags |= MediaSession.FLAG_HANDLES_MEDIA_BUTTONS;
holder.mSession.setFlags(holder.mFlags);
holder.mSession.getTransportPerformer().addListener(holder.mMediaButtonListener, mHandler);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
index 417527c..b2ecb61 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
@@ -24,6 +24,7 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.media.IAudioService;
+import android.media.session.MediaSessionLegacyHelper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -39,6 +40,9 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
private static String TAG = "PhoneFallbackEventHandler";
private static final boolean DEBUG = false;
+ // Use the new sessions APIs
+ private static final boolean USE_SESSIONS = true;
+
Context mContext;
View mView;
@@ -70,14 +74,14 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
return onKeyUp(keyCode, event);
}
}
-
+
boolean onKeyDown(int keyCode, KeyEvent event) {
/* ****************************************************************************
* HOW TO DECIDE WHERE YOUR KEY HANDLING GOES.
* See the comment in PhoneWindow.onKeyDown
* ****************************************************************************/
final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
-
+
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -156,7 +160,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
if (event.getRepeatCount() == 0) {
dispatcher.startTracking(event, this);
} else if (event.isLongPress() && dispatcher.isTracking(event)) {
- Configuration config = mContext.getResources().getConfiguration();
+ Configuration config = mContext.getResources().getConfiguration();
if (config.keyboard == Configuration.KEYBOARD_NOKEYS
|| config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
// launch the search activity
@@ -191,7 +195,7 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
if (dispatcher != null) {
dispatcher.handleUpEvent(event);
}
-
+
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -277,29 +281,33 @@ public class PhoneFallbackEventHandler implements FallbackEventHandler {
}
return mKeyguardManager;
}
-
+
AudioManager getAudioManager() {
if (mAudioManager == null) {
mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
}
return mAudioManager;
}
-
+
void sendCloseSystemWindows() {
PhoneWindowManager.sendCloseSystemWindows(mContext, null);
}
private void handleMediaKeyEvent(KeyEvent keyEvent) {
- IAudioService audioService = IAudioService.Stub.asInterface(
- ServiceManager.checkService(Context.AUDIO_SERVICE));
- if (audioService != null) {
- try {
- audioService.dispatchMediaKeyEvent(keyEvent);
- } catch (RemoteException e) {
- Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
- }
+ if (USE_SESSIONS) {
+ MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
} else {
- Slog.w(TAG, "Unable to find IAudioService for media key event.");
+ IAudioService audioService = IAudioService.Stub.asInterface(
+ ServiceManager.checkService(Context.AUDIO_SERVICE));
+ if (audioService != null) {
+ try {
+ audioService.dispatchMediaKeyEvent(keyEvent);
+ } catch (RemoteException e) {
+ Log.e(TAG, "dispatchMediaKeyEvent threw exception " + e);
+ }
+ } else {
+ Slog.w(TAG, "Unable to find IAudioService for media key event.");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 00d364b..030e3ed 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -365,8 +365,8 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
return mSessionCb.mCb;
}
- public void sendMediaButton(KeyEvent ke, ResultReceiver cb) {
- mSessionCb.sendMediaButton(ke, cb);
+ public void sendMediaButton(KeyEvent ke, int sequenceId, ResultReceiver cb) {
+ mSessionCb.sendMediaButton(ke, sequenceId, cb);
}
public void dump(PrintWriter pw, String prefix) {
@@ -649,11 +649,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
mCb = cb;
}
- public void sendMediaButton(KeyEvent keyEvent, ResultReceiver cb) {
+ public void sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
try {
- mCb.onMediaButton(mediaButtonIntent, cb);
+ mCb.onMediaButton(mediaButtonIntent, sequenceId, cb);
} catch (RemoteException e) {
Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
}
@@ -789,7 +789,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
@Override
public void sendMediaButton(KeyEvent mediaButtonIntent) {
- mSessionCb.sendMediaButton(mediaButtonIntent, null);
+ mSessionCb.sendMediaButton(mediaButtonIntent, 0, null);
}
@Override
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index d9e45f5ba..9d85167 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -19,6 +19,8 @@ package com.android.server.media;
import android.Manifest;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -40,6 +42,7 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.provider.Settings;
+import android.speech.RecognizerIntent;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -61,6 +64,8 @@ public class MediaSessionService extends SystemService implements Monitor {
private static final String TAG = "MediaSessionService";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final int WAKELOCK_TIMEOUT = 5000;
+
private final SessionManagerImpl mSessionManagerImpl;
// private final MediaRouteProviderWatcher mRouteProviderWatcher;
private final MediaSessionStack mPriorityStack;
@@ -73,6 +78,8 @@ public class MediaSessionService extends SystemService implements Monitor {
private final Handler mHandler = new Handler();
private final PowerManager.WakeLock mMediaEventWakeLock;
+ private KeyguardManager mKeyguardManager;
+
private MediaSessionRecord mPrioritySession;
private int mCurrentUserId = -1;
@@ -96,6 +103,8 @@ public class MediaSessionService extends SystemService implements Monitor {
publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
Watchdog.getInstance().addMonitor(this);
updateUser();
+ mKeyguardManager =
+ (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
}
/**
@@ -599,6 +608,9 @@ public class MediaSessionService extends SystemService implements Monitor {
"android.media.AudioService.WAKELOCK_ACQUIRED";
private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
+ private boolean mVoiceButtonDown = false;
+ private boolean mVoiceButtonHandled = false;
+
@Override
public ISession createSession(String packageName, ISessionCallback cb, String tag,
int userId) throws RemoteException {
@@ -676,31 +688,13 @@ public class MediaSessionService extends SystemService implements Monitor {
final long token = Binder.clearCallingIdentity();
try {
- if (needWakeLock) {
- mMediaEventWakeLock.acquire();
- }
synchronized (mLock) {
- MediaSessionRecord mbSession = mPriorityStack
+ MediaSessionRecord session = mPriorityStack
.getDefaultMediaButtonSession(mCurrentUserId);
- if (mbSession != null) {
- if (DEBUG) {
- Log.d(TAG, "Sending media key to " + mbSession.getSessionInfo());
- }
- mbSession.sendMediaButton(keyEvent,
- needWakeLock ? mKeyEventDoneReceiver : null);
+ if (isVoiceKey(keyEvent.getKeyCode())) {
+ handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
} else {
- if (DEBUG) {
- Log.d(TAG, "Sending media key ordered broadcast");
- }
- // Fallback to legacy behavior
- Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
- keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
- if (needWakeLock) {
- keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
- WAKELOCK_RELEASE_ON_FINISHED);
- }
- getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
- null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
+ dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
}
}
} finally {
@@ -743,15 +737,171 @@ public class MediaSessionService extends SystemService implements Monitor {
}
}
- ResultReceiver mKeyEventDoneReceiver = new ResultReceiver(mHandler) {
+ private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
+ MediaSessionRecord session) {
+ if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
+ // If the phone app has priority just give it the event
+ dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
+ return;
+ }
+ int action = keyEvent.getAction();
+ boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
+ if (action == KeyEvent.ACTION_DOWN) {
+ if (keyEvent.getRepeatCount() == 0) {
+ mVoiceButtonDown = true;
+ mVoiceButtonHandled = false;
+ } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
+ mVoiceButtonHandled = true;
+ startVoiceInput(needWakeLock);
+ }
+ } else if (action == KeyEvent.ACTION_UP) {
+ if (mVoiceButtonDown) {
+ mVoiceButtonDown = false;
+ if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
+ // Resend the down then send this event through
+ KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
+ dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
+ dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
+ }
+ }
+ }
+ }
+
+ private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
+ MediaSessionRecord session) {
+ if (session != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Sending media key to " + session.getSessionInfo());
+ }
+ if (needWakeLock) {
+ mKeyEventReceiver.aquireWakeLockLocked();
+ }
+ // If we don't need a wakelock use -1 as the id so we
+ // won't release it later
+ session.sendMediaButton(keyEvent,
+ needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
+ mKeyEventReceiver);
+ } else {
+ if (needWakeLock) {
+ mMediaEventWakeLock.acquire();
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Sending media key ordered broadcast");
+ }
+ // Fallback to legacy behavior
+ Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+ keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+ if (needWakeLock) {
+ keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
+ WAKELOCK_RELEASE_ON_FINISHED);
+ }
+ getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
+ }
+ }
+
+ private void startVoiceInput(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) getContext().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 {
+ 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();
+ }
+ try {
+ if (voiceIntent != null) {
+ voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
+ }
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "No activity for search: " + e);
+ } finally {
+ if (needWakeLock) {
+ mMediaEventWakeLock.release();
+ }
+ }
+ }
+
+ private boolean isVoiceKey(int keyCode) {
+ return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
+ }
+
+ private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
+
+ class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable {
+ private final Handler mHandler;
+ private int mRefCount = 0;
+ private int mLastTimeoutId = 0;
+
+ public KeyEventWakeLockReceiver(Handler handler) {
+ super(handler);
+ mHandler = handler;
+ }
+
+ public void onTimeout() {
+ synchronized (mLock) {
+ if (mRefCount == 0) {
+ // We've already released it, so just return
+ return;
+ }
+ mLastTimeoutId++;
+ mRefCount = 0;
+ releaseWakeLockLocked();
+ }
+ }
+
+ public void aquireWakeLockLocked() {
+ if (mRefCount == 0) {
+ mMediaEventWakeLock.acquire();
+ }
+ mRefCount++;
+ mHandler.removeCallbacks(this);
+ mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
+
+ }
+
+ @Override
+ public void run() {
+ onTimeout();
+ }
+
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
- synchronized (mLock) {
- if (mMediaEventWakeLock.isHeld()) {
- mMediaEventWakeLock.release();
+ if (resultCode < mLastTimeoutId) {
+ // Ignore results from calls that were before the last
+ // timeout, just in case.
+ return;
+ } else {
+ synchronized (mLock) {
+ if (mRefCount > 0) {
+ mRefCount--;
+ if (mRefCount == 0) {
+ releaseWakeLockLocked();
+ }
+ }
}
}
}
+
+ private void releaseWakeLockLocked() {
+ mMediaEventWakeLock.release();
+ mHandler.removeCallbacks(this);
+ }
};
BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {