diff options
| author | Jeff Brown <jeffbrown@google.com> | 2010-10-29 21:50:21 -0700 |
|---|---|---|
| committer | Jeff Brown <jeffbrown@google.com> | 2010-11-01 15:00:25 -0700 |
| commit | 4d396052deb54399cbadbeb8abd873df6f3af342 (patch) | |
| tree | 632cf9922df2abe3b23738792a100a9297517db0 | |
| parent | 487d9586635e6b209f9a5ed4063f005590d10e85 (diff) | |
| download | frameworks_base-4d396052deb54399cbadbeb8abd873df6f3af342.zip frameworks_base-4d396052deb54399cbadbeb8abd873df6f3af342.tar.gz frameworks_base-4d396052deb54399cbadbeb8abd873df6f3af342.tar.bz2 | |
Fix policy issues when screen is off.
Rewrote interceptKeyBeforeQueueing to make the handling more systematic.
Behavior should be identical except:
- We never pass keys to applications when the screen is off and the keyguard
is not showing (the proximity sensor turned off the screen).
Previously we passed all non-wake keys through in this case which
caused a bug on Crespo where the screen would come back on if a soft key
was held at the time of power off because the resulting key up event
would sneak in just before the keyguard was shown. It would then be
passed through to the dispatcher which would poke user activity and
wake up the screen.
- We propagate the key flags when broadcasting media keys which
ensures that recipients can tell when the key is canceled.
- We ignore endcall or power if canceled (shouldn't happen anyways).
Changed the input dispatcher to not poke user activity for canceled
events since they are synthetic and should not wake the device.
Changed the lock screen so that it does not poke the wake lock when the
grab handle is released. This fixes a bug where the screen would come
back on immediately if the power went off while the user was holding
one of the grab handles because the sliding tab would receive an up
event after screen turned off and release the grab handles.
Fixed a couple of issues where media keys were being handled inconsistently
or not at all, particularly in the case of the new PAUSE, PLAY
and RECORD keys.
Bug: 3144874
Change-Id: Ie630f5fb6f128cfdf94845f9428067045f42892c
13 files changed, 356 insertions, 308 deletions
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 1fd31a3..1a341e1 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -544,16 +544,18 @@ public interface WindowManagerPolicy { * Generally, it's best to keep as little as possible in the queue thread * because it's the most fragile. * @param whenNanos The event time in uptime nanoseconds. + * @param action The key event action. + * @param flags The key event flags. * @param keyCode The key code. - * @param down True if the key is down. + * @param scanCode The key's scan code. * @param policyFlags The policy flags associated with the key. * @param isScreenOn True if the screen is already on * * @return The bitwise or of the {@link #ACTION_PASS_TO_USER}, * {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags. */ - public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, int policyFlags, - boolean isScreenOn); + public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, + int keyCode, int scanCode, int policyFlags, boolean isScreenOn); /** * Called from the input dispatcher thread before a key is dispatched to a window. @@ -571,6 +573,7 @@ public interface WindowManagerPolicy { * @param action The key event action. * @param flags The key event flags. * @param keyCode The key code. + * @param scanCode The key's scan code. * @param metaState bit mask of meta keys that are held. * @param repeatCount Number of times a key down has repeated. * @param policyFlags The policy flags associated with the key. @@ -578,7 +581,7 @@ public interface WindowManagerPolicy { * not be further dispatched. */ public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags, - int keyCode, int metaState, int repeatCount, int policyFlags); + int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags); /** * Called when layout of the windows is about to start. diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java index 39b1377..be1234d 100644 --- a/core/java/android/widget/MediaController.java +++ b/core/java/android/widget/MediaController.java @@ -413,33 +413,46 @@ public class MediaController extends FrameLayout { @Override public boolean dispatchKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode(); - if (event.getRepeatCount() == 0 && event.isDown() && ( - keyCode == KeyEvent.KEYCODE_HEADSETHOOK || - keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || - keyCode == KeyEvent.KEYCODE_SPACE)) { - doPauseResume(); - show(sDefaultTimeout); - if (mPauseButton != null) { - mPauseButton.requestFocus(); + final boolean uniqueDown = event.getRepeatCount() == 0 + && event.getAction() == KeyEvent.ACTION_DOWN; + if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK + || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE + || keyCode == KeyEvent.KEYCODE_SPACE) { + if (uniqueDown) { + doPauseResume(); + show(sDefaultTimeout); + if (mPauseButton != null) { + mPauseButton.requestFocus(); + } + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { + if (uniqueDown && !mPlayer.isPlaying()) { + mPlayer.start(); + updatePausePlay(); + show(sDefaultTimeout); } return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) { - if (mPlayer.isPlaying()) { + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP + || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { + if (uniqueDown && mPlayer.isPlaying()) { mPlayer.pause(); updatePausePlay(); + show(sDefaultTimeout); } return true; - } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || - keyCode == KeyEvent.KEYCODE_VOLUME_UP) { + } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { // don't show the controls for volume adjustment return super.dispatchKeyEvent(event); } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) { - hide(); - + if (uniqueDown) { + hide(); + } return true; - } else { - show(sDefaultTimeout); } + + show(sDefaultTimeout); return super.dispatchKeyEvent(event); } diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 3d9cde4..2be7bca 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -529,10 +529,19 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { mMediaController.hide(); } return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { + if (mMediaPlayer.isPlaying()) { + start(); + mMediaController.hide(); + } + return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP - && mMediaPlayer.isPlaying()) { - pause(); - mMediaController.show(); + || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { + if (!mMediaPlayer.isPlaying()) { + pause(); + mMediaController.show(); + } + return true; } else { toggleMediaControlsVisiblity(); } diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index b936c4d..58d4c56 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -1408,8 +1408,13 @@ String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { int32_t eventType = POWER_MANAGER_BUTTON_EVENT; - if (eventEntry->type == EventEntry::TYPE_MOTION) { + switch (eventEntry->type) { + case EventEntry::TYPE_MOTION: { const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry); + if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) { + return; + } + if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { switch (motionEntry->action) { case AMOTION_EVENT_ACTION_DOWN: @@ -1427,6 +1432,15 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { break; } } + break; + } + case EventEntry::TYPE_KEY: { + const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry); + if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { + return; + } + break; + } } CommandEntry* commandEntry = postCommandLocked( diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java index 91dc2b2..0f1aa4e 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewBase.java @@ -145,7 +145,7 @@ public abstract class KeyguardViewBase extends FrameLayout { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - /* Suppress PLAYPAUSE toggle when phone is ringing or + /* Suppress PLAY/PAUSE toggle when phone is ringing or * in-call to avoid music playback */ if (mTelephonyManager == null) { mTelephonyManager = (TelephonyManager) getContext().getSystemService( @@ -155,11 +155,13 @@ public abstract class KeyguardViewBase extends FrameLayout { mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) { return true; // suppress key event } - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, event); @@ -191,12 +193,15 @@ public abstract class KeyguardViewBase extends FrameLayout { } else if (event.getAction() == KeyEvent.ACTION_UP) { switch (keyCode) { case KeyEvent.KEYCODE_MUTE: - case KeyEvent.KEYCODE_HEADSETHOOK: - case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - case KeyEvent.KEYCODE_MEDIA_STOP: - case KeyEvent.KEYCODE_MEDIA_NEXT: - case KeyEvent.KEYCODE_MEDIA_PREVIOUS: - case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, event); diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index c034ec9..c870503 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -795,6 +795,7 @@ public class KeyguardViewMediator implements KeyguardViewCallback, case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: case KeyEvent.KEYCODE_CAMERA: return false; diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java index 1383354..4644a7c 100644 --- a/policy/src/com/android/internal/policy/impl/LockScreen.java +++ b/policy/src/com/android/internal/policy/impl/LockScreen.java @@ -209,7 +209,12 @@ class LockScreen extends LinearLayout implements KeyguardScreen, mSlidingTab.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label : R.string.lockscreen_sound_off_label); } - mCallback.pokeWakelock(); + // Don't poke the wake lock when returning to a state where the handle is + // not grabbed since that can happen when the system (instead of the user) + // cancels the grab. + if (grabbedState != SlidingTab.OnTriggerListener.NO_HANDLE) { + mCallback.pokeWakelock(); + } } } @@ -231,10 +236,11 @@ class LockScreen extends LinearLayout implements KeyguardScreen, /** {@inheritDoc} */ public void onGrabbedStateChange(View v, int grabbedState) { + // Don't poke the wake lock when returning to a state where the handle is + // not grabbed since that can happen when the system (instead of the user) + // cancels the grab. if (grabbedState == WaveView.OnTriggerListener.CENTER_HANDLE) { mCallback.pokeWakelock(STAY_ON_WHILE_GRABBED_TIMEOUT); - } else { - mCallback.pokeWakelock(); } } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 11e1024..e944f9d 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -1252,7 +1252,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: - /* Suppress PLAYPAUSE toggle when phone is ringing or in-call + /* Suppress PLAY/PAUSE toggle when phone is ringing or in-call * to avoid music playback */ if (mTelephonyManager == null) { mTelephonyManager = (TelephonyManager) getContext().getSystemService( @@ -1268,6 +1268,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, event); @@ -1448,6 +1449,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MUTE: case KeyEvent.KEYCODE_MEDIA_PLAY: case KeyEvent.KEYCODE_MEDIA_PAUSE: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: @@ -1455,6 +1457,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); intent.putExtra(Intent.EXTRA_KEY_EVENT, event); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 7f49da9..e950ae5 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -54,6 +54,7 @@ import com.android.internal.telephony.ITelephony; import com.android.internal.view.BaseInputHandler; import com.android.internal.widget.PointerLocationView; +import android.telephony.TelephonyManager; import android.util.Config; import android.util.EventLog; import android.util.Log; @@ -63,6 +64,7 @@ import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.IWindowManager; import android.view.InputChannel; +import android.view.InputDevice; import android.view.InputQueue; import android.view.InputHandler; import android.view.KeyEvent; @@ -217,7 +219,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowState mKeyguard = null; KeyguardViewMediator mKeyguardMediator; GlobalActions mGlobalActions; - boolean mShouldTurnOffOnKeyUp; + volatile boolean mPowerKeyHandled; RecentApplicationsDialog mRecentAppsDialog; Handler mHandler; @@ -477,28 +479,47 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - Runnable mPowerLongPress = new Runnable() { + private void interceptPowerKeyDown(boolean handled) { + mPowerKeyHandled = handled; + if (!handled) { + mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout()); + } + } + + private boolean interceptPowerKeyUp(boolean canceled) { + if (!mPowerKeyHandled) { + mHandler.removeCallbacks(mPowerLongPress); + return !canceled; + } else { + mPowerKeyHandled = true; + return false; + } + } + + private final Runnable mPowerLongPress = new Runnable() { public void run() { - // The context isn't read - if (mLongPressOnPowerBehavior < 0) { - mLongPressOnPowerBehavior = mContext.getResources().getInteger( - com.android.internal.R.integer.config_longPressOnPowerBehavior); - } - switch (mLongPressOnPowerBehavior) { - case LONG_PRESS_POWER_NOTHING: - break; - case LONG_PRESS_POWER_GLOBAL_ACTIONS: - mShouldTurnOffOnKeyUp = false; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); - showGlobalActionsDialog(); - break; - case LONG_PRESS_POWER_SHUT_OFF: - mShouldTurnOffOnKeyUp = false; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); - sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); - ShutdownThread.shutdown(mContext, true); - break; + if (!mPowerKeyHandled) { + // The context isn't read + if (mLongPressOnPowerBehavior < 0) { + mLongPressOnPowerBehavior = mContext.getResources().getInteger( + com.android.internal.R.integer.config_longPressOnPowerBehavior); + } + switch (mLongPressOnPowerBehavior) { + case LONG_PRESS_POWER_NOTHING: + break; + case LONG_PRESS_POWER_GLOBAL_ACTIONS: + mPowerKeyHandled = true; + performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); + showGlobalActionsDialog(); + break; + case LONG_PRESS_POWER_SHUT_OFF: + mPowerKeyHandled = true; + performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false); + sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); + ShutdownThread.shutdown(mContext, true); + break; + } } } }; @@ -1111,12 +1132,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.anim.lock_screen_behind_enter); } - static ITelephony getPhoneInterface() { - return ITelephony.Stub.asInterface(ServiceManager.checkService(Context.TELEPHONY_SERVICE)); + static ITelephony getTelephonyService() { + ITelephony telephonyService = ITelephony.Stub.asInterface( + ServiceManager.checkService(Context.TELEPHONY_SERVICE)); + if (telephonyService == null) { + Log.w(TAG, "Unable to find ITelephony interface."); + } + return telephonyService; } - static IAudioService getAudioInterface() { - return IAudioService.Stub.asInterface(ServiceManager.checkService(Context.AUDIO_SERVICE)); + static IAudioService getAudioService() { + IAudioService audioService = IAudioService.Stub.asInterface( + ServiceManager.checkService(Context.AUDIO_SERVICE)); + if (audioService == null) { + Log.w(TAG, "Unable to find IAudioService interface."); + } + return audioService; } boolean keyguardOn() { @@ -1131,7 +1162,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags, - int keyCode, int metaState, int repeatCount, int policyFlags) { + int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) { final boolean keyguardOn = keyguardOn(); final boolean down = (action == KeyEvent.ACTION_DOWN); final boolean canceled = ((flags & KeyEvent.FLAG_CANCELED) != 0); @@ -1164,11 +1195,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // and his ONLY options are to answer or reject the call.) boolean incomingRinging = false; try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - incomingRinging = phoneServ.isRinging(); - } else { - Log.w(TAG, "Unable to find ITelephony interface"); + ITelephony telephonyService = getTelephonyService(); + if (telephonyService != null) { + incomingRinging = telephonyService.isRinging(); } } catch (RemoteException ex) { Log.w(TAG, "RemoteException from getPhoneInterface()", ex); @@ -1824,23 +1853,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** - * @return Whether a telephone call is in progress right now. - */ - boolean isInCall() { - final ITelephony phone = getPhoneInterface(); - if (phone == null) { - Log.w(TAG, "couldn't get ITelephony reference"); - return false; - } - try { - return phone.isOffhook(); - } catch (RemoteException e) { - Log.w(TAG, "ITelephony.isOffhhook threw RemoteException " + e); - return false; - } - } - - /** * @return Whether music is being played right now. */ boolean isMusicActive() { @@ -1857,9 +1869,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { * @param keycode */ void handleVolumeKey(int stream, int keycode) { - final IAudioService audio = getAudioInterface(); - if (audio == null) { - Log.w(TAG, "handleVolumeKey: couldn't get IAudioService reference"); + IAudioService audioService = getAudioService(); + if (audioService == null) { return; } try { @@ -1867,7 +1878,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // during the call, but we do it as a precaution for the rare possibility // that the music stops right before we call this mBroadcastWakeLock.acquire(); - audio.adjustStreamVolume(stream, + audioService.adjustStreamVolume(stream, keycode == KeyEvent.KEYCODE_VOLUME_UP ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER, @@ -1878,41 +1889,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { mBroadcastWakeLock.release(); } } - - static boolean isMediaKey(int code) { - if (code == KeyEvent.KEYCODE_HEADSETHOOK || - code == KeyEvent.KEYCODE_MEDIA_PLAY || - code == KeyEvent.KEYCODE_MEDIA_PAUSE || - code == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || - code == KeyEvent.KEYCODE_MEDIA_STOP || - code == KeyEvent.KEYCODE_MEDIA_NEXT || - code == KeyEvent.KEYCODE_MEDIA_PREVIOUS || - code == KeyEvent.KEYCODE_MEDIA_REWIND || - code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) { - return true; - } - return false; - } - + /** {@inheritDoc} */ @Override - public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, - int policyFlags, boolean isScreenOn) { - int result = ACTION_PASS_TO_USER; + public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, + int keyCode, int scanCode, int policyFlags, boolean isScreenOn) { + final boolean down = action == KeyEvent.ACTION_DOWN; + final boolean canceled = (flags & KeyEvent.FLAG_CANCELED) != 0; - if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) { - performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); - } - - final boolean isWakeKey = (policyFlags - & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; - - // If the key is injected, pretend that the screen is on and don't let the - // device go to sleep. This feature is mainly used for testing purposes. final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0; - if (isInjected) { - isScreenOn = true; - } // If screen is off then we treat the case where the keyguard is open but hidden // the same as if it were open and in front. @@ -1927,202 +1912,210 @@ public class PhoneWindowManager implements WindowManagerPolicy { + " screenIsOn=" + isScreenOn + " keyguardActive=" + keyguardActive); } - if (keyguardActive) { - if (isScreenOn) { - // when the screen is on, always give the event to the keyguard - result |= ACTION_PASS_TO_USER; - } else { - // otherwise, don't pass it to the user - result &= ~ACTION_PASS_TO_USER; + if (down && (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0) { + performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false); + } - if (isWakeKey && down) { - - // tell the mediator about a wake key, it may decide to - // turn on the screen depending on whether the key is - // appropriate. - if (!mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode) - && (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { - // when keyguard is showing and screen off, we need - // to handle the volume key for calls and music here - if (isInCall()) { - handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); - } else if (isMusicActive()) { - handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); + // Basic policy based on screen state and keyguard. + // FIXME: This policy isn't quite correct. We shouldn't care whether the screen + // is on or off, really. We should care about whether the device is in an + // interactive state or is in suspend pretending to be "off". + // The primary screen might be turned off due to proximity sensor or + // because we are presenting media on an auxiliary screen or remotely controlling + // the device some other way (which is why we have an exemption here for injected + // events). + int result; + if (isScreenOn || isInjected) { + // When the screen is on or if the key is injected pass the key to the application. + result = ACTION_PASS_TO_USER; + } else { + // When the screen is off and the key is not injected, determine whether + // to wake the device but don't pass the key to the application. + result = 0; + + final boolean isWakeKey = (policyFlags + & (WindowManagerPolicy.FLAG_WAKE | WindowManagerPolicy.FLAG_WAKE_DROPPED)) != 0; + if (down && isWakeKey) { + if (keyguardActive) { + // If the keyguard is showing, let it decide what to do with the wake key. + mKeyguardMediator.onWakeKeyWhenKeyguardShowingTq(keyCode); + } else { + // Otherwise, wake the device ourselves. + result |= ACTION_POKE_USER_ACTIVITY; + } + } + } + + // Handle special keys. + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_UP: { + if (down) { + ITelephony telephonyService = getTelephonyService(); + if (telephonyService != null) { + try { + if (telephonyService.isRinging()) { + // If an incoming call is ringing, either VOLUME key means + // "silence ringer". We handle these keys here, rather than + // in the InCallScreen, to make sure we'll respond to them + // even if the InCallScreen hasn't come to the foreground yet. + // Look for the DOWN event here, to agree with the "fallback" + // behavior in the InCallScreen. + Log.i(TAG, "interceptKeyBeforeQueueing:" + + " VOLUME key-down while ringing: Silence ringer!"); + + // Silence the ringer. (It's safe to call this + // even if the ringer has already been silenced.) + telephonyService.silenceRinger(); + + // And *don't* pass this key thru to the current activity + // (which is probably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; + break; + } + if (telephonyService.isOffhook() + && (result & ACTION_PASS_TO_USER) == 0) { + // If we are in call but we decided not to pass the key to + // the application, handle the volume change here. + handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); + break; + } + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); } } + + if (isMusicActive() && (result & ACTION_PASS_TO_USER) == 0) { + // If music is playing but we decided not to pass the key to the + // application, handle the volume change here. + handleVolumeKey(AudioManager.STREAM_MUSIC, keyCode); + break; + } } + break; } - } else if (!isScreenOn) { - // If we are in-call with screen off and keyguard is not showing, - // then handle the volume key ourselves. - // This is necessary because the phone app will disable the keyguard - // when the proximity sensor is in use. - if (isInCall() && - (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN - || keyCode == KeyEvent.KEYCODE_VOLUME_UP)) { - result &= ~ACTION_PASS_TO_USER; - handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode); - } - if (isWakeKey) { - // a wake key has a sole purpose of waking the device; don't pass - // it to the user - result |= ACTION_POKE_USER_ACTIVITY; + + case KeyEvent.KEYCODE_ENDCALL: { result &= ~ACTION_PASS_TO_USER; + if (down) { + ITelephony telephonyService = getTelephonyService(); + boolean hungUp = false; + if (telephonyService != null) { + try { + hungUp = telephonyService.endCall(); + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); + } + } + interceptPowerKeyDown(!isScreenOn || hungUp); + } else { + if (interceptPowerKeyUp(canceled)) { + if ((mEndcallBehavior + & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) { + if (goHome()) { + break; + } + } + if ((mEndcallBehavior + & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) { + result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP; + } + } + } + break; } - } - if (keyCode == KeyEvent.KEYCODE_ENDCALL - || keyCode == KeyEvent.KEYCODE_POWER) { - if (down) { - boolean handled = false; - boolean hungUp = false; - // key repeats are generated by the window manager, and we don't see them - // here, so unless the driver is doing something it shouldn't be, we know - // this is the real press event. - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - try { - if (keyCode == KeyEvent.KEYCODE_ENDCALL) { - handled = hungUp = phoneServ.endCall(); - } else if (keyCode == KeyEvent.KEYCODE_POWER) { - if (phoneServ.isRinging()) { + case KeyEvent.KEYCODE_POWER: { + result &= ~ACTION_PASS_TO_USER; + if (down) { + ITelephony telephonyService = getTelephonyService(); + boolean hungUp = false; + if (telephonyService != null) { + try { + if (telephonyService.isRinging()) { // Pressing Power while there's a ringing incoming // call should silence the ringer. - phoneServ.silenceRinger(); - handled = true; - } else if (phoneServ.isOffhook() && - ((mIncallPowerBehavior - & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) - != 0)) { + telephonyService.silenceRinger(); + } else if ((mIncallPowerBehavior + & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0 + && telephonyService.isOffhook()) { // Otherwise, if "Power button ends call" is enabled, // the Power button will hang up any current active call. - handled = hungUp = phoneServ.endCall(); + hungUp = telephonyService.endCall(); } + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); } - } catch (RemoteException ex) { - Log.w(TAG, "ITelephony threw RemoteException" + ex); } + interceptPowerKeyDown(!isScreenOn || hungUp); } else { - Log.w(TAG, "!!! Unable to find ITelephony interface !!!"); - } - - if (!isScreenOn - || (handled && keyCode != KeyEvent.KEYCODE_POWER) - || (handled && hungUp && keyCode == KeyEvent.KEYCODE_POWER)) { - mShouldTurnOffOnKeyUp = false; - } else { - // Only try to turn off the screen if we didn't already hang up. - mShouldTurnOffOnKeyUp = true; - mHandler.postDelayed(mPowerLongPress, - ViewConfiguration.getGlobalActionKeyTimeout()); - result &= ~ACTION_PASS_TO_USER; - } - } else { - mHandler.removeCallbacks(mPowerLongPress); - if (mShouldTurnOffOnKeyUp) { - mShouldTurnOffOnKeyUp = false; - boolean gohome, sleeps; - if (keyCode == KeyEvent.KEYCODE_ENDCALL) { - gohome = (mEndcallBehavior - & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0; - sleeps = (mEndcallBehavior - & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0; - } else { - gohome = false; - sleeps = true; - } - if (keyguardActive - || (sleeps && !gohome) - || (gohome && !goHome() && sleeps)) { - // They must already be on the keyguard or home screen, - // go to sleep instead unless the event was injected. - if (!isInjected) { - Log.d(TAG, "I'm tired mEndcallBehavior=0x" - + Integer.toHexString(mEndcallBehavior)); - result &= ~ACTION_POKE_USER_ACTIVITY; - result |= ACTION_GO_TO_SLEEP; - } + if (interceptPowerKeyUp(canceled)) { + result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP; } - result &= ~ACTION_PASS_TO_USER; } + break; } - } else if (isMediaKey(keyCode)) { - // This key needs to be handled even if the screen is off. - // If others need to be handled while it's off, this is a reasonable - // pattern to follow. - if ((result & ACTION_PASS_TO_USER) == 0) { - // Only do this if we would otherwise not pass it to the user. In that - // case, the PhoneWindow class will do the same thing, except it will - // only do it if the showing app doesn't process the key on its own. - long when = whenNanos / 1000000; - KeyEvent keyEvent = new KeyEvent(when, when, - down ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP, - keyCode, 0); - mBroadcastWakeLock.acquire(); - mHandler.post(new PassHeadsetKey(keyEvent)); - } - } else if (keyCode == KeyEvent.KEYCODE_CALL) { - // If an incoming call is ringing, answer it! - // (We handle this key here, rather than in the InCallScreen, to make - // sure we'll respond to the key even if the InCallScreen hasn't come to - // the foreground yet.) - - // We answer the call on the DOWN event, to agree with - // the "fallback" behavior in the InCallScreen. - if (down) { - try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - if (phoneServ.isRinging()) { - Log.i(TAG, "interceptKeyTq:" - + " CALL key-down while ringing: Answer the call!"); - phoneServ.answerRingingCall(); - - // And *don't* pass this key thru to the current activity - // (which is presumably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; + + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + if (down) { + ITelephony telephonyService = getTelephonyService(); + if (telephonyService != null) { + try { + if (!telephonyService.isIdle()) { + // Suppress PLAY/PAUSE toggle when phone is ringing or in-call + // to avoid music playback. + break; + } + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); } - } else { - Log.w(TAG, "CALL button: Unable to find ITelephony interface"); } - } catch (RemoteException ex) { - Log.w(TAG, "CALL button: RemoteException from getPhoneInterface()", ex); } + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { + if ((result & ACTION_PASS_TO_USER) == 0) { + // Only do this if we would otherwise not pass it to the user. In that + // case, the PhoneWindow class will do the same thing, except it will + // only do it if the showing app doesn't process the key on its own. + long when = whenNanos / 1000000; + KeyEvent keyEvent = new KeyEvent(when, when, action, keyCode, 0, 0, + 0, scanCode, flags, InputDevice.SOURCE_KEYBOARD); + mBroadcastWakeLock.acquire(); + mHandler.post(new PassHeadsetKey(keyEvent)); + } + break; } - } else if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) - || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { - // If an incoming call is ringing, either VOLUME key means - // "silence ringer". We handle these keys here, rather than - // in the InCallScreen, to make sure we'll respond to them - // even if the InCallScreen hasn't come to the foreground yet. - - // Look for the DOWN event here, to agree with the "fallback" - // behavior in the InCallScreen. - if (down) { - try { - ITelephony phoneServ = getPhoneInterface(); - if (phoneServ != null) { - if (phoneServ.isRinging()) { - Log.i(TAG, "interceptKeyTq:" - + " VOLUME key-down while ringing: Silence ringer!"); - // Silence the ringer. (It's safe to call this - // even if the ringer has already been silenced.) - phoneServ.silenceRinger(); - - // And *don't* pass this key thru to the current activity - // (which is probably the InCallScreen.) - result &= ~ACTION_PASS_TO_USER; + + case KeyEvent.KEYCODE_CALL: { + if (down) { + ITelephony telephonyService = getTelephonyService(); + if (telephonyService != null) { + try { + if (telephonyService.isRinging()) { + Log.i(TAG, "interceptKeyBeforeQueueing:" + + " CALL key-down while ringing: Answer the call!"); + telephonyService.answerRingingCall(); + + // And *don't* pass this key thru to the current activity + // (which is presumably the InCallScreen.) + result &= ~ACTION_PASS_TO_USER; + } + } catch (RemoteException ex) { + Log.w(TAG, "ITelephony threw RemoteException", ex); } - } else { - Log.w(TAG, "VOLUME button: Unable to find ITelephony interface"); } - } catch (RemoteException ex) { - Log.w(TAG, "VOLUME button: RemoteException from getPhoneInterface()", ex); } + break; } } - return result; } diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java index cb4071a..e7eb129 100644 --- a/services/java/com/android/server/InputManager.java +++ b/services/java/com/android/server/InputManager.java @@ -404,17 +404,18 @@ public class InputManager { } @SuppressWarnings("unused") - public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, - int policyFlags, boolean isScreenOn) { + public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, + int keyCode, int scanCode, int policyFlags, boolean isScreenOn) { return mWindowManagerService.mInputMonitor.interceptKeyBeforeQueueing( - whenNanos, keyCode, down, policyFlags, isScreenOn); + whenNanos, action, flags, keyCode, scanCode, policyFlags, isScreenOn); } @SuppressWarnings("unused") public boolean interceptKeyBeforeDispatching(InputChannel focus, int action, - int flags, int keyCode, int metaState, int repeatCount, int policyFlags) { + int flags, int keyCode, int scanCode, int metaState, int repeatCount, + int policyFlags) { return mWindowManagerService.mInputMonitor.interceptKeyBeforeDispatching(focus, - action, flags, keyCode, metaState, repeatCount, policyFlags); + action, flags, keyCode, scanCode, metaState, repeatCount, policyFlags); } @SuppressWarnings("unused") diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index c29e4a9..55ebded 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -5706,20 +5706,20 @@ public class WindowManagerService extends IWindowManager.Stub /* Provides an opportunity for the window manager policy to intercept early key * processing as soon as the key has been read from the device. */ - public int interceptKeyBeforeQueueing(long whenNanos, int keyCode, boolean down, - int policyFlags, boolean isScreenOn) { - return mPolicy.interceptKeyBeforeQueueing(whenNanos, - keyCode, down, policyFlags, isScreenOn); + public int interceptKeyBeforeQueueing(long whenNanos, int action, int flags, + int keyCode, int scanCode, int policyFlags, boolean isScreenOn) { + return mPolicy.interceptKeyBeforeQueueing(whenNanos, action, flags, + keyCode, scanCode, policyFlags, isScreenOn); } /* Provides an opportunity for the window manager policy to process a key before * ordinary dispatch. */ public boolean interceptKeyBeforeDispatching(InputChannel focus, - int action, int flags, int keyCode, int metaState, int repeatCount, + int action, int flags, int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) { WindowState windowState = getWindowStateForInputChannel(focus); return mPolicy.interceptKeyBeforeDispatching(windowState, action, flags, - keyCode, metaState, repeatCount, policyFlags); + keyCode, scanCode, metaState, repeatCount, policyFlags); } /* Called when the current input focus changes. diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 599163b..d4c4ba4 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -857,7 +857,7 @@ void NativeInputManager::interceptKeyBeforeQueueing(nsecs_t when, JNIEnv* env = jniEnv(); jint wmActions = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.interceptKeyBeforeQueueing, - when, keyCode, action == AKEY_EVENT_ACTION_DOWN, policyFlags, isScreenOn); + when, action, flags, keyCode, scanCode, policyFlags, isScreenOn); if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { wmActions = 0; } @@ -926,7 +926,7 @@ bool NativeInputManager::interceptKeyBeforeDispatching(const sp<InputChannel>& i jboolean consumed = env->CallBooleanMethod(mCallbacksObj, gCallbacksClassInfo.interceptKeyBeforeDispatching, inputChannelObj, keyEvent->getAction(), keyEvent->getFlags(), - keyEvent->getKeyCode(), keyEvent->getMetaState(), + keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), keyEvent->getRepeatCount(), policyFlags); bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching"); @@ -1358,10 +1358,10 @@ int register_android_server_InputManager(JNIEnv* env) { "notifyANR", "(Ljava/lang/Object;Landroid/view/InputChannel;)J"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeQueueing, gCallbacksClassInfo.clazz, - "interceptKeyBeforeQueueing", "(JIZIZ)I"); + "interceptKeyBeforeQueueing", "(JIIIIIZ)I"); GET_METHOD_ID(gCallbacksClassInfo.interceptKeyBeforeDispatching, gCallbacksClassInfo.clazz, - "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIII)Z"); + "interceptKeyBeforeDispatching", "(Landroid/view/InputChannel;IIIIIII)Z"); GET_METHOD_ID(gCallbacksClassInfo.checkInjectEventsPermission, gCallbacksClassInfo.clazz, "checkInjectEventsPermission", "(II)Z"); diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 7a1587b..3c4bb12 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -66,7 +66,7 @@ interface ITelephony { boolean showCallScreenWithDialpad(boolean showDialpad); /** - * End call or go to the Home screen + * End call if there is a call in progress, otherwise does nothing. * * @return whether it hung up */ |
