From b1b9a8ac07ea7d438eda613f4c798dd8b10a66ce Mon Sep 17 00:00:00 2001 From: Brian Colonna Date: Fri, 29 Mar 2013 11:52:42 -0400 Subject: [PATCH] FUL now restarts when flipping tablet 180 (bug 7484464) When a tablet rotates, FUL must be stopped and restarted in a new position. 90 degree rotations cause a configuration change, causing FUL to be automatically reconstructed in the new location. However, a 180 degree rotation is not a configuration change, so FUL was not restarting. A 180 degree rotation happens more often than one might think. If you set the tablet down and later picked it up in the opposite orientation, FUL would not work prior to this fix. This change adds a rotation watcher to KeyguardFaceUnlockView. It watches for 180 degree rotations and stops and restarts FUL accordingly. The rotation watcher callback must be unregistered when KeyguardFaceUnlockView is recreated (as during 90 degree rotation changes), otherwise the number of rotation watcher callbacks will keep growing and they will never go away. This is a problem not just because there are many callbacks hanging around, but also because the old callbacks end up trying to access biometric unlock views that no longer exist, resulting in crashes. So, a simple function was added to the window manager to unregister a rotation watcher. Change-Id: Ie1ef20a9a22b8f4e39918987dff2b8ad444fcfd1 --- core/java/android/view/IWindowManager.aidl | 6 ++ .../impl/keyguard/KeyguardFaceUnlockView.java | 68 ++++++++++++++++++- .../server/wm/WindowManagerService.java | 13 ++++ .../src/android/view/IWindowManagerImpl.java | 4 ++ 4 files changed, 88 insertions(+), 3 deletions(-) diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index a85a558fa3640..8ed4a86e9b60a 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -174,6 +174,12 @@ interface IWindowManager */ int watchRotation(IRotationWatcher watcher); + /** + * Remove a rotation watcher set using watchRotation. + * @hide + */ + void removeRotationWatcher(IRotationWatcher watcher); + /** * Determine the preferred edge of the screen to pin the compact options menu against. * @return a Gravity value for the options menu panel diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java index 4df434c7895c0..965e3786439f8 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardFaceUnlockView.java @@ -18,17 +18,22 @@ package com.android.internal.policy.impl.keyguard; import android.content.Context; import android.graphics.drawable.Drawable; import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; +import android.view.IRotationWatcher; +import android.view.IWindowManager; import android.view.View; import android.widget.ImageButton; import android.widget.LinearLayout; import com.android.internal.R; - import com.android.internal.widget.LockPatternUtils; +import java.lang.Math; + public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecurityView { private static final String TAG = "FULKeyguardFaceUnlockView"; @@ -45,6 +50,30 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu private boolean mIsShowing = false; private final Object mIsShowingLock = new Object(); + private int mLastRotation; + private boolean mWatchingRotation; + private final IWindowManager mWindowManager = + IWindowManager.Stub.asInterface(ServiceManager.getService("window")); + + private final IRotationWatcher mRotationWatcher = new IRotationWatcher.Stub() { + public void onRotationChanged(int rotation) { + if (DEBUG) Log.d(TAG, "onRotationChanged(): " + mLastRotation + "->" + rotation); + + // If the difference between the new rotation value and the previous rotation value is + // equal to 2, the rotation change was 180 degrees. This stops the biometric unlock + // and starts it in the new position. This is not performed for 90 degree rotations + // since a 90 degree rotation is a configuration change, which takes care of this for + // us. + if (Math.abs(rotation - mLastRotation) == 2) { + if (mBiometricUnlock != null) { + mBiometricUnlock.stop(); + maybeStartBiometricUnlock(); + } + } + mLastRotation = rotation; + } + }; + public KeyguardFaceUnlockView(Context context) { this(context, null); } @@ -91,6 +120,14 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu mBiometricUnlock.stop(); } KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback); + if (mWatchingRotation) { + try { + mWindowManager.removeRotationWatcher(mRotationWatcher); + mWatchingRotation = false; + } catch (RemoteException e) { + Log.e(TAG, "Remote exception when removing rotation watcher"); + } + } } @Override @@ -100,6 +137,14 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu mBiometricUnlock.stop(); } KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateCallback); + if (mWatchingRotation) { + try { + mWindowManager.removeRotationWatcher(mRotationWatcher); + mWatchingRotation = false; + } catch (RemoteException e) { + Log.e(TAG, "Remote exception when removing rotation watcher"); + } + } } @Override @@ -108,6 +153,17 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu mIsShowing = KeyguardUpdateMonitor.getInstance(mContext).isKeyguardVisible(); maybeStartBiometricUnlock(); KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback); + + // Registers a callback which handles stopping the biometric unlock and restarting it in + // the new position for a 180 degree rotation change. + if (!mWatchingRotation) { + try { + mLastRotation = mWindowManager.watchRotation(mRotationWatcher); + mWatchingRotation = true; + } catch (RemoteException e) { + Log.e(TAG, "Remote exception when adding rotation watcher"); + } + } } @Override @@ -172,9 +228,15 @@ public class KeyguardFaceUnlockView extends LinearLayout implements KeyguardSecu return; } - // TODO: Some of these conditions are handled in KeyguardSecurityModel and may not be - // necessary here. + // Although these same conditions are handled in KeyguardSecurityModel, they are still + // necessary here. When a tablet is rotated 90 degrees, a configuration change is + // triggered and everything is torn down and reconstructed. That means + // KeyguardSecurityModel gets a chance to take care of the logic and doesn't even + // reconstruct KeyguardFaceUnlockView if the biometric unlock should be suppressed. + // However, for a 180 degree rotation, no configuration change is triggered, so only + // the logic here is capable of suppressing Face Unlock. if (monitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE + && monitor.isAlternateUnlockEnabled() && !monitor.getMaxBiometricUnlockAttemptsReached() && !backupIsTimedOut) { mBiometricUnlock.start(); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 34052f34e18a0..cbc42ebe3a2b0 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5803,6 +5803,19 @@ public class WindowManagerService extends IWindowManager.Stub } } + @Override + public void removeRotationWatcher(IRotationWatcher watcher) { + final IBinder watcherBinder = watcher.asBinder(); + synchronized (mWindowMap) { + for (int i=0; i