|
21 | 21 | import static android.media.AudioManager.RINGER_MODE_SILENT; |
22 | 22 | import static android.media.AudioManager.RINGER_MODE_VIBRATE; |
23 | 23 |
|
| 24 | +import android.app.Activity; |
24 | 25 | import android.app.ActivityManagerNative; |
25 | 26 | import android.app.KeyguardManager; |
26 | 27 | import android.app.PendingIntent; |
27 | 28 | import android.app.PendingIntent.CanceledException; |
| 29 | +import android.app.PendingIntent.OnFinished; |
28 | 30 | import android.bluetooth.BluetoothA2dp; |
29 | 31 | import android.bluetooth.BluetoothAdapter; |
30 | 32 | import android.bluetooth.BluetoothClass; |
|
48 | 50 | import android.os.IBinder; |
49 | 51 | import android.os.Looper; |
50 | 52 | import android.os.Message; |
| 53 | +import android.os.PowerManager; |
51 | 54 | import android.os.RemoteException; |
52 | 55 | import android.os.ServiceManager; |
53 | 56 | import android.os.SystemProperties; |
|
85 | 88 | * |
86 | 89 | * @hide |
87 | 90 | */ |
88 | | -public class AudioService extends IAudioService.Stub { |
| 91 | +public class AudioService extends IAudioService.Stub implements OnFinished { |
89 | 92 |
|
90 | 93 | private static final String TAG = "AudioService"; |
91 | 94 |
|
@@ -289,10 +292,6 @@ public void onError(int error) { |
289 | 292 | // Broadcast receiver for device connections intent broadcasts |
290 | 293 | private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); |
291 | 294 |
|
292 | | - // Broadcast receiver for media button broadcasts (separate from mReceiver to |
293 | | - // independently change its priority) |
294 | | - private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver(); |
295 | | - |
296 | 295 | // Used to alter media button redirection when the phone is ringing. |
297 | 296 | private boolean mIsRinging = false; |
298 | 297 |
|
@@ -383,6 +382,9 @@ public AudioService(Context context) { |
383 | 382 | mVoiceCapable = mContext.getResources().getBoolean( |
384 | 383 | com.android.internal.R.bool.config_voice_capable); |
385 | 384 |
|
| 385 | + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); |
| 386 | + mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mediaKeyEvent"); |
| 387 | + |
386 | 388 | // Intialized volume |
387 | 389 | MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt( |
388 | 390 | "ro.config.vc_call_vol_steps", |
@@ -434,13 +436,6 @@ public AudioService(Context context) { |
434 | 436 | pkgFilter.addDataScheme("package"); |
435 | 437 | context.registerReceiver(mReceiver, pkgFilter); |
436 | 438 |
|
437 | | - // Register for media button intent broadcasts. |
438 | | - intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON); |
439 | | - // Workaround for bug on priority setting |
440 | | - //intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); |
441 | | - intentFilter.setPriority(Integer.MAX_VALUE); |
442 | | - context.registerReceiver(mMediaButtonReceiver, intentFilter); |
443 | | - |
444 | 439 | // Register for phone state monitoring |
445 | 440 | TelephonyManager tmgr = (TelephonyManager) |
446 | 441 | context.getSystemService(Context.TELEPHONY_SERVICE); |
@@ -3492,54 +3487,109 @@ public void unregisterAudioFocusClient(String clientId) { |
3492 | 3487 | //========================================================================================== |
3493 | 3488 | // RemoteControl |
3494 | 3489 | //========================================================================================== |
| 3490 | + public void dispatchMediaKeyEvent(KeyEvent keyEvent) { |
| 3491 | + dispatchMediaKeyEvent(keyEvent, false /*needWakeLock*/); |
| 3492 | + } |
| 3493 | + |
| 3494 | + public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) { |
| 3495 | + dispatchMediaKeyEvent(keyEvent, true /*needWakeLock*/); |
| 3496 | + } |
| 3497 | + |
3495 | 3498 | /** |
3496 | | - * Receiver for media button intents. Handles the dispatching of the media button event |
3497 | | - * to one of the registered listeners, or if there was none, resumes the intent broadcast |
3498 | | - * to the rest of the system. |
| 3499 | + * Handles the dispatching of the media button events to one of the registered listeners, |
| 3500 | + * or if there was none, broadcast a ACTION_MEDIA_BUTTON intent to the rest of the system. |
3499 | 3501 | */ |
3500 | | - private class MediaButtonBroadcastReceiver extends BroadcastReceiver { |
3501 | | - @Override |
3502 | | - public void onReceive(Context context, Intent intent) { |
3503 | | - String action = intent.getAction(); |
3504 | | - if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) { |
| 3502 | + private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { |
| 3503 | + // sanity check on the incoming key event |
| 3504 | + if (!isValidMediaKeyEvent(keyEvent)) { |
| 3505 | + Log.e(TAG, "not dispatching invalid media key event " + keyEvent); |
| 3506 | + return; |
| 3507 | + } |
| 3508 | + // event filtering |
| 3509 | + synchronized(mRingingLock) { |
| 3510 | + if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) || |
| 3511 | + (getMode() == AudioSystem.MODE_IN_COMMUNICATION) || |
| 3512 | + (getMode() == AudioSystem.MODE_RINGTONE) ) { |
3505 | 3513 | return; |
3506 | 3514 | } |
3507 | | - KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); |
3508 | | - if (event != null) { |
3509 | | - // if in a call or ringing, do not break the current phone app behavior |
3510 | | - // TODO modify this to let the phone app specifically get the RC focus |
3511 | | - // add modify the phone app to take advantage of the new API |
3512 | | - synchronized(mRingingLock) { |
3513 | | - if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) || |
3514 | | - (getMode() == AudioSystem.MODE_IN_COMMUNICATION) || |
3515 | | - (getMode() == AudioSystem.MODE_RINGTONE) ) { |
3516 | | - return; |
3517 | | - } |
| 3515 | + } |
| 3516 | + if (needWakeLock) { |
| 3517 | + mMediaEventWakeLock.acquire(); |
| 3518 | + } |
| 3519 | + Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); |
| 3520 | + keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); |
| 3521 | + synchronized(mRCStack) { |
| 3522 | + if (!mRCStack.empty()) { |
| 3523 | + // send the intent that was registered by the client |
| 3524 | + try { |
| 3525 | + mRCStack.peek().mMediaIntent.send(mContext, |
| 3526 | + needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/, |
| 3527 | + keyIntent, AudioService.this, mAudioHandler); |
| 3528 | + } catch (CanceledException e) { |
| 3529 | + Log.e(TAG, "Error sending pending intent " + mRCStack.peek()); |
| 3530 | + e.printStackTrace(); |
3518 | 3531 | } |
3519 | | - synchronized(mRCStack) { |
3520 | | - if (!mRCStack.empty()) { |
3521 | | - // create a new intent to fill in the extras of the registered PendingIntent |
3522 | | - Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); |
3523 | | - Bundle extras = intent.getExtras(); |
3524 | | - if (extras != null) { |
3525 | | - targetedIntent.putExtras(extras); |
3526 | | - // trap the current broadcast |
3527 | | - abortBroadcast(); |
3528 | | - //Log.v(TAG, " Sending intent" + targetedIntent); |
3529 | | - // send the intent that was registered by the client |
3530 | | - try { |
3531 | | - mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent); |
3532 | | - } catch (CanceledException e) { |
3533 | | - Log.e(TAG, "Error sending pending intent " + mRCStack.peek()); |
3534 | | - e.printStackTrace(); |
3535 | | - } |
3536 | | - } |
3537 | | - } |
| 3532 | + } else { |
| 3533 | + // legacy behavior when nobody registered their media button event receiver |
| 3534 | + // through AudioManager |
| 3535 | + if (needWakeLock) { |
| 3536 | + keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED); |
3538 | 3537 | } |
| 3538 | + mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone, |
| 3539 | + mAudioHandler, Activity.RESULT_OK, null, null); |
3539 | 3540 | } |
3540 | 3541 | } |
3541 | 3542 | } |
3542 | 3543 |
|
| 3544 | + private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) { |
| 3545 | + if (keyEvent == null) { |
| 3546 | + return false; |
| 3547 | + } |
| 3548 | + final int keyCode = keyEvent.getKeyCode(); |
| 3549 | + switch (keyCode) { |
| 3550 | + case KeyEvent.KEYCODE_MUTE: |
| 3551 | + case KeyEvent.KEYCODE_HEADSETHOOK: |
| 3552 | + case KeyEvent.KEYCODE_MEDIA_PLAY: |
| 3553 | + case KeyEvent.KEYCODE_MEDIA_PAUSE: |
| 3554 | + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: |
| 3555 | + case KeyEvent.KEYCODE_MEDIA_STOP: |
| 3556 | + case KeyEvent.KEYCODE_MEDIA_NEXT: |
| 3557 | + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: |
| 3558 | + case KeyEvent.KEYCODE_MEDIA_REWIND: |
| 3559 | + case KeyEvent.KEYCODE_MEDIA_RECORD: |
| 3560 | + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: |
| 3561 | + case KeyEvent.KEYCODE_MEDIA_CLOSE: |
| 3562 | + case KeyEvent.KEYCODE_MEDIA_EJECT: |
| 3563 | + break; |
| 3564 | + default: |
| 3565 | + return false; |
| 3566 | + } |
| 3567 | + return true; |
| 3568 | + } |
| 3569 | + |
| 3570 | + private PowerManager.WakeLock mMediaEventWakeLock; |
| 3571 | + |
| 3572 | + private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number |
| 3573 | + |
| 3574 | + // only set when wakelock was acquired, no need to check value when received |
| 3575 | + private static final String EXTRA_WAKELOCK_ACQUIRED = |
| 3576 | + "android.media.AudioService.WAKELOCK_ACQUIRED"; |
| 3577 | + |
| 3578 | + public void onSendFinished(PendingIntent pendingIntent, Intent intent, |
| 3579 | + int resultCode, String resultData, Bundle resultExtras) { |
| 3580 | + if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) { |
| 3581 | + mMediaEventWakeLock.release(); |
| 3582 | + } |
| 3583 | + } |
| 3584 | + |
| 3585 | + BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { |
| 3586 | + public void onReceive(Context context, Intent intent) { |
| 3587 | + if (intent.getExtras().containsKey(EXTRA_WAKELOCK_ACQUIRED)) { |
| 3588 | + mMediaEventWakeLock.release(); |
| 3589 | + } |
| 3590 | + } |
| 3591 | + }; |
| 3592 | + |
3543 | 3593 | private final Object mCurrentRcLock = new Object(); |
3544 | 3594 | /** |
3545 | 3595 | * The one remote control client which will receive a request for display information. |
|
0 commit comments