From eec83dcadd1dca722d3070fe5eae7d4e0561aa2b Mon Sep 17 00:00:00 2001 From: Sandeep Siddhartha Date: Wed, 4 Sep 2013 14:59:59 -0700 Subject: [PATCH] Move the hotword code to KeyguardHostView - Try hotword detection on Pattern unlock screen as well - disabled in this CL - Explicitly stop hotword detection when starting the assist intent. Change-Id: I3d22a9029abf888431113b86e4410ea9e9866c57 --- .../src/com/android/keyguard/KeyguardHostView.java | 191 +++++++++++++++++++++ .../com/android/keyguard/KeyguardSelectorView.java | 133 -------------- 2 files changed, 191 insertions(+), 133 deletions(-) diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java index eedb7d06e8fe..00124b0563fc 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java @@ -24,6 +24,7 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.AlertDialog; +import android.app.PendingIntent; import android.app.SearchManager; import android.app.admin.DevicePolicyManager; import android.appwidget.AppWidgetHost; @@ -40,6 +41,7 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; import android.media.RemoteControlClient; +import android.os.Bundle; import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; @@ -47,6 +49,9 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.speech.hotword.HotwordRecognitionListener; +import android.speech.hotword.HotwordRecognizer; +import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; import android.util.Slog; @@ -63,6 +68,11 @@ import java.util.List; public class KeyguardHostView extends KeyguardViewBase { private static final String TAG = "KeyguardHostView"; + // Don't enable hotword on limited-memory devices. + private static final boolean ENABLE_HOTWORD = !ActivityManager.isLowRamDeviceStatic(); + // Indicates if hotword is enabled, should it also be available on secure keyguard(s). + private static final boolean ENABLE_HOTWORD_SECURE = false; + // Transport control states. static final int TRANSPORT_GONE = 0; static final int TRANSPORT_INVISIBLE = 1; @@ -77,6 +87,13 @@ public class KeyguardHostView extends KeyguardViewBase { // Found in KeyguardAppWidgetPickActivity.java static final int APPWIDGET_HOST_ID = 0x4B455947; + // TODO: Fix this to be non-static. + // We need to be careful here to make stopRecognition calls on the same instance + // that started it. Since KeyguardHostView is a view, it keeps getting + // recreated every now and then, and unless we figure out a better way, + // this needs to be a static field. + private static HotwordRecognizer sHotwordClient; + private final int MAX_WIDGETS = 5; private AppWidgetHost mAppWidgetHost; @@ -117,6 +134,8 @@ public class KeyguardHostView extends KeyguardViewBase { private KeyguardMultiUserSelectorView mKeyguardMultiUserSelectorView; + private boolean mIsScreenOn; + protected int mClientGeneration; protected boolean mShowSecurityWhenReturn; @@ -198,6 +217,11 @@ public class KeyguardHostView extends KeyguardViewBase { if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) { Log.v(TAG, "Keyguard secure camera disabled by DPM"); } + + // Create Hotword recognizer, for the first time. + if (ENABLE_HOTWORD && sHotwordClient == null) { + sHotwordClient = HotwordRecognizer.createHotwordRecognizer(getContext()); + } } private void getInitialTransportState() { @@ -295,6 +319,21 @@ public class KeyguardHostView extends KeyguardViewBase { } } } + @Override + public void onPhoneStateChanged(int phoneState) { + // We need to stop hotword detection when a call state is not idle anymore. + if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection) + && TelephonyManager.CALL_STATE_IDLE != phoneState) { + if (DEBUG) Log.d(TAG, "Stopping due to call state not being idle"); + maybeStopHotwordDetector(); + } + } + @Override + public void onUserSwitching(int userId) { + if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) { + maybeStopHotwordDetector(); + } + } }; private static final boolean isMusicPlaying(int playbackState) { @@ -778,6 +817,9 @@ public class KeyguardHostView extends KeyguardViewBase { // If the alternate unlock was suppressed, it can now be safely // enabled because the user has left keyguard. KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); + if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)){ + maybeStopHotwordDetector(); + } // If there's a pending runnable because the user interacted with a widget // and we're leaving keyguard, then run it. @@ -942,6 +984,9 @@ public class KeyguardHostView extends KeyguardViewBase { // Emulate Activity life cycle if (oldView != null) { + if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) { + maybeStopHotwordDetector(); + } oldView.onPause(); oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view } @@ -982,6 +1027,7 @@ public class KeyguardHostView extends KeyguardViewBase { @Override public void onScreenTurnedOn() { if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); + mIsScreenOn = true; showPrimarySecurityScreen(false); getSecurityView(mCurrentSecuritySelection).onResume(KeyguardSecurityView.SCREEN_ON); @@ -993,6 +1039,12 @@ public class KeyguardHostView extends KeyguardViewBase { if (mViewStateManager != null) { mViewStateManager.showUsabilityHints(); } + + // Start hotword detection on insecure Keyguard. + if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) { + maybeStartHotwordDetector(); + } + requestFocus(); } @@ -1000,6 +1052,7 @@ public class KeyguardHostView extends KeyguardViewBase { public void onScreenTurnedOff() { if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s", Integer.toHexString(hashCode()), SystemClock.uptimeMillis())); + mIsScreenOn = false; // Once the screen turns off, we no longer consider this to be first boot and we want the // biometric unlock to start next time keyguard is shown. KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); @@ -1013,6 +1066,12 @@ public class KeyguardHostView extends KeyguardViewBase { if (cameraPage != null) { cameraPage.onScreenTurnedOff(); } + + // Stop hotword detection on insecure Keyguard. + if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) { + maybeStopHotwordDetector(); + } + clearFocus(); } @@ -1097,6 +1156,9 @@ public class KeyguardHostView extends KeyguardViewBase { new CameraWidgetFrame.Callbacks() { @Override public void onLaunchingCamera() { + if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) { + maybeStopHotwordDetector(); + } setSliderHandleAlpha(0); } @@ -1626,6 +1688,9 @@ public class KeyguardHostView extends KeyguardViewBase { } public void showAssistant() { + if (shouldRunHotwordInSecurityMode(mCurrentSecuritySelection)) { + maybeStopHotwordDetector(); + } final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) .getAssistIntent(mContext, true, UserHandle.USER_CURRENT); @@ -1640,4 +1705,130 @@ public class KeyguardHostView extends KeyguardViewBase { mActivityLauncher.launchActivityWithAnimation( intent, false, opts.toBundle(), null, null); } + + + /** + * Start the hotword detector if: + *
  • ENABLE_HOTWORD is true and + *
  • Hotword detection is not already running and + *
  • TelephonyManager is in CALL_STATE_IDLE + *
  • and Screen is turned on. + */ + private void maybeStartHotwordDetector() { + if (!ENABLE_HOTWORD) return; + + if (sHotwordClient != null) { + if (DEBUG) Log.d(TAG, "maybeStartHotwordDetector()"); + // Don't start hotword detection if the screen is off. + if (!mIsScreenOn) { + if (DEBUG) Log.d(TAG, "screen was off, not starting"); + return; + } + + KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext()); + if (monitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE) { + if (DEBUG) Log.d(TAG, "Call underway, not starting"); + return; + } + + try { + sHotwordClient.startRecognition(mHotwordCallback); + } catch(Exception ex) { + // Don't allow hotword errors to make the keyguard unusable + Log.e(TAG, "Failed to start hotword recognition", ex); + sHotwordClient = null; + } + } + } + + /** + * Stop hotword detector if: + *
  • ENABLE_HOTWORD is true + *
  • and hotword is running. + */ + private void maybeStopHotwordDetector() { + if (!ENABLE_HOTWORD) return; + + if (sHotwordClient != null) { + if (DEBUG) Log.d(TAG, "maybeStopHotwordDetector()"); + try { + sHotwordClient.stopRecognition(); + } catch(Exception ex) { + // Don't allow hotword errors to make the keyguard unusable + Log.e(TAG, "Failed to start hotword recognition", ex); + } finally { + sHotwordClient = null; + } + } + } + + private final HotwordRecognitionListener mHotwordCallback = new HotwordRecognitionListener() { + private static final String TAG = "HotwordRecognitionListener"; + + public void onHotwordRecognitionStarted() { + if (DEBUG) Log.d(TAG, "onHotwordRecognitionStarted()"); + } + + public void onHotwordRecognitionStopped() { + if (DEBUG) Log.d(TAG, "onHotwordRecognitionStopped()"); + } + + public void onHotwordEvent(int eventType, Bundle eventBundle) { + if (DEBUG) Log.d(TAG, "onHotwordEvent: " + eventType); + if (eventType == HotwordRecognizer.EVENT_TYPE_STATE_CHANGED) { + if (eventBundle != null && eventBundle.containsKey(HotwordRecognizer.PROMPT_TEXT)) { + new KeyguardMessageArea.Helper( + (View) getSecurityView(mCurrentSecuritySelection)) + .setMessage(eventBundle.getString(HotwordRecognizer.PROMPT_TEXT),true); + } + } + } + + public void onHotwordRecognized(final PendingIntent intent) { + if (DEBUG) Log.d(TAG, "onHotwordRecognized"); + maybeStopHotwordDetector(); + if (SecurityMode.None == mCurrentSecuritySelection) { + if (intent != null) { + try { + intent.send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Failed to launch PendingIntent. Encountered CanceledException"); + } + } + mCallback.userActivity(0); + mCallback.dismiss(false); + } else if (ENABLE_HOTWORD_SECURE && mLockPatternUtils.isSecure()) { + setOnDismissAction(new OnDismissAction() { + @Override + public boolean onDismiss() { + if (intent != null) { + try { + intent.send(); + } catch (PendingIntent.CanceledException e) { + Log.w(TAG, "Failed to launch PendingIntent." + + "Encountered CanceledException"); + } + } + return false; + } + }); + getSecurityView(mCurrentSecuritySelection).showBouncer(0); + } + } + + public void onHotwordError(int errorCode) { + if (DEBUG) Log.d(TAG, "onHotwordError: " + errorCode); + // TODO: Inspect the error code and handle the errors appropriately + // instead of blindly failing. + maybeStopHotwordDetector(); + } + }; + + private boolean shouldRunHotwordInSecurityMode(SecurityMode mode) { + // Enable hotoword for insecure keyguard, + // and for pattern unlock if ENABLE_HOTWORD_SECURE is true. + return ENABLE_HOTWORD + && ((SecurityMode.None == mode) + || (ENABLE_HOTWORD_SECURE && mLockPatternUtils.isSecure())); + } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java index 40d55cf479e6..63be102451a1 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java @@ -28,8 +28,6 @@ import android.os.Bundle; import android.os.PowerManager; import android.os.UserHandle; import android.provider.Settings; -import android.speech.hotword.HotwordRecognitionListener; -import android.speech.hotword.HotwordRecognizer; import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; @@ -48,12 +46,6 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri private static final String ASSIST_ICON_METADATA_NAME = "com.android.systemui.action_assist_icon"; - // Don't enable hotword on limited-memory devices. - private static final boolean ENABLE_HOTWORD = !ActivityManager.isLowRamDeviceStatic(); - - // TODO: Fix this to be non-static. - private static HotwordRecognizer sHotwordClient; - private KeyguardSecurityCallback mCallback; private GlowPadView mGlowPadView; private ObjectAnimator mAnim; @@ -69,7 +61,6 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri public void onTrigger(View v, int target) { final int resId = mGlowPadView.getResourceIdForTarget(target); - maybeStopHotwordDetector(); switch (resId) { case R.drawable.ic_action_assist_generic: @@ -129,22 +120,6 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri public void onSimStateChanged(State simState) { updateTargets(); } - - @Override - public void onPhoneStateChanged(int phoneState) { - if (ENABLE_HOTWORD) { - // We need to stop hotword detection when a call state is not idle anymore. - if (phoneState != TelephonyManager.CALL_STATE_IDLE) { - if (DEBUG) Log.d(TAG, "Stopping due to call state not being idle"); - maybeStopHotwordDetector(); - } - } - } - - @Override - public void onUserSwitching(int userId) { - maybeStopHotwordDetector(); - } }; private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() { @@ -183,9 +158,6 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); View bouncerFrameView = findViewById(R.id.keyguard_selector_view_frame); mBouncerFrame = bouncerFrameView.getBackground(); - if (ENABLE_HOTWORD && sHotwordClient == null) { - sHotwordClient = HotwordRecognizer.createHotwordRecognizer(getContext()); - } } public void setCarrierArea(View carrierArea) { @@ -289,20 +261,11 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri @Override public void onPause() { KeyguardUpdateMonitor.getInstance(getContext()).removeCallback(mUpdateCallback); - maybeStopHotwordDetector(); } @Override public void onResume(int reason) { KeyguardUpdateMonitor.getInstance(getContext()).registerCallback(mUpdateCallback); - // TODO: Figure out if there's a better way to do it. - // onResume gets called multiple times, however we are interested in - // the reason to figure out when to start/stop hotword detection. - if (reason == SCREEN_ON) { - if (!KeyguardUpdateMonitor.getInstance(getContext()).isSwitchingUser()) { - maybeStartHotwordDetector(); - } - } } @Override @@ -323,100 +286,4 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri KeyguardSecurityViewHelper. hideBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration); } - - /** - * Start the hotword detector if: - *
  • FLAG_HOTWORD is true and - *
  • Hotword detection is not already running and - *
  • TelephonyManager is in CALL_STATE_IDLE - * - * If this method is called when the screen is off, - * it attempts to stop hotword detection if it's running. - */ - private void maybeStartHotwordDetector() { - if (ENABLE_HOTWORD && sHotwordClient != null) { - if (DEBUG) Log.d(TAG, "maybeStartHotwordDetector()"); - // Don't start it if the screen is off or not showing - PowerManager powerManager = (PowerManager) getContext().getSystemService( - Context.POWER_SERVICE); - if (!powerManager.isScreenOn()) { - if (DEBUG) Log.d(TAG, "screen was off, not starting"); - return; - } - - KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(getContext()); - if (monitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE) { - if (DEBUG) Log.d(TAG, "Call underway, not starting"); - return; - } - - try { - sHotwordClient.startRecognition(mHotwordCallback); - } catch(Exception ex) { - // Don't allow hotword errors to make the keyguard unusable - Log.e(TAG, "Failed to start hotword recognition", ex); - sHotwordClient = null; - } - } - } - - /** - * Stop hotword detector if HOTWORDING_ENABLED is true. - */ - private void maybeStopHotwordDetector() { - if (ENABLE_HOTWORD && sHotwordClient != null) { - if (DEBUG) Log.d(TAG, "maybeStopHotwordDetector()"); - try { - sHotwordClient.stopRecognition(); - } catch(Exception ex) { - // Don't allow hotword errors to make the keyguard unusable - Log.e(TAG, "Failed to start hotword recognition", ex); - } finally { - sHotwordClient = null; - } - } - } - - private final HotwordRecognitionListener mHotwordCallback = new HotwordRecognitionListener() { - private static final String TAG = "HotwordRecognitionListener"; - - public void onHotwordRecognitionStarted() { - if (DEBUG) Log.d(TAG, "onHotwordRecognitionStarted()"); - } - - public void onHotwordRecognitionStopped() { - if (DEBUG) Log.d(TAG, "onHotwordRecognitionStopped()"); - } - - public void onHotwordEvent(int eventType, Bundle eventBundle) { - if (DEBUG) Log.d(TAG, "onHotwordEvent: " + eventType); - if (eventType == HotwordRecognizer.EVENT_TYPE_STATE_CHANGED) { - if (eventBundle != null && eventBundle.containsKey(HotwordRecognizer.PROMPT_TEXT)) { - mSecurityMessageDisplay.setMessage( - eventBundle.getString(HotwordRecognizer.PROMPT_TEXT), true); - } - } - } - - public void onHotwordRecognized(PendingIntent intent) { - if (DEBUG) Log.d(TAG, "onHotwordRecognized"); - maybeStopHotwordDetector(); - if (intent != null) { - try { - intent.send(); - } catch (PendingIntent.CanceledException e) { - Log.w(TAG, "Failed to launch PendingIntent. Encountered CanceledException"); - } - } - mCallback.userActivity(0); - mCallback.dismiss(false); - } - - public void onHotwordError(int errorCode) { - if (DEBUG) Log.d(TAG, "onHotwordError: " + errorCode); - // TODO: Inspect the error code and handle the errors appropriately - // instead of blindly failing. - maybeStopHotwordDetector(); - } - }; } -- 2.11.0