diff options
author | Todd Gurzick <toddgurz@gmail.com> | 2011-09-28 15:00:54 -0400 |
---|---|---|
committer | Emilio López <turl@tuxfamily.org> | 2012-02-26 02:25:24 +0000 |
commit | 3496383906750b7ca928078e60fcbc49097b708e (patch) | |
tree | b55fd338db2f0b52a1f5ad7fa359be226cd31a94 | |
parent | 058717133f46e6f1729ed5b46cfdfefe774dee54 (diff) | |
download | android_frameworks_base-3496383906750b7ca928078e60fcbc49097b708e.tar.gz android_frameworks_base-3496383906750b7ca928078e60fcbc49097b708e.tar.bz2 android_frameworks_base-3496383906750b7ca928078e60fcbc49097b708e.zip |
Implement basic framework for fingerprint lockscreen
Uses reflection not to depend on proprietary jars
Change-Id: I86acf878ff51a14b3ca7d9962b62305f010e0b29
23 files changed, 2594 insertions, 50 deletions
diff --git a/Android.mk b/Android.mk index d707fa92740..e2044761193 100644 --- a/Android.mk +++ b/Android.mk @@ -194,7 +194,8 @@ LOCAL_SRC_FILES += \ vpn/java/android/net/vpn/IVpnService.aidl \ voip/java/android/net/sip/ISipSession.aidl \ voip/java/android/net/sip/ISipSessionListener.aidl \ - voip/java/android/net/sip/ISipService.aidl + voip/java/android/net/sip/ISipService.aidl \ + core/java/com/authentec/TrueSuiteMobile/RelayReceiverService.aidl # diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 570351d6057..bac2f87cbec 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -190,7 +190,14 @@ public class DevicePolicyManager { * are ordered so that higher values are more restrictive. */ public static final int PASSWORD_QUALITY_SOMETHING = 0x10000; - + + /** + * Constant for {@link #setPasswordQuality}: the user must swipe a finger + * to unlock the screen. Note that quality constants are + * ordered so that higher values are more restrictive. + */ + public static final int PASSWORD_QUALITY_FINGER = 0x1FFFF; + /** * Constant for {@link #setPasswordQuality}: the user must have entered a * password containing at least numeric characters. Note that quality @@ -418,6 +425,12 @@ public class DevicePolicyManager { * the password again until the user has entered it. */ public static final int RESET_PASSWORD_REQUIRE_ENTRY = 0x0001; + + /** + * Flag for {@link #resetPassword}: Enable/Disable finger lock + */ + public static final int ENABLE_FINGER_LOCK = 0x1000; + public static final int DISABLE_FINGER_LOCK = 0x1001; /** * Force a new device unlock password (the password needed to access the diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 668b0298120..cbd13175587 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -101,8 +101,9 @@ public class LockPatternUtils { private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; - public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; private final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; + private final static String LOCK_FINGER_ENABLED = "lockscreen.lockfingerenabled"; + public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; private final Context mContext; private final ContentResolver mContentResolver; @@ -251,6 +252,14 @@ public class LockPatternUtils { } /** + * Check to see if the user has stored a finger. + * @return Whether a saved finger exists. + */ + public boolean savedFingerExists() { + return true; + } + + /** * Return true if the user has ever chosen a pattern. This is true even if the pattern is * currently cleared. * @@ -287,6 +296,11 @@ public class LockPatternUtils { activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; } break; + case DevicePolicyManager.PASSWORD_QUALITY_FINGER: + if (isLockFingerEnabled()) { + activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_FINGER; + } + break; } return activePasswordQuality; } @@ -560,6 +574,15 @@ public class LockPatternUtils { } /** + * @return Whether the lock finger is enabled. + */ + public boolean isLockFingerEnabled() { + return getBoolean(LOCK_FINGER_ENABLED) + && getLong(PASSWORD_TYPE_KEY, 0) + == DevicePolicyManager.PASSWORD_QUALITY_FINGER; + } + + /** * Set whether the lock pattern is enabled. */ public void setLockPatternEnabled(boolean enabled) { @@ -567,6 +590,18 @@ public class LockPatternUtils { } /** + * Set whether the lock finger is enabled. + */ + public void setLockFingerEnabled(boolean enabled) { + setBoolean(LOCK_FINGER_ENABLED, enabled); + if (enabled) { + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_FINGER); + } else { + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + } + } + + /** * @return Whether the visible pattern is enabled. */ public boolean isVisiblePatternEnabled() { @@ -593,59 +628,59 @@ public class LockPatternUtils { public void setTactileFeedbackEnabled(boolean enabled) { setBoolean(Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, enabled); } - + public void setVisibleDotsEnabled(boolean enabled) { - setBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, enabled); + setBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, enabled); } - + public boolean isVisibleDotsEnabled() { return getBoolean(Settings.Secure.LOCK_DOTS_VISIBLE, true); } - + public void setShowErrorPath(boolean enabled) { - setBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, enabled); + setBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, enabled); } - + public boolean isShowErrorPath() { return getBoolean(Settings.Secure.LOCK_SHOW_ERROR_PATH, true); } - + public void setShowCustomMsg(boolean enabled) { setBoolean(Settings.Secure.LOCK_SHOW_CUSTOM_MSG, enabled); } - + public boolean isShowCustomMsg() { return getBoolean(Settings.Secure.LOCK_SHOW_CUSTOM_MSG, false); } - + public void setCustomMsg(String msg) { setString(Settings.Secure.LOCK_CUSTOM_MSG, msg); } - + public String getCustomMsg() { return getString(Settings.Secure.LOCK_CUSTOM_MSG); } - + public int getIncorrectDelay() { return getInt(Settings.Secure.LOCK_INCORRECT_DELAY, 2000); } - + public void setIncorrectDelay(int delay) { setInt(Settings.Secure.LOCK_INCORRECT_DELAY, delay); } - + public void setShowUnlockMsg(boolean enabled) { setBoolean(Settings.Secure.SHOW_UNLOCK_TEXT, enabled); } - + public boolean isShowUnlockMsg() { return getBoolean(Settings.Secure.SHOW_UNLOCK_TEXT, true); } - + public void setShowUnlockErrMsg(boolean enabled) { setBoolean(Settings.Secure.SHOW_UNLOCK_ERR_TEXT, enabled); } - + public boolean isShowUnlockErrMsg() { return getBoolean(Settings.Secure.SHOW_UNLOCK_ERR_TEXT, true); } @@ -852,7 +887,7 @@ public class LockPatternUtils { return 1 == android.provider.Settings.Secure.getInt(mContentResolver, secureSettingKey, 0); } - + private boolean getBoolean(String systemSettingKey, boolean defaultValue) { return 1 == android.provider.Settings.Secure.getInt( @@ -872,7 +907,7 @@ public class LockPatternUtils { private void setLong(String secureSettingKey, long value) { android.provider.Settings.Secure.putLong(mContentResolver, secureSettingKey, value); } - + private int getInt(String systemSettingKey, int def) { return android.provider.Settings.Secure.getInt(mContentResolver, systemSettingKey, def); } @@ -880,16 +915,16 @@ public class LockPatternUtils { private void setInt(String systemSettingKey, int value) { android.provider.Settings.Secure.putInt(mContentResolver, systemSettingKey, value); } - + private String getString(String systemSettingKey) { String s = android.provider.Settings.Secure.getString(mContentResolver, systemSettingKey); - + if (s == null) return ""; - + return s; } - + private void setString(String systemSettingKey, String value) { android.provider.Settings.Secure.putString(mContentResolver, systemSettingKey, value); } @@ -897,10 +932,12 @@ public class LockPatternUtils { public boolean isSecure() { long mode = getKeyguardStoredPasswordQuality(); final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; + final boolean isFinger = mode == DevicePolicyManager.PASSWORD_QUALITY_FINGER; final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists() + || isFinger && isLockFingerEnabled() && savedFingerExists() || isPassword && savedPasswordExists(); return secure; } diff --git a/core/java/com/authentec/AuthentecHelper.java b/core/java/com/authentec/AuthentecHelper.java new file mode 100644 index 00000000000..7a5b17b086e --- /dev/null +++ b/core/java/com/authentec/AuthentecHelper.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2011 The CyanogenMOD Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.authentec; + +import dalvik.system.DexClassLoader; + +import android.content.Context; +import android.content.ContextWrapper; + +import android.os.SystemProperties; + +import java.lang.reflect.Constructor; + +public class AuthentecHelper +{ + private static DexClassLoader sAuthentecClassLoader = null; + private Class TSM = null; + private Class AuthentecMobile = null; + private Object am = null; + + /* AM_STATUS codes */ + public static final int eAM_STATUS_ACCESS_ERROR = 6; + public static final int eAM_STATUS_APPLICATION_IO_ERROR = 11; + public static final int eAM_STATUS_CAPTURE_FAILED = 15; + public static final int eAM_STATUS_CLIENT_CANCELED = 17; + public static final int eAM_STATUS_CLIENT_NOT_PERMITTED = 18; + public static final int eAM_STATUS_CREDENTIAL_LOCKED = 3; + public static final int eAM_STATUS_CREDENTIAL_TOO_LARGE = 5; + public static final int eAM_STATUS_DATABASE_FULL = 7; + public static final int eAM_STATUS_FINGERS_NOT_PROVISIONED = 19; + public static final int eAM_STATUS_FOREIGN_DATABASE = 8; + public static final int eAM_STATUS_GUI_IS_OFFLINE = 21; + public static final int eAM_STATUS_INVALID_APP_CONTEXT = 10; + public static final int eAM_STATUS_INVALID_PARAMETER = 4; + public static final int eAM_STATUS_INVALID_USER_ID = 22; + public static final int eAM_STATUS_LIBRARY_NOT_AVAILABLE = 14; + public static final int eAM_STATUS_NO_ACTIVE_USER = 20; + public static final int eAM_STATUS_NO_STORED_CREDENTIAL = 2; + public static final int eAM_STATUS_OK = 0; + public static final int eAM_STATUS_TIMEOUT = 12; + public static final int eAM_STATUS_UI_TIMEOUT = 16; + public static final int eAM_STATUS_UNKNOWN_COMMAND = 1; + public static final int eAM_STATUS_UNKNOWN_ERROR = 99; + public static final int eAM_STATUS_USER_CANCELED = 9; + public static final int eAM_STATUS_USURP_FAILURE = 13; + + private AuthentecHelper(Context ctx) + { + try { + AuthentecMobile = sAuthentecClassLoader.loadClass("com.authentec.amjni.AuthentecMobile"); + TSM = sAuthentecClassLoader.loadClass("com.authentec.amjni.TSM"); + Constructor ctor = AuthentecMobile.getDeclaredConstructors()[0]; + am = ctor.newInstance(ctx); + } catch (Exception ex) { + ex.printStackTrace(); + return; + } + } + + public int fingerprintUnlock(String sScreen, Context ctx) { + int iResult = 0; + + try { + if (! (Boolean) AuthentecMobile.getMethod("AM2ClientLibraryLoaded").invoke(am)) { + return eAM_STATUS_LIBRARY_NOT_AVAILABLE; + } + + if(null == ctx) { + return eAM_STATUS_INVALID_PARAMETER; + } + + //int iResult = TSM.LAP(ctx).verify().viaGfxScreen(sScreen).exec(); + Class partTypes[] = new Class[1]; + Object argList[] = new Object[1]; + + partTypes[0] = Context.class; + argList[0] = ctx; + Object TSMi = TSM.getMethod("LAP", partTypes).invoke(null, argList); + + TSM.getMethod("verify").invoke(TSMi); + + partTypes[0] = String.class; + argList[0] = sScreen; + TSM.getMethod("viaGfxScreen", partTypes).invoke(TSMi, argList); + + iResult = (Integer) TSM.getMethod("exec").invoke(TSMi); + TSMi = null; + + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return iResult; + } + + public int startEnrollmentWizard(Context choosefinger, String msTempPasscode) + { + int miResult = eAM_STATUS_LIBRARY_NOT_AVAILABLE; + + try { + Class partTypes[] = new Class[1]; + Object argList[] = new Object[1]; + + partTypes[0] = Context.class; + argList[0] = choosefinger; + Object TSMi = TSM.getMethod("LAP", partTypes).invoke(null, argList); + + if (msTempPasscode != null) { + partTypes[0] = String.class; + argList[0] = msTempPasscode; + argList[0] = (String) TSM.getMethod("Hexify", partTypes).invoke(null, argList); + + TSM.getMethod("usingPasscode", partTypes).invoke(TSMi, argList); + + TSM.getMethod("enroll").invoke(TSMi); + //miResult = TSM.LAP(ChooseLockFinger.this).usingPasscode(TSM.Hexify(msTempPasscode)).enroll().exec(); + } else { + partTypes[0] = String.class; + argList[0] = "_classicEnroll"; + TSM.getMethod("addFunction", partTypes).invoke(TSMi, argList); + //miResult = TSM.LAP(ChooseLockFinger.this).addFunction("_classicEnroll").exec(); + } + + miResult = (Integer) TSM.getMethod("exec").invoke(TSMi); + TSMi = null; + + } catch (Exception e) { + e.printStackTrace(); + } + + return miResult; + } + + public int startVerification(Context choosefinger) + { + int miResult = eAM_STATUS_LIBRARY_NOT_AVAILABLE; + + try { + //miResult = TSM.LAP(ChooseLockFinger.this).verify().viaGfxScreen("lap-verify").exec(); + Class partTypes[] = new Class[1]; + Object argList[] = new Object[1]; + + partTypes[0] = Context.class; + argList[0] = choosefinger; + Object TSMi = TSM.getMethod("LAP", partTypes).invoke(null, argList); + + TSM.getMethod("verify").invoke(TSMi); + + partTypes[0] = String.class; + argList[0] = "lap-verify"; + TSM.getMethod("viaGfxScreen", partTypes).invoke(TSMi, argList); + + miResult = (Integer) TSM.getMethod("exec").invoke(TSMi); + TSMi = null; + + } catch (Exception e) { + e.printStackTrace(); + } + + return miResult; + } + + public int verifyPolicy(Context ctx) + { + int iResult = eAM_STATUS_LIBRARY_NOT_AVAILABLE; + + try { + //iResult = TSM.LAP(m_Context).waitForUI().verify().exec(); + Class partTypes[] = new Class[1]; + Object argList[] = new Object[1]; + + partTypes[0] = Context.class; + argList[0] = ctx; + Object TSMi = TSM.getMethod("LAP", partTypes).invoke(null, argList); + + TSM.getMethod("waitForUI").invoke(TSMi, (Object[]) null); + + TSM.getMethod("verify").invoke(TSMi, (Object[]) null); + + iResult = (Integer) TSM.getMethod("exec").invoke(TSMi, (Object[]) null); + TSMi = null; + + } catch (Exception e) { + e.printStackTrace(); + } + + return iResult; + } + + public static AuthentecHelper getInstance(Context ctx) + { + ensureClassLoader(ctx); + return new AuthentecHelper(ctx); + } + + public static void ensureClassLoader(Context ctx) + { + if (sAuthentecClassLoader != null) { + return; + } + + String authentecJarLocation = SystemProperties.get("ro.authentec.fingerprint.jar",""); + String authentecSoLocation = SystemProperties.get("ro.authentec.fingerprint.so",""); + + if ("".equals(authentecJarLocation) || "".equals(authentecSoLocation)) + return; + + sAuthentecClassLoader = new DexClassLoader(authentecJarLocation, + new ContextWrapper(ctx).getCacheDir().getAbsolutePath(), + authentecSoLocation,ClassLoader.getSystemClassLoader()); + } +} diff --git a/core/java/com/authentec/GfxEngineRelayService.java b/core/java/com/authentec/GfxEngineRelayService.java new file mode 100644 index 00000000000..53a4d68f576 --- /dev/null +++ b/core/java/com/authentec/GfxEngineRelayService.java @@ -0,0 +1,127 @@ +package com.authentec; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; + +import com.authentec.TrueSuiteMobile.RelayReceiverService; + +public class GfxEngineRelayService extends Service { + + public interface Receiver + { + public void receiveCommand(String command, String args); + } + + static Receiver mLocalReceiver = null; + public static void setLocalReceiver(Receiver localReceiver) { + // first send any queued commands to the activity + while (!mCommandBuffer.isEmpty()) { + Command storedCommand = mCommandBuffer.remove(0); + if (null == localReceiver) continue; + + // send the command on to the receiver + localReceiver.receiveCommand( + storedCommand.mCommand, storedCommand.mArguments); + + } + + // finally record who the receiver is + mLocalReceiver = localReceiver; + } + + static private ArrayList<Command> mCommandBuffer = new ArrayList<Command>(); + static private List<String> mEventBuffer = new ArrayList<String>(); + static private Semaphore mEventBufferSemaphore = null; + + private class Command { + public String mCommand; + public String mArguments; + public Command(String command, String arguments) { + mCommand = command; + mArguments = arguments; + } + } + + static public void queueEvent(String event) { + if (null == mEventBufferSemaphore) return; + mEventBuffer.add(event); + mEventBufferSemaphore.release(); + } + + @Override + public IBinder onBind(Intent intent) { + /* when we're bound to, we want to have an empty event buffer */ + mEventBuffer.clear(); + mEventBufferSemaphore = new Semaphore(0); + return new RelayReceiverServiceImpl(); + } + + private class RelayReceiverServiceImpl extends RelayReceiverService.Stub + implements RelayReceiverService { + + /* remote clients call sendCommand() when the GfxEngine has provided */ + /* a new command to apply to the UI */ + public void sendCommand(String command, String args) throws RemoteException { + + /* if we've got a local receiver, pass the command to it */ + if (null != mLocalReceiver) { + while (!mCommandBuffer.isEmpty()) { + // first pull items from the buffer. if anything is in here, + // it came in before the activity was ready to receive them. + Command storedCommand = mCommandBuffer.remove(0); + mLocalReceiver.receiveCommand( + storedCommand.mCommand, storedCommand.mArguments); + } + mLocalReceiver.receiveCommand(command, args); + } + else { + // append it to a buffer to be delivered later + mCommandBuffer.add(new Command(command, args)); + } + } + + /* remote clients call receiveEvent() to get the next event from the */ + /* UI's event queue -- things like #cancel and #timeout */ + public String receiveEvent() throws RemoteException { + /* block until there's something in the event queue */ + try { + // mEventBufferSemaphore.acquire(); + + // This method runs in the service's main thread (and there's no way + // to move it to a child thread, since it needs to return an answer + // to the GfxEngine), and when the keyguard blocks here, Android has + // problems. So it's better to add a timeout to the block waiting. + if (!mEventBufferSemaphore.tryAcquire(10, TimeUnit.SECONDS)) { + + // The GfxEngine is not currently expecting this exception and it will + // try to use the null pointer. We should probably fix this in the GfxEngine, + // but a short term solution is to return "" instead of null. + return ""; + } + } catch (InterruptedException e) { + // return null; + return ""; + } + + /* remove the next event from the queue and return it */ + if (mEventBuffer.isEmpty()) { + return ""; + } + else{ + return mEventBuffer.remove(0); + } + } + + /* remote clients call receiveEvent() to release mEventBufferSemaphore */ + public void quit() throws RemoteException { + mEventBufferSemaphore.release(); + } + } +} diff --git a/core/java/com/authentec/TrueSuiteMobile/RelayReceiverService.aidl b/core/java/com/authentec/TrueSuiteMobile/RelayReceiverService.aidl new file mode 100644 index 00000000000..ac8c8af99f7 --- /dev/null +++ b/core/java/com/authentec/TrueSuiteMobile/RelayReceiverService.aidl @@ -0,0 +1,7 @@ +package com.authentec.TrueSuiteMobile; +interface RelayReceiverService +{ + void sendCommand(String command, String args); + String receiveEvent(); + void quit(); +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e179d34a4d9..11d849b0998 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1395,6 +1395,12 @@ android:permission="android.permission.BIND_WALLPAPER"> </service> + <service android:name="com.authentec.GfxEngineRelayService"> + <intent-filter> + <action android:name="com.authentec.Keyguard" /> + </intent-filter> + </service> + <receiver android:name="com.android.server.BootReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> diff --git a/core/res/res/drawable/cmfp.png b/core/res/res/drawable/cmfp.png Binary files differnew file mode 100644 index 00000000000..b120773ab32 --- /dev/null +++ b/core/res/res/drawable/cmfp.png diff --git a/core/res/res/layout/keyguard_screen_finger_landscape.xml b/core/res/res/layout/keyguard_screen_finger_landscape.xml new file mode 100644 index 00000000000..928c7d42d26 --- /dev/null +++ b/core/res/res/layout/keyguard_screen_finger_landscape.xml @@ -0,0 +1,206 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License") +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- This is the screen that shows the 9 circle unlock widget and instructs + the user how to unlock their device, or make an emergency call. This + is the portrait layout. --> + +<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="#70000000" + > + + <!-- left side: instructions and emergency call button --> + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:layout_marginLeft="24dip" + android:gravity="left" + > + + <TextView + android:id="@+id/status1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="16dip" + android:textAppearance="?android:attr/textAppearanceMedium" + /> + + <TextView + android:id="@+id/carrier" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="17sp" + android:drawablePadding="4dip" + android:layout_marginTop="32dip" + android:singleLine="true" + android:ellipsize="marquee" + android:gravity="right|bottom" + /> + + <com.android.internal.widget.DigitalClock android:id="@+id/time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:layout_marginTop="8dip" + android:paddingBottom="8dip" + > + + <TextView android:id="@+id/timeDisplay" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="none" + android:textSize="72sp" + android:textAppearance="?android:attr/textAppearanceMedium" + android:shadowColor="#C0000000" + android:shadowDx="0" + android:shadowDy="0" + android:shadowRadius="3.0" + android:layout_marginBottom="6dip" + /> + + + <TextView android:id="@+id/am_pm" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/timeDisplay" + android:layout_alignBaseline="@id/timeDisplay" + android:singleLine="true" + android:ellipsize="none" + android:textSize="22sp" + android:layout_marginLeft="8dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:shadowColor="#C0000000" + android:shadowDx="0" + android:shadowDy="0" + android:shadowRadius="3.0" + /> + + </com.android.internal.widget.DigitalClock> + + <TextView + android:id="@+id/date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/time" + android:textAppearance="?android:attr/textAppearanceMedium" + /> + + <!-- used for instructions such as "draw pattern to unlock", the next alarm, and charging + status. --> + <LinearLayout + android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="8dip" + android:gravity="center" + > + <TextView + android:id="@+id/statusSep" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="5dip" + android:layout_marginRight="5dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="17sp" + /> + <TextView + android:id="@+id/status2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="17sp" + android:drawablePadding="4dip" + /> + </LinearLayout> + + <!-- fill space between header and button below --> + <View + android:layout_weight="1.0" + android:layout_width="match_parent" + android:layout_height="0dip" + /> + + <!-- footer --> + <FrameLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="16dip" + > + + <!-- option 1: a single emergency call button --> + <RelativeLayout android:id="@+id/footerNormal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="left" + > + <Button android:id="@+id/emergencyCallAlone" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/lockscreen_emergency_call" + style="@style/Widget.Button.Transparent" + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + /> + </RelativeLayout> + + <!-- option 2: an emergency call button, and a 'forgot pattern?' button --> + <LinearLayout android:id="@+id/footerForgotPattern" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="left" + > + <Button android:id="@+id/forgotPattern" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/Widget.Button.Transparent" + android:visibility="invisible" + /> + <Button android:id="@+id/emergencyCallTogether" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/lockscreen_emergency_call" + style="@style/Widget.Button.Transparent" + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + /> + </LinearLayout> + </FrameLayout> + </LinearLayout> + + <!-- right side: lock finger --> + <include + layout="@layout/unlock_finger" + android:id="@+id/unlock_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + /> + +</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient> diff --git a/core/res/res/layout/keyguard_screen_finger_portrait.xml b/core/res/res/layout/keyguard_screen_finger_portrait.xml new file mode 100644 index 00000000000..8bb9a22e881 --- /dev/null +++ b/core/res/res/layout/keyguard_screen_finger_portrait.xml @@ -0,0 +1,217 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License") +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- This is the screen that shows the 9 circle unlock widget and instructs + the user how to unlock their device, or make an emergency call. This + is the portrait layout. --> +<com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_horizontal" + android:background="#70000000" + > + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + <TextView + android:id="@+id/carrier" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_marginTop="6dip" + android:layout_alignParentRight="true" + android:layout_marginRight="8dip" + android:layout_toRightOf="@+id/time" + android:singleLine="true" + android:ellipsize="marquee" + android:gravity="right|bottom" + android:textAppearance="?android:attr/textAppearanceMedium" + /> + + <com.android.internal.widget.DigitalClock android:id="@+id/time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignParentTop="true" + android:layout_marginTop="15dip" + android:layout_marginLeft="20dip" + android:paddingBottom="8dip" + > + + <TextView android:id="@+id/timeDisplay" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="none" + android:textSize="56sp" + android:textAppearance="?android:attr/textAppearanceMedium" + android:shadowColor="#C0000000" + android:shadowDx="0" + android:shadowDy="0" + android:shadowRadius="3.0" + android:layout_marginBottom="6dip" + /> + + <TextView android:id="@+id/am_pm" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/timeDisplay" + android:layout_alignBaseline="@id/timeDisplay" + android:singleLine="true" + android:ellipsize="none" + android:textSize="18sp" + android:layout_marginLeft="4dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:shadowColor="#C0000000" + android:shadowDx="0" + android:shadowDy="0" + android:shadowRadius="3.0" + /> + + </com.android.internal.widget.DigitalClock> + + <TextView + android:id="@+id/date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/time" + android:layout_marginLeft="24dip" + android:textAppearance="?android:attr/textAppearanceMedium" + /> + + </RelativeLayout> + + <View + android:id="@+id/divider" + android:layout_width="match_parent" + android:layout_height="1dip" + android:layout_marginTop="8dip" + android:layout_marginBottom="8dip" + android:background="@android:drawable/divider_horizontal_dark" + /> + + <!-- used for instructions such as "draw pattern to unlock", the next alarm, and charging + status. --> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="0dip" + android:layout_marginLeft="12dip" + android:gravity="left" + > + <TextView + android:id="@+id/status1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="18sp" + android:drawablePadding="4dip" + /> + <TextView + android:id="@+id/statusSep" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="5dip" + android:layout_marginRight="5dip" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="18sp" + /> + <TextView + android:id="@+id/status2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="18sp" + android:drawablePadding="4dip" + /> + </LinearLayout> + + <include + layout="@layout/unlock_finger" + android:id="@+id/unlock_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="12dip" + /> + + <!-- footer --> + <FrameLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + + <!-- option 1: a single emergency call button --> + <RelativeLayout android:id="@+id/footerNormal" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <Button android:id="@+id/emergencyCallAlone" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:text="@string/lockscreen_emergency_call" + style="@style/Widget.Button.Transparent" + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + /> + + </RelativeLayout> + + <!-- option 2: an emergency call button, and a 'forgot pattern?' button --> + <LinearLayout android:id="@+id/footerForgotPattern" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center" + > + <Button android:id="@+id/emergencyCallTogether" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1.0" + android:layout_marginTop="4dip" + android:layout_marginBottom="4dip" + android:layout_marginLeft="4dip" + android:layout_marginRight="2dip" + android:text="@string/lockscreen_emergency_call" + style="@style/Widget.Button.Transparent" + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + /> + <Button android:id="@+id/forgotPattern" + android:layout_width="0dip" + android:layout_height="match_parent" + android:layout_weight="1.0" + android:layout_marginTop="4dip" + android:layout_marginBottom="4dip" + android:layout_marginLeft="2dip" + android:layout_marginRight="4dip" + style="@style/Widget.Button.Transparent" + android:visibility="invisible" + /> + </LinearLayout> + + </FrameLayout> + +</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient> diff --git a/core/res/res/layout/unlock_finger.xml b/core/res/res/layout/unlock_finger.xml new file mode 100644 index 00000000000..f3a74f66669 --- /dev/null +++ b/core/res/res/layout/unlock_finger.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License") +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="8dip" + android:paddingBottom="8dip" + android:gravity="left" + > + <LinearLayout + android:id="@+id/userPrompt" + android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + > + <ImageView + android:src="@drawable/ic_lock_lock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> + <TextView + android:id="@+id/usageMessage" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="8dip" + /> + </LinearLayout> + + <TextView + android:id="@+id/errorMessage" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:paddingTop="2dip" + android:paddingBottom="2dip" + /> + + <ImageView + android:src="@drawable/cmfp" + android:paddingLeft="1dip" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + /> + +</LinearLayout> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 4a359eb517e..edd46d9387e 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -957,4 +957,20 @@ <!-- Description of an application permission that lets lets an application view/edit revoked permissions. --> <string name="permdesc_revokePermissions">Permitir ver y editar los permisos revocados a las aplicaciones. No utilizar para aplicaciones normales.</string> + <string name="keyguard_finger_please_swipe">Pase el dedo sobre el sensor para desbloquear</string> + <string name="keyguard_finger_ui_timeout">La IU ha superado el tiempo de espera</string> + <string name="keyguard_finger_failed_to_unlock">No se ha podido desbloquear</string> + <string name="keyguard_finger_screen_off">Pantalla apagada</string> + <string name="keyguard_finger_screen_on">Pantalla encendida</string> + <string name="keyguard_finger_match">El dedo coincide, desbloqueando la pantalla...</string> + <string name="keyguard_finger_not_match">El dedo no coincide...</string> + <string name="keyguard_finger_swipe_too_far_right">Pase el dedo por el centro.</string> + <string name="keyguard_finger_swipe_too_far_left">Pase el dedo por el centro.</string> + <string name="keyguard_finger_swipe_too_fast">Pase demasiado rápido.</string> + <string name="keyguard_finger_swipe_too_short">Pase demasiado corto.</string> + <string name="keyguard_finger_swipe_too_slow">Pase demasiado lento.</string> + <string name="keyguard_finger_swipe_too_skewed">Pase demasiado fraccionado.</string> + <string name="lockscreen_too_many_failed_fingers_dialog_message">La huella dactilar ha dado error al identificarle %1$d veces. \n\nVuelva a intentarlo en %2$d segundos. </string> + <string name="lockscreen_failed_fingers_almost_glogin">La huella dactilar ha dado error al identificarle %1$d veces. Tras %2$d identificaciones correctas más, se le pedirá que desbloquee el teléfono mediante el inicio de sesión de Google.\n\n Vuelva a intentarlo en %3$d segundos. </string> + <string name="lockscreen_forgot_finger_button_text">¿Ha olvidado el dedo?</string> </resources> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index afd887ca0b2..acbf6961aba 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -943,4 +943,21 @@ <item quantity="one">%d seconde restante</item> <item quantity="other">%d secondes restantes</item> </plurals> + + <string name="keyguard_finger_please_swipe">Passez le doigt sur le capteur pour déverrouiller</string> + <string name="keyguard_finger_ui_timeout">Délai d\'attente de l\'interface utilisateur !</string> + <string name="keyguard_finger_failed_to_unlock">Échec du déverrouillage !</string> + <string name="keyguard_finger_screen_off">Écran désactivé</string> + <string name="keyguard_finger_screen_on">Écran activé</string> + <string name="keyguard_finger_match">Le doigt correspond, déverrouillage de l\'écran...</string> + <string name="keyguard_finger_not_match">Le doigt ne correspond pas...</string> + <string name="keyguard_finger_swipe_too_far_right">Faites glisser le doigt au centre.</string> + <string name="keyguard_finger_swipe_too_far_left">Faites glisser le doigt au centre.</string> + <string name="keyguard_finger_swipe_too_fast">Vous avez fait glisser votre doigt trop rapidement.</string> + <string name="keyguard_finger_swipe_too_short">La distance sur laquelle vous avez fait glisser votre doigt est trop courte.</string> + <string name="keyguard_finger_swipe_too_slow">Vous avez fait glisser votre doigt trop lentement.</string> + <string name="keyguard_finger_swipe_too_skewed">Vous avez fait glisser votre doigt de travers.</string> + <string name="lockscreen_too_many_failed_fingers_dialog_message">L\'identification avec votre empreinte digitale à échoué %1$d fois.\n\nVeuillez réessayer dans %2$d secondes.</string> + <string name="lockscreen_failed_fingers_almost_glogin">L\'identification avec votre empreinte digitale à échoué %1$d fois. Après %2$d autres échecs d\'identification, il vous sera demandé de déverrouiller votre téléphone à l\'aide de votre connexion Google.\n\n Veuillez réessayer dans %3$d secondes.</string> + <string name="lockscreen_forgot_finger_button_text">Vous ne vous souvenez plus du doigt utilisé ?</string> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 1c64dac7c1b..0e846caff42 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -978,4 +978,20 @@ <!-- Description of an application permission that lets lets an application view/edit revoked permissions. --> <string name="permdesc_revokePermissions">取り消された権限を読み書きする。通常のアプリケーションでは不要です。</string> + <string name="keyguard_finger_please_swipe">ロックを解除するには指をセンサー上で滑らせてください</string> + <string name="keyguard_finger_ui_timeout">UI がタイムアウトしました!</string> + <string name="keyguard_finger_failed_to_unlock">ロックを解除できませんでした!</string> + <string name="keyguard_finger_screen_off">画面オフ</string> + <string name="keyguard_finger_screen_on">画面オン</string> + <string name="keyguard_finger_match">指が一致しました。画面のロックを解除しています...</string> + <string name="keyguard_finger_not_match">指が一致しませんでした...</string> + <string name="keyguard_finger_swipe_too_far_right">中央で指を滑らせてください。</string> + <string name="keyguard_finger_swipe_too_far_left">中央で指を滑らせてください。</string> + <string name="keyguard_finger_swipe_too_fast">滑らせ方が速すぎます。</string> + <string name="keyguard_finger_swipe_too_short">滑らせる距離が短すぎます。</string> + <string name="keyguard_finger_swipe_too_slow">滑らせ方が遅すぎます。</string> + <string name="keyguard_finger_swipe_too_skewed">滑らせ方が傾きすぎです。</string> + <string name="lockscreen_too_many_failed_fingers_dialog_message">指紋の識別に %1$d 回失敗しました。\n\n%2$d 秒後に再試行してください。</string> + <string name="lockscreen_failed_fingers_almost_glogin">指紋の識別に %1$d 回失敗しました。識別にあと %2$d 回失敗すると、Google サインインを使用して電話のロックを解除するように求められます。\n\n%3$d 秒後に再試行してください。</string> + <string name="lockscreen_forgot_finger_button_text">どの指かわからなくなった場合</string> </resources> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml new file mode 100644 index 00000000000..6e93e15ab5b --- /dev/null +++ b/core/res/res/values-pt-rBR/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- THIS FILE WAS AUTO-GENERATED, MANUAL MODIFICATIONS WILL BE LOST! --> +<!-- resource category: keyguard --> +<!-- file generated at 05:10:05 on 2011/08/12--> +<resources> + <string name="keyguard_finger_please_swipe">Deslize o dedo sobre o sensor para desbloquear</string> + <string name="keyguard_finger_ui_timeout">O tempo limite da interface do usuário expirou!</string> + <string name="keyguard_finger_failed_to_unlock">Falha ao desbloquear!</string> + <string name="keyguard_finger_screen_off">Tela apagada</string> + <string name="keyguard_finger_screen_on">Tela acesa</string> + <string name="keyguard_finger_match">Correspondência encontrada para o dedo. Desbloqueando tela...</string> + <string name="keyguard_finger_not_match">Nenhuma correspondência encontrada para o dedo...</string> + <string name="keyguard_finger_swipe_too_far_right">Deslize o dedo pelo centro.</string> + <string name="keyguard_finger_swipe_too_far_left">Deslize o dedo pelo centro.</string> + <string name="keyguard_finger_swipe_too_fast">Movimento rápido demais.</string> + <string name="keyguard_finger_swipe_too_short">Movimento curto demais.</string> + <string name="keyguard_finger_swipe_too_slow">Movimento lento demais.</string> + <string name="keyguard_finger_swipe_too_skewed">Movimento inclinado demais.</string> + <string name="lockscreen_too_many_failed_fingers_dialog_message">Sua impressão digital não identificou você %1$d vezes.\n\nTente novamente em %2$d segundos.</string> + <string name="lockscreen_failed_fingers_almost_glogin">Sua impressão digital não identificou você %1$d vezes. Depois de mais %2$d identificação(ões) malsucedida(s), você será solicitado a desbloquear o telefone usando seu logon do Google.\n\n Tente novamente em %3$d segundos.</string> + <string name="lockscreen_forgot_finger_button_text">Esqueceu o dedo?</string> +</resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 2c661016a05..0e988e7d5d8 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -954,4 +954,21 @@ <item quantity="one">还剩 %d 秒</item> <item quantity="other">还剩 %d 秒</item> </plurals> + + <string name="keyguard_finger_please_swipe">将手指滑过传感器以解锁</string> + <string name="keyguard_finger_ui_timeout">UI 超时!</string> + <string name="keyguard_finger_failed_to_unlock">无法解锁!</string> + <string name="keyguard_finger_screen_off">屏幕关闭</string> + <string name="keyguard_finger_screen_on">屏幕打开</string> + <string name="keyguard_finger_match">手指吻合,解锁屏幕...</string> + <string name="keyguard_finger_not_match">手指不吻合...</string> + <string name="keyguard_finger_swipe_too_far_right">在中心滑动。</string> + <string name="keyguard_finger_swipe_too_far_left">在中心滑动。</string> + <string name="keyguard_finger_swipe_too_fast">滑得太快。</string> + <string name="keyguard_finger_swipe_too_short">滑得太短。</string> + <string name="keyguard_finger_swipe_too_slow">滑得太慢。</string> + <string name="keyguard_finger_swipe_too_skewed">滑得太偏。</string> + <string name="lockscreen_too_many_failed_fingers_dialog_message">您的指纹识别已失败 %1$d 次。\n\n请在 %2$d 秒内重试。</string> + <string name="lockscreen_failed_fingers_almost_glogin">您的指纹识别已失败 %1$d 次。如果识别再失败 %2$d 次,系统将要求您使用 Google 登录来解锁您的电话。\n\n请在 %3$d 秒内重试。</string> + <string name="lockscreen_forgot_finger_button_text">忘记手指?</string> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 8b851bf3a3e..2c85287dda7 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2230,4 +2230,20 @@ <!-- Description of an application permission that lets lets an application view/edit revoked permissions. --> <string name="permdesc_revokePermissions">允許讀取及寫入被撤消的權限。不應使用於正常的應用程式。</string> + <string name="keyguard_finger_please_swipe">在感應器上劃一下手指以解除鎖定</string> + <string name="keyguard_finger_ui_timeout">UI 逾時!</string> + <string name="keyguard_finger_failed_to_unlock">無法解除鎖定!</string> + <string name="keyguard_finger_screen_off">螢幕關閉</string> + <string name="keyguard_finger_screen_on">螢幕開啟</string> + <string name="keyguard_finger_match">手指符合,解除鎖定螢幕...</string> + <string name="keyguard_finger_not_match">手指不符合...</string> + <string name="keyguard_finger_swipe_too_far_right">在中心位置劃一下。</string> + <string name="keyguard_finger_swipe_too_far_left">在中心位置劃一下。</string> + <string name="keyguard_finger_swipe_too_fast">劃太快。</string> + <string name="keyguard_finger_swipe_too_short">劃太短。</string> + <string name="keyguard_finger_swipe_too_slow">劃太慢。</string> + <string name="keyguard_finger_swipe_too_skewed">劃太歪。</string> + <string name="lockscreen_too_many_failed_fingers_dialog_message">您的指紋無法識別您的身分達 %1$d 次。\n\n請在 %2$d 秒內再試一次。</string> + <string name="lockscreen_failed_fingers_almost_glogin">您的指紋無法識別您的身分達 %1$d 次。如果識別再失敗 %2$d 次,系統將要求您使用 Google 登入資訊來解除鎖定您的電話。\n\n請在 %3$d 秒內再試一次。</string> + <string name="lockscreen_forgot_finger_button_text">忘記手指?</string> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6d5c256ad17..07de23c9b5a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -636,4 +636,7 @@ -SourcePortDestPort-SourcePortDestPort-OMADM PDU If false, not supported. --> <bool name="config_duplicate_port_omadm_wappush">false</bool> + + <!-- Location of the Authentec framework jar --> + <string name="config_authentecJarLocation"></string> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index e91d0d20504..ed94087c3ce 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2471,4 +2471,20 @@ <!-- Description of an application permission that lets lets an application view/edit revoked permissions. --> <string name="permdesc_revokePermissions">Allows read and write for revoked permissions. Not for use by normal applications.</string> + <string name="keyguard_finger_please_swipe">Swipe finger over sensor to unlock</string> + <string name="keyguard_finger_ui_timeout">UI timeout!</string> + <string name="keyguard_finger_failed_to_unlock">Failed to unlock!</string> + <string name="keyguard_finger_screen_off">Screen off</string> + <string name="keyguard_finger_screen_on">Screen on</string> + <string name="keyguard_finger_match">Finger match, unlock screen...</string> + <string name="keyguard_finger_not_match">Finger did not match...</string> + <string name="keyguard_finger_swipe_too_far_right">Swipe in the center.</string> + <string name="keyguard_finger_swipe_too_far_left">Swipe in the center.</string> + <string name="keyguard_finger_swipe_too_fast">Swipe was too fast.</string> + <string name="keyguard_finger_swipe_too_short">Swipe was too short.</string> + <string name="keyguard_finger_swipe_too_slow">Swipe was too slow.</string> + <string name="keyguard_finger_swipe_too_skewed">Swipe was too skewed.</string> + <string name="lockscreen_too_many_failed_fingers_dialog_message">Your fingerprint failed to identify you %1$d times.\n\nPlease try again in %2$d seconds.</string> + <string name="lockscreen_failed_fingers_almost_glogin">Your fingerprint failed to identify you %1$d times. After %2$d more unsuccessful identifications, you will be asked to unlock your phone using your Google sign-in.\n\n Please try again in %3$d seconds.</string> + <string name="lockscreen_forgot_finger_button_text">Forgot finger?</string> </resources> diff --git a/policy/src/com/android/internal/policy/impl/FingerUnlockScreen.java b/policy/src/com/android/internal/policy/impl/FingerUnlockScreen.java new file mode 100644 index 00000000000..eab7e775606 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/FingerUnlockScreen.java @@ -0,0 +1,1402 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.policy.impl; + +import dalvik.system.DexClassLoader; + +import android.content.Context; +import android.content.ContextWrapper; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.CountDownTimer; +import android.os.SystemClock; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.MotionEvent; +import android.widget.Button; +import android.widget.TextView; +import android.text.format.DateFormat; +import android.text.TextUtils; +import android.util.Log; +import com.android.internal.R; +import com.android.internal.telephony.IccCard; +import com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient; +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockPatternView.Cell; + +import java.util.List; +import java.util.Date; + +// For the finger keyguard +import android.os.Handler; +import android.os.Vibrator; +import android.os.PowerManager; +import android.os.Message; +import android.os.SystemProperties; + +import com.authentec.AuthentecHelper; +import com.authentec.GfxEngineRelayService; + + +/** + * This is the screen that shows the 9 circle unlock widget and instructs + * the user how to unlock their device, or make an emergency call. + */ +class FingerUnlockScreen extends LinearLayoutWithDefaultTouchRecepient + implements KeyguardScreen, KeyguardUpdateMonitor.InfoCallback, + KeyguardUpdateMonitor.SimStateCallback, + GfxEngineRelayService.Receiver { + + private static final boolean DEBUG = true; + private static final boolean DEBUG_CONFIGURATION = true; + private static final String TAG = "FingerUnlockScreen"; + + // how long we stay awake once the user is ready to enter a pattern + private static final int UNLOCK_FINGER_WAKE_INTERVAL_MS = 7000; + + private int mFailedPatternAttemptsSinceLastTimeout = 0; + private int mTotalFailedPatternAttempts = 0; + private CountDownTimer mCountdownTimer = null; + + private CountDownTimer mCountdownTimerToast = null; + private final LockPatternUtils mLockPatternUtils; + private final KeyguardUpdateMonitor mUpdateMonitor; + private final KeyguardScreenCallback mCallback; + + /** + * whether there is a fallback option available when the pattern is forgotten. + */ + private boolean mEnableFallback; + + private static boolean m_bVerifyied; + + // Do we still hold a wakelock? + // Initialize this value to "true" since the screen is off at the very beginning. + private static boolean m_bPaused = true; + + // The key guard would be put into lockout state every four bad swipes. + private static boolean m_bAttemptLockout; + + // A flag to indicate if you are currently connected to the GfxEngine or not. + private boolean m_bGfxEngineAttached; + + // A flag to indicate that there is a delayed #cancel command to send. + private boolean m_bSendDelayedCancel; + + // Make sure that you do not double-cancel. + private boolean m_bCancelHasBeenSent; + + // A flag to indicate that there is a successive power on to the previous power off. + // If the previous #cancel command has not terminate the verify runner yet, set this + // flag to let the verify runner start again. + private boolean m_bStartAgain; + + // Below four are related with tactile feedback... + // A flag to indicate if tactile feedback is supported. + private boolean mTactileFeedbackEnabled = false; + // Vibrator pattern for creating a tactile bump + private static final long[] DEFAULT_VIBE_PATTERN = {0, 1, 40, 41}; + // Vibrator for creating tactile feedback + private Vibrator vibe; + // Vibration pattern, either default or customized + private long[] mVibePattern; + + private String mDateFormatString; + + private TextView mCarrier; + private TextView mDate; + + // are we showing battery information? + private boolean mShowingBatteryInfo = false; + + // last known plugged in state + private boolean mPluggedIn = false; + + // last known battery level + private int mBatteryLevel = 100; + + private String mNextAlarm = null; + + private String mInstructions = null; + private TextView mStatus1; + private TextView mStatusSep; + private TextView mStatus2; + private TextView mUserPrompt; + private TextView mErrorPrompt; + + private ViewGroup mFooterNormal; + private ViewGroup mFooterForgotPattern; + + /** + * Keeps track of the last time we poked the wake lock during dispatching + * of the touch event, initalized to something gauranteed to make us + * poke it when the user starts drawing the pattern. + * @see #dispatchTouchEvent(android.view.MotionEvent) + */ + private long mLastPokeTime = -UNLOCK_FINGER_WAKE_INTERVAL_MS; + + /** + * Useful for clearing out the wrong pattern after a delay + */ + private Runnable mCancelPatternRunnable = new Runnable() { + public void run() { + //mLockPatternView.clearPattern(); + } + }; + + private Button mForgotPatternButton; + private Button mEmergencyAlone; + private Button mEmergencyTogether; + private int mCreationOrientation; + + static Thread mExecutionThread = null; + private Thread mUiThread; + private boolean mbFeedbackDelivered = false; + private VerifyRunner mVerifyRunner = new VerifyRunner(); + private Context m_Context; + + /** High level access to the power manager for WakeLocks */ + private PowerManager mPM; + + /** + * Used to keep the device awake while the keyguard is showing, i.e for + * calls to {@link #pokeWakelock()} + * Note: Unlike the mWakeLock in KeyguardViewMediator (which has the + * ACQUIRE_CAUSES_WAKEUP flag), this one doesn't actually turn + * on the illumination. Instead, they cause the illumination to + * remain on once it turns on. + */ + private PowerManager.WakeLock mWakeLock; + + /** + * Does not turn on screen, used to keep the device awake until the + * fingerprint verification is ready. The KeyguardViewMediator only + * poke the wake lock for 5 seconds, which is not enough for the + * initialization of the fingerprint verification. + */ + private PowerManager.WakeLock mHandOffWakeLock; + + private int mWakelockSequence; + + // used for handler messages + private static final int TIMEOUT = 1; + + enum FooterMode { + Normal, + ForgotLockPattern, + VerifyUnlocked + } + + private void updateFooter(FooterMode mode) { + switch (mode) { + case Normal: + mFooterNormal.setVisibility(View.VISIBLE); + mFooterForgotPattern.setVisibility(View.GONE); + break; + case ForgotLockPattern: + mFooterNormal.setVisibility(View.GONE); + mFooterForgotPattern.setVisibility(View.VISIBLE); + mForgotPatternButton.setVisibility(View.VISIBLE); + break; + case VerifyUnlocked: + mFooterNormal.setVisibility(View.GONE); + mFooterForgotPattern.setVisibility(View.GONE); + } + } + + private AuthentecHelper fingerhelper = null; + + /** + * @param context The context. + * @param lockPatternUtils Used to lookup lock pattern settings. + * @param updateMonitor Used to lookup state affecting keyguard. + * @param callback Used to notify the manager when we're done, etc. + * @param totalFailedAttempts The current number of failed attempts. + * @param enableFallback True if a backup unlock option is available when the user has forgotten + * their pattern (e.g they have a google account so we can show them the account based + * backup option). + */ + FingerUnlockScreen(Context context, + Configuration configuration, + LockPatternUtils LockPatternUtils, + KeyguardUpdateMonitor updateMonitor, + KeyguardScreenCallback callback, + int totalFailedAttempts) { + super(context); + + fingerhelper = AuthentecHelper.getInstance(context); + + m_Context = context; + + /* goToSleep of PowerManagerService class would cancel all of the wake locks. */ + mPM = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mWakeLock = mPM.newWakeLock( + PowerManager.FULL_WAKE_LOCK, + "FpKeyguard"); + mWakeLock.setReferenceCounted(false); + mHandOffWakeLock = mPM.newWakeLock( + PowerManager.FULL_WAKE_LOCK, + "FpKeyguardHandOff"); + mHandOffWakeLock.setReferenceCounted(false); + + mLockPatternUtils = LockPatternUtils; + mUpdateMonitor = updateMonitor; + mCallback = callback; + mTotalFailedPatternAttempts = totalFailedAttempts; + mFailedPatternAttemptsSinceLastTimeout = + totalFailedAttempts % mLockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; + + if (DEBUG) Log.d(TAG, + "UnlockScreen() ctor: totalFailedAttempts=" + + totalFailedAttempts + ", mFailedPat...=" + + mFailedPatternAttemptsSinceLastTimeout + ); + + m_bVerifyied = false; + m_bAttemptLockout = false; + + m_bGfxEngineAttached = false; + m_bSendDelayedCancel = false; + m_bCancelHasBeenSent = false; + m_bStartAgain = false; + + mTactileFeedbackEnabled = mLockPatternUtils.isTactileFeedbackEnabled(); + if (mTactileFeedbackEnabled) { + if (DEBUG) Log.d(TAG, "Create a vibrator"); + vibe = new Vibrator(); + } + + mCreationOrientation = configuration.orientation; + + LayoutInflater inflater = LayoutInflater.from(context); + if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) { + if (DEBUG) Log.d(TAG, "UnlockScreen() : portrait"); + inflater.inflate(R.layout.keyguard_screen_finger_portrait, this, true); + } else { + if (DEBUG) Log.d(TAG, "UnlockScreen() : landscape"); + inflater.inflate(R.layout.keyguard_screen_finger_landscape, this, true); + } + + mCarrier = (TextView) findViewById(R.id.carrier); + mDate = (TextView) findViewById(R.id.date); + + mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year); + refreshTimeAndDateDisplay(); + + mStatus1 = (TextView) findViewById(R.id.status1); + mStatusSep = (TextView) findViewById(R.id.statusSep); + mStatus2 = (TextView) findViewById(R.id.status2); + + resetStatusInfo(); + + mUserPrompt = (TextView) findViewById(R.id.usageMessage); + // Temporary hints for the emulator. + mUserPrompt.setText(R.string.keyguard_finger_screen_off); + mErrorPrompt = (TextView) findViewById(R.id.errorMessage); + + mFooterNormal = (ViewGroup) findViewById(R.id.footerNormal); + mFooterForgotPattern = (ViewGroup) findViewById(R.id.footerForgotPattern); + + mUiThread = Thread.currentThread(); + GfxEngineRelayService.setLocalReceiver(this); + + // emergency call buttons + final OnClickListener emergencyClick = new OnClickListener() { + public void onClick(View v) { + // Cancel the Verify thread. + mUserPrompt.setText(""); + if (mExecutionThread != null && mExecutionThread.isAlive()) { + if (!m_bGfxEngineAttached) { + if (DEBUG) Log.d(TAG,"emergencyClick send cancel delayed"); + m_bSendDelayedCancel = true; + } else { + if (DEBUG) Log.d(TAG,"emergencyClick send cancel"); + if (!m_bCancelHasBeenSent) + { + GfxEngineRelayService.queueEvent("#cancel"); + m_bCancelHasBeenSent = true; + } + } + } + mCallback.takeEmergencyCallAction(); + } + }; + + mEmergencyAlone = (Button) findViewById(R.id.emergencyCallAlone); + mEmergencyAlone.setFocusable(false); // touch only! + mEmergencyAlone.setOnClickListener(emergencyClick); + mEmergencyTogether = (Button) findViewById(R.id.emergencyCallTogether); + mEmergencyTogether.setFocusable(false); + mEmergencyTogether.setOnClickListener(emergencyClick); + refreshEmergencyButtonText(); + + mForgotPatternButton = (Button) findViewById(R.id.forgotPattern); + mForgotPatternButton.setText(R.string.lockscreen_forgot_finger_button_text); + mForgotPatternButton.setOnClickListener(new OnClickListener() { + + public void onClick(View v) { + mCallback.forgotPattern(true); + } + }); + + if (mTactileFeedbackEnabled) { + // allow vibration pattern to be customized + if (DEBUG) Log.d(TAG, "Load vibration pattern"); + mVibePattern = loadVibratePattern(com.android.internal.R.array.config_virtualKeyVibePattern); + } + + // assume normal footer mode for now + updateFooter(FooterMode.Normal); + + updateMonitor.registerInfoCallback(this); + updateMonitor.registerSimStateCallback(this); + setFocusableInTouchMode(true); + + // Required to get Marquee to work. + mCarrier.setSelected(true); + mCarrier.setTextColor(0xffffffff); + + // until we get an update... + mCarrier.setText( + LockScreen.getCarrierString( + mUpdateMonitor.getTelephonyPlmn(), + mUpdateMonitor.getTelephonySpn())); + } + + private void refreshEmergencyButtonText() { + mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyAlone); + mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyTogether); + } + + public void setEnableFallback(boolean state) { + if (DEBUG) Log.d(TAG, "setEnableFallback(" + state + ")"); + mEnableFallback = state; + } + + private void resetStatusInfo() { + mInstructions = null; + mShowingBatteryInfo = mUpdateMonitor.shouldShowBatteryInfo(); + mPluggedIn = mUpdateMonitor.isDevicePluggedIn(); + mBatteryLevel = mUpdateMonitor.getBatteryLevel(); + mNextAlarm = mLockPatternUtils.getNextAlarm(); + updateStatusLines(); + } + + private void updateStatusLines() { + if (mInstructions != null) { + // instructions only + if (DEBUG) Log.d(TAG,"instructions only"); + mStatus1.setText(mInstructions); + if (TextUtils.isEmpty(mInstructions)) { + mStatus1.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } else { + mStatus1.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_lock_idle_lock, 0, 0, 0); + } + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.GONE); + mStatus2.setVisibility(View.GONE); + } else if (mShowingBatteryInfo && mNextAlarm == null) { + // battery only + if (DEBUG) Log.d(TAG,"battery only"); + if (mPluggedIn) { + if (mBatteryLevel >= 100) { + mStatus1.setText(getContext().getString(R.string.lockscreen_charged)); + } else { + mStatus1.setText(getContext().getString(R.string.lockscreen_plugged_in, mBatteryLevel)); + } + } else { + mStatus1.setText(getContext().getString(R.string.lockscreen_low_battery, mBatteryLevel)); + } + mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0); + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.GONE); + mStatus2.setVisibility(View.GONE); + + } else if (mNextAlarm != null && !mShowingBatteryInfo) { + // alarm only + if (DEBUG) Log.d(TAG,"alarm only"); + mStatus1.setText(mNextAlarm); + mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0); + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.GONE); + mStatus2.setVisibility(View.GONE); + } else if (mNextAlarm != null && mShowingBatteryInfo) { + // both battery and next alarm + if (DEBUG) Log.d(TAG,"both battery and next alarm"); + mStatus1.setText(mNextAlarm); + mStatusSep.setText("|"); + mStatus2.setText(getContext().getString( + R.string.lockscreen_battery_short, + Math.min(100, mBatteryLevel))); + mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_alarm, 0, 0, 0); + if (mPluggedIn) { + mStatus2.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_charging, 0, 0, 0); + } else { + mStatus2.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.VISIBLE); + mStatus2.setVisibility(View.VISIBLE); + } else { + // nothing specific to show; show general instructions + if (DEBUG) Log.d(TAG,"nothing specific to show"); + // "keyguard_finger_please_swipe" string would be showed when the TSM is ready. + //mStatus1.setText(R.string.lockscreen_pattern_instructions); + mStatus1.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_lock_idle_lock, 0, 0, 0); + + mStatus1.setVisibility(View.VISIBLE); + mStatusSep.setVisibility(View.GONE); + mStatus2.setVisibility(View.GONE); + } + } + + + private void refreshTimeAndDateDisplay() { + mDate.setText(DateFormat.format(mDateFormatString, new Date())); + } + + private long[] loadVibratePattern(int id) { + int[] pattern = null; + try { + pattern = getResources().getIntArray(id); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Vibrate pattern missing, using default", e); + } + if (pattern == null) { + return DEFAULT_VIBE_PATTERN; + } + + long[] tmpPattern = new long[pattern.length]; + for (int i = 0; i < pattern.length; i++) { + tmpPattern[i] = pattern[i]; + } + return tmpPattern; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + // as long as the user is entering a pattern (i.e sending a touch + // event that was handled by this screen), keep poking the + // wake lock so that the screen will stay on. + final boolean result = super.dispatchTouchEvent(ev); + if (result && + ((SystemClock.elapsedRealtime() - mLastPokeTime) + > (UNLOCK_FINGER_WAKE_INTERVAL_MS - 100))) { + mLastPokeTime = SystemClock.elapsedRealtime(); + } + return result; + } + + + // ---------- InfoCallback + + /** {@inheritDoc} */ + public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { + mShowingBatteryInfo = showBatteryInfo; + mPluggedIn = pluggedIn; + mBatteryLevel = batteryLevel; + updateStatusLines(); + } + + /** {@inheritDoc} */ + public void onTimeChanged() { + refreshTimeAndDateDisplay(); + } + + /** {@inheritDoc} */ + public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) { + mCarrier.setText(LockScreen.getCarrierString(plmn, spn)); + } + + /** {@inheritDoc} */ + public void onRingerModeChanged(int state) { + // not currently used + } + + // ---------- SimStateCallback + + /** {@inheritDoc} */ + public void onSimStateChanged(IccCard.State simState) { + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (DEBUG_CONFIGURATION) { + Log.v(TAG, "***** FINGERPRINT ATTACHED TO WINDOW"); + Log.v(TAG, "Cur orient=" + mCreationOrientation + + ", new config=" + getResources().getConfiguration()); + } + if (getResources().getConfiguration().orientation != mCreationOrientation) { + mCallback.recreateMe(getResources().getConfiguration()); + } + } + + + /** {@inheritDoc} */ + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (DEBUG_CONFIGURATION) { + Log.v(TAG, "***** FINGERPRINT CONFIGURATION CHANGED"); + Log.v(TAG, "Cur orient=" + mCreationOrientation + + ", new config=" + getResources().getConfiguration()); + } + if (newConfig.orientation != mCreationOrientation) { + if (DEBUG) Log.d(TAG,"Orientation changed, recreateMe"); + mCallback.recreateMe(newConfig); + } + } + + /** {@inheritDoc} */ + public void onKeyboardChange(boolean isKeyboardOpen) {} + + /** {@inheritDoc} */ + public boolean needsInput() { + return false; + } + + /** {@inheritDoc} */ + public void onPause() { + if (DEBUG) Log.d(TAG,"onPause"); + + // Temporary hints for the emulator. + mUserPrompt.setText(R.string.keyguard_finger_screen_off); + mErrorPrompt.setText(""); + + if (mWakeLock.isHeld()) { + /* Must release since it is a screen lock. */ + mHandler.removeMessages(TIMEOUT); + mWakeLock.release(); + } + + if (mHandOffWakeLock.isHeld()) { + /* Must release since it is a screen lock. */ + mHandOffWakeLock.release(); + } + + m_bVerifyied = false; + m_bPaused = true; + m_bStartAgain = false; + if (mCountdownTimer != null) { + mCountdownTimer.cancel(); + mCountdownTimer = null; + } + if (mCountdownTimerToast != null) { + mCountdownTimerToast.cancel(); + mCountdownTimerToast = null; + } + if (mExecutionThread != null && mExecutionThread.isAlive()) { + if (!m_bGfxEngineAttached) { + if (DEBUG) Log.d(TAG,"onPause send cancel delayed"); + m_bSendDelayedCancel = true; + } else { + if (DEBUG) Log.d(TAG,"onPause send cancel"); + if (!m_bCancelHasBeenSent) + { + GfxEngineRelayService.queueEvent("#cancel"); + m_bCancelHasBeenSent = true; + } + } + } + } + + private void resumeViews() { + if ((mExecutionThread!=null)&&(mExecutionThread.isAlive())) { + if (DEBUG) Log.d(TAG,"resumeViews: Thread in execution"); + return; + } + + if (!mLockPatternUtils.savedFingerExists()) { + if (DEBUG) Log.d(TAG,"resumeViews: No saved finger"); + // By design, this situation should never happen. + // If finger lock is in use, we should only allow the finger settings menu + // to delete all enrolled fingers. Other applications, like TSMDemo, should + // not get access to the database. + // + // Simply disable the finger lock and exit. + mLockPatternUtils.setLockFingerEnabled(false); + return; + } + + // reset header + resetStatusInfo(); + if (DEBUG) Log.d(TAG,"resumeViews: m_bVerifyied=" + m_bVerifyied); + + // show "forgot pattern?" button if we have an alternate authentication method + mForgotPatternButton.setVisibility(View.INVISIBLE); + + // if the user is currently locked out, enforce it. + long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); + if (deadline != 0) { + if (DEBUG) Log.d(TAG,"resumeViews: In lockout state"); + handleAttemptLockout(deadline); + } else { + // The onFinish() method of the CountDownTimer object would not be + // called when the screen is off. So we need to reset the m_bAttemptLockout + // if the lockout has finished. + m_bAttemptLockout = false; + } + + // the footer depends on how many total attempts the user has failed + if (mCallback.isVerifyUnlockOnly()) { + updateFooter(FooterMode.VerifyUnlocked); + } else if (mEnableFallback && + (mTotalFailedPatternAttempts >= mLockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { + updateFooter(FooterMode.ForgotLockPattern); + } else { + updateFooter(FooterMode.Normal); + } + + refreshEmergencyButtonText(); + } + + /** {@inheritDoc} */ + public void onResume() { + // Resume the finger unlock screen. + resumeViews(); + + // Launch a TSM Verify thread when the screen is turned on. + if (mPM.isScreenOn()) { + onScreenOn(); + } + } + + private void onScreenOn() { + if (DEBUG) Log.d(TAG,"onScreenOn()"); + + m_bPaused = false; + if (m_bSendDelayedCancel) { + /* If "cancel" has not been sent out, simply ignore it. */ + m_bSendDelayedCancel = false; + } + + /* Thread in execution, no need to create & start again. */ + if ((mExecutionThread!=null)&&(mExecutionThread.isAlive())) { + if (DEBUG) Log.d(TAG,"onScreenOn: Thread in execution"); + if (m_bCancelHasBeenSent) { + /* If "cancel" has been sent, set this flag to indicate the verify runner to start again. */ + m_bStartAgain = true; + } + return; + } + + // Temporary hints for the emulator. + mUserPrompt.setText(R.string.keyguard_finger_screen_on); + + if (mLockPatternUtils.isLockFingerEnabled()&&!m_bVerifyied) { + /** + * acquire the handoff lock that will keep the cpu running. this will + * be released once the fingerprint keyguard has set the verification up + * and poked the mWakelock of itself (not the one of the KeyguradViewMediator). + */ + mHandOffWakeLock.acquire(); + + // Create a thread for verification. + mExecutionThread = new Thread(mVerifyRunner); + + // Only start the verification when the screen is not in lockout state. + if (!m_bAttemptLockout) + { + mExecutionThread.start(); + } + else + { + // Release the wakelock early if the screen is in lockout state now. + pokeWakelock(1000); + } + } + } + + /** {@inheritDoc} */ + public void cleanUp() { + if (mExecutionThread != null && mExecutionThread.isAlive()) { + if (!m_bGfxEngineAttached) { + if (DEBUG) Log.d(TAG,"cleanUp send cancel delayed"); + m_bSendDelayedCancel = true; + } else { + if (DEBUG) Log.d(TAG,"cleanUp send cancel"); + if (!m_bCancelHasBeenSent) + { + GfxEngineRelayService.queueEvent("#cancel"); + m_bCancelHasBeenSent = true; + } + } + } + + // must make sure that the verify runner has terminated. + while (mExecutionThread != null && mExecutionThread.isAlive()) { + try { + // Set a flag to indicate the verify runner to terminate itself. + m_bPaused = true; + mUiThread.sleep(50); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + mUpdateMonitor.removeCallback(this); + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + if (hasWindowFocus) { + // when timeout dialog closes we want to update our state + //onResume(); + if (DEBUG) Log.d(TAG,"onWindowFocusChanged"); + resumeViews(); + + // Start the verification if screen is on. + if (mPM.isScreenOn()) { + if ( (!m_bAttemptLockout) && (!m_bPaused) && + (mExecutionThread != null) && (!(mExecutionThread.isAlive())) ) + { + if (DEBUG) Log.d(TAG,"Screen is on, start LAP verification"); + /** + * acquire the handoff lock that will keep the cpu running. this will + * be released once the fingerprint keyguard has set the verification up + * and poked the mWakelock of itself (not the one of the KeyguradViewMediator). + */ + mHandOffWakeLock.acquire(); + + if (mExecutionThread.getState() == Thread.State.TERMINATED) + { + // If the thread state is TERMINATED, it cannot be start() again. + // Create a thread for verification. + mExecutionThread = new Thread(mVerifyRunner); + mExecutionThread.start(); + } + else + { + if (DEBUG) Log.d(TAG,"Verify thread exists, just start it"); + mExecutionThread.start(); + } + } + } + } + } + + public final void runOnUiThread(Runnable action) { + if (Thread.currentThread() != mUiThread) { + mHandler .post(action); + } else { + action.run(); + } + } + + private void pokeWakelock(int holdMs) { + synchronized (this) { + if (DEBUG) Log.d(TAG, "pokeWakelock(" + holdMs + ")"); + mWakeLock.acquire(); + mHandler.removeMessages(TIMEOUT); + mWakelockSequence++; + Message msg = mHandler.obtainMessage(TIMEOUT, mWakelockSequence, 0); + mHandler.sendMessageDelayed(msg, holdMs); + } + + if (mHandOffWakeLock.isHeld()) { + /** + * The fingerprint keyguard has been set up, and has poked the keyguard + * main wake lock. It's time to release the handoff wake lock. + */ + mHandOffWakeLock.release(); + } + } + + /** + * This handler will be associated with the policy thread, which will also + * be the UI thread of the keyguard. Since the apis of the policy, and therefore + * this class, can be called by other threads, any action that directly + * interacts with the keyguard ui should be posted to this handler, rather + * than called directly. + */ + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case TIMEOUT: + handleTimeout(msg.arg1); + return ; + } + } + }; + + /** + * Handles the message sent by {@link #pokeWakelock} + * @param seq used to determine if anything has changed since the message + * was sent. + * @see #TIMEOUT + */ + private void handleTimeout(int seq) { + synchronized (this) { + if (DEBUG) Log.d(TAG, "handleTimeout"); + if (seq == mWakelockSequence) { + mWakeLock.release(); + } + } + } + + /** + * Provides a Runnable class to handle the finger + * verification + */ + private class VerifyRunner implements Runnable { + public void run() { + int iResult = 0; + boolean bRetryAfterLockout = false; + + m_bVerifyied = false; + + // A trick to dismiss the timeout dialog. + mCallback.isVerifyUnlockOnly(); + + try { + while (true) { + if (m_bPaused) { + if (DEBUG) Log.d(TAG,"VerifyRunner: paused before the verify() call."); + /* Do not call the verify interface if the "cancel" comes before it starts. */ + if (AuthentecHelper.eAM_STATUS_OK == iResult) { + /* The result should never be eAM_STATUS_OK. */ + iResult = AuthentecHelper.eAM_STATUS_USER_CANCELED; + } + break; + } + + iResult = fingerhelper.verifyPolicy(m_Context); + + if (DEBUG) Log.d(TAG,"Verify result=" + iResult); + if (AuthentecHelper.eAM_STATUS_CREDENTIAL_LOCKED == iResult) { + Log.e(TAG, "Credential locked!"); + //runOnUiThread(new Runnable() { + //public void run() { + //toast(getContext().getString(R.string.keyguard_finger_failed_to_unlock)); + //} + //}); + } else if (AuthentecHelper.eAM_STATUS_USER_CANCELED == iResult) { + if (m_bStartAgain) { + Log.e(TAG, "Cancel OK, continue because of the successive launch."); + m_bStartAgain = false; + m_bPaused = false; + } else { + break; + } + } else { + // Terminate the current thread for all other cases. + break; + } + + if (m_bPaused) { + /* Break immediatly without the sleep. */ + if (DEBUG) Log.d(TAG,"VerifyRunner: paused after the verify() call."); + break; + } + + // Give other tasks a chance. + Thread.sleep(10); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + try { + switch (iResult) { + case AuthentecHelper.eAM_STATUS_OK: + m_bVerifyied = true; + if (DEBUG) Log.d(TAG,"keyguardDone, m_bVerifyied=" + m_bVerifyied); + mCallback.keyguardDone(true); + mHandler.removeMessages(TIMEOUT); + mWakeLock.release(); + break; + + case AuthentecHelper.eAM_STATUS_USER_CANCELED: + // Power off. + Log.e(TAG, "Simulating device lock.\nYou may not cancel!"); + break; + + case AuthentecHelper.eAM_STATUS_CREDENTIAL_LOCKED: + // When m_bPaused becomes true. + bRetryAfterLockout = true; + break; + + case AuthentecHelper.eAM_STATUS_NO_STORED_CREDENTIAL: + // Should never happen. + if (DEBUG) Log.d(TAG,"No stored credential"); + bRetryAfterLockout = true; + break; + + case AuthentecHelper.eAM_STATUS_LIBRARY_NOT_AVAILABLE: + Log.e(TAG, "Library failed to load... cannot proceed!"); + bRetryAfterLockout = true; + break; + + case AuthentecHelper.eAM_STATUS_UI_TIMEOUT: + Log.e(TAG, "UI timeout!"); + runOnUiThread(new Runnable() { + public void run() { + toast(getContext().getString(R.string.keyguard_finger_ui_timeout)); + } + }); + bRetryAfterLockout = true; + break; + + case AuthentecHelper.eAM_STATUS_UNKNOWN_ERROR: + Log.e(TAG, "Unkown error!"); + bRetryAfterLockout = true; + break; + + default: + Log.e(TAG, "Other results: " + iResult); + bRetryAfterLockout = true; + } + } catch (Exception e){} + + if (!m_bPaused && bRetryAfterLockout) { + if (DEBUG) Log.d(TAG,"Error happens, retry after lock out"); + runOnUiThread(new Runnable() { + public void run() { + pokeWakelock(1000); + long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); + handleAttemptLockout(deadline); + } + }); + } + } + } + + private void handleAttemptLockout(long elapsedRealtimeDeadline) { + // Put the key guard into lockout state. + m_bAttemptLockout = true; + // Cancel the Verify thread. + if (mExecutionThread != null && mExecutionThread.isAlive()) { + if (!m_bGfxEngineAttached) { + if (DEBUG) Log.d(TAG,"Lockout send cancel delayed"); + m_bSendDelayedCancel = true; + } else { + if (DEBUG) Log.d(TAG,"Lockout send cancel"); + if (!m_bCancelHasBeenSent) + { + GfxEngineRelayService.queueEvent("#cancel"); + m_bCancelHasBeenSent = true; + } + } + } + + long elapsedRealtime = SystemClock.elapsedRealtime(); + mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) { + + @Override + public void onTick(long millisUntilFinished) { + int secondsRemaining = (int) (millisUntilFinished / 1000); + //mInstructions = getContext().getString( + // R.string.lockscreen_too_many_failed_attempts_countdown, + // secondsRemaining); + //updateStatusLines(); + mUserPrompt.setText(getContext().getString( + R.string.lockscreen_too_many_failed_attempts_countdown, + secondsRemaining)); + } + + @Override + public void onFinish() { + // Put the key guard out of lockout state. + if (DEBUG) Log.d(TAG,"handleAttemptLockout: onFinish"); + m_bAttemptLockout = false; + // "keyguard_finger_please_swipe" string would be showed when the TSM is ready. + // mInstructions = getContext().getString(R.string.lockscreen_pattern_instructions); + //updateStatusLines(); + resetStatusInfo(); + // TODO mUnlockIcon.setVisibility(View.VISIBLE); + mFailedPatternAttemptsSinceLastTimeout = 0; + if (mEnableFallback) { + updateFooter(FooterMode.ForgotLockPattern); + } else { + updateFooter(FooterMode.Normal); + } + + // Show the "Screen off" status + if (m_bPaused) { + mUserPrompt.setText(R.string.keyguard_finger_screen_off); + } else { + mUserPrompt.setText(""); + } + + // Start the verification if the finger key guard is holding the wakelock. + if ( (!m_bPaused) && (mExecutionThread != null) && (!(mExecutionThread.isAlive())) ) { + /** + * acquire the handoff lock that will keep the cpu running. this will + * be released once the fingerprint keyguard has set the verification up + * and poked the mWakelock of itself (not the one of the KeyguradViewMediator). + */ + mHandOffWakeLock.acquire(); + if (mExecutionThread.getState() == Thread.State.TERMINATED) { + // If the thread state is TERMINATED, it cannot be start() again. + // Create a thread for verification. + mExecutionThread = new Thread(mVerifyRunner); + mExecutionThread.start(); + } else { + if (DEBUG) Log.d(TAG,"Verify thread exists, just start it"); + mExecutionThread.start(); + } + } + } + }.start(); + } + + public void onPhoneStateChanged(String newState) { + refreshEmergencyButtonText(); + } + + /* handleShow() is called when the GfxEngine sends "show <target>" */ + private void handleShow(String target) + { + if (DEBUG) Log.w(TAG, "'show' target: " + target); + + // Has the object paused? + if (m_bPaused) + { + if (DEBUG) Log.d(TAG,"handleShow: paused"); + return; + } + + if (m_bAttemptLockout) + { + if (DEBUG) Log.d(TAG,"handleShow: Locked out"); + return; + } + + /* if the target is C1-C10 or D1-D10, ignore the command */ + if (target.matches("[CD][1-9]0?$")) return; + + /* if the target is please_swipe, show the UI command to the user: */ + if (target.equals("please_swipe")) { + /* update the UI */ + runOnUiThread(new Runnable() { + public void run() { + mUserPrompt.setText(R.string.keyguard_finger_please_swipe); + // How long we stay awake once the system is ready for a user to enter a pattern. + pokeWakelock(UNLOCK_FINGER_WAKE_INTERVAL_MS); + } + }); + return; + } + + if (target.equals("swipe_good")) { + runOnUiThread(new Runnable() { + public void run() { + toast(getContext().getString(R.string.keyguard_finger_match)); + } + }); + return; + } + /* we'll be asked to show swipe_bad if the finger does not verify. */ + /* this could be following a usage feedback message, in which case */ + /* we've already displayed the feedback, so we don't want to worry */ + /* about an additional message. */ + if (target.equals("swipe_bad")) { + // Update the total failed attempts. + mTotalFailedPatternAttempts++; + mFailedPatternAttemptsSinceLastTimeout++; + + if (!mbFeedbackDelivered) { + runOnUiThread(new Runnable() { + public void run() { + toast(getContext().getString(R.string.keyguard_finger_not_match)); + mCallback.reportFailedUnlockAttempt(); + if (mFailedPatternAttemptsSinceLastTimeout >= + LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { + pokeWakelock(1000); + long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); + handleAttemptLockout(deadline); + } + } + }); + } else { + runOnUiThread(new Runnable() { + public void run() { + mCallback.reportFailedUnlockAttempt(); + if (mFailedPatternAttemptsSinceLastTimeout >= + LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { + pokeWakelock(1000); + long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); + handleAttemptLockout(deadline); + } + } + }); + } + + mbFeedbackDelivered = false; + return; + } + + /* if the target is any of our feedback messages, provide a toast... */ + if (target.equals("swipe_too_fast")) { + mbFeedbackDelivered = true; + runOnUiThread(new Runnable() { + public void run() { + toast(getContext().getString(R.string.keyguard_finger_swipe_too_fast)); + } + }); + return; + } + if (target.equals("swipe_too_slow")) { + mbFeedbackDelivered = true; + runOnUiThread(new Runnable() { + public void run() { + toast(getContext().getString(R.string.keyguard_finger_swipe_too_slow)); + } + }); + return; + } + if (target.equals("swipe_too_short")) { + mbFeedbackDelivered = true; + runOnUiThread(new Runnable() { + public void run() { + toast(getContext().getString(R.string.keyguard_finger_swipe_too_short)); + } + }); + return; + } + if (target.equals("swipe_too_skewed")) { + mbFeedbackDelivered = true; + runOnUiThread(new Runnable() { + public void run() { + toast(getContext().getString(R.string.keyguard_finger_swipe_too_skewed)); + } + }); + return; + } + if (target.equals("swipe_too_far_left")) { + mbFeedbackDelivered = true; + runOnUiThread(new Runnable() { + public void run() { + toast(getContext().getString(R.string.keyguard_finger_swipe_too_far_left)); + } + }); + return; + } + if (target.equals("swipe_too_far_right")) { + mbFeedbackDelivered = true; + runOnUiThread(new Runnable() { + public void run() { + toast(getContext().getString(R.string.keyguard_finger_swipe_too_far_right)); + } + }); + return; + } + + /* if we get here, we did not recognize the target... */ + if (DEBUG) Log.w(TAG, "Unhandled 'show' target: " + target); + } + + /* handleHide() is called when the GfxEngine sends "hide <target>" */ + private void handleHide(String target) + { + // Has the object paused? + if (m_bPaused) + { + if (DEBUG) Log.d(TAG,"handleHide: paused"); + return; + } + + /* if the target is C1-C10 or D1-D10, ignore the command */ + if (target.matches("[CD][1-9]0?$")) return; + + /* if the target is please_swipe, remove the user prompt */ + if (target.equals("please_swipe")) + { + runOnUiThread(new Runnable() { + public void run() { + mUserPrompt.setText(""); + /** + * acquire the handoff lock that will keep the cpu running. this will + * be released once the fingerprint keyguard has set the next verification + * up and poked the mWakelock of itself (not the one of the KeyguradViewMediator). + */ + mHandOffWakeLock.acquire(); + + if (mTactileFeedbackEnabled) { + // Generate tactile feedback + if (DEBUG) Log.d(TAG,"Finger on vibration"); + vibe.vibrate(mVibePattern, -1); + } + } + }); + return; + } + + /* if the target is any of our feedback messages, nothing to hide... */ + + /* if we get here, we did not recognize the target... */ + if (DEBUG) Log.w(TAG, "Unhandled 'hide' target: " + target); + } + + /* receiveCommand() is called by a package-local component of the */ + /* relay server, any time a command is received from the GfxEngine. */ + public void receiveCommand(String command, String args) { + /* process the command: */ + + /* for "screen" without parameters, the GfxEngine is done with us... + * we expect to receive exactly two screen commands during our + * lifetime: + * command=="screen", args=="<something here>" + * and + * command=="screen", args=="" + * The first pattern will be the very first command we receive, and + * the second will be the very last command we receive. The argument + * in the first pattern will be indicative of the reason the system + * is presenting a UI; if our activity/service is intended to manage + * multiple screen types, we can use this to distinguish which one to + * present. + */ + if (command.equals("screen") && ((args == null) || (args.length() == 0))) { + // in this case we received "screen" without any arguments... if + // our activity had been launched by the GfxEngine, we would key on + // this to terminate. in this demo, our activity is around before + // the GfxEngine, and we trigger it rather than it triggering us, + // so we can safely ignore this command. + + // we just received "screen" with no arguments... the connection is breaking. + if (DEBUG) Log.w(TAG, "receiveCommand: the connection is breaking"); + m_bGfxEngineAttached = false; + m_bCancelHasBeenSent = false; + return; + } + else if (command.equals("screen")) { + // we don't need to do anything here unless we care about the name + // of the screen being instantiated (in which case we are interested + // in the value of args) + + // we're attached to the GfxEngine, manage delayed cancelation. + if (DEBUG) Log.w(TAG, "receiveCommand: attatched to the GfxEngine"); + m_bGfxEngineAttached = true; + if ((m_bSendDelayedCancel) && (!m_bCancelHasBeenSent)) { + if (DEBUG) Log.w(TAG, "receiveCommand: cancel delayed"); + if (mExecutionThread != null && mExecutionThread.isAlive()) { + if (DEBUG) Log.w(TAG, "receiveCommand: send delayed #cancel"); + GfxEngineRelayService.queueEvent("#cancel"); + m_bCancelHasBeenSent = true; + m_bSendDelayedCancel = false; + } + } + return; + } + + /* if the command is "show" or "hide", we're being asked to show or + * hide some named UI element. In some cases, the UI elements may not + * actually exist, in which case the command can probably be ignored. + * based on the name of the element to be shown / hidden, we might + * make different choices. An element may exist to tell the user to do + * something: the element 'please_swipe' is used to inform the user + * that the system is waiting for their finger to be swiped IF the + * element is being shown. However, if the element is being hidden, + * the system is acknowledging that the user has started their swipe. + * It is normal to start a timer when "show please_swipe" is seen, and + * if the timer expires before "hide please_swipe" occurs, it's normal + * to send back a '#timeout' event. + * Other elements exist for the purpose of providing the user with + * usage feedback; these elements will be shown but not hidden. It is + * this activity's responsibility to decide when/if they should be + * taken down from the display. Often these types of feedback will be + * presented through an Android 'toast'. Examples of these feedback + * messages include: swipe_good, swipe_too_fast, swipe_too_slow, + * sensor_dirty, swipe_too_short, swipe_too_skewed, + * swipe_too_far_right, swipe_too_far_left. + * Generally it is believed that these element names explain the reason + * they will be shown, but there are some caveats to be aware of. + * + * swipe_too_far_[right|left] may be inaccurate. That is, the + * left message may be shown when right is appropriate, and the reverse + * is true. The issue here is that the sensor can be mounted upside + * down, and the user could swipe in the opposite direction versus + * what the hardware design intends. As long as the swipe is in the + * center of the sensor, neither of those possibilities will have a + * negative effect on the operation, however they can cause the + * confusion. It is recommended that the same response be used for + * each of these conditions, and that the response merely tell the + * user two swipe in the center of the sensor. + * + * Some of these messages will accompany another message immediately + * following: swipe_bad. It is tempting to simply ignore the swipe_bad + * message if the more direct message is being presented, however the + * swipe_bad message will also be sent (without other feedback) in the + * case of a good swipe failing to correctly verify against the + * database; the swipe_bad element is the only no-match notification. + * + * Other elements that could be shown or hidden include C1 through C10 + * and D1 through D10. These elements are only useful in the event + * that our UI includes a set of fingers and wishes to highlight + * certain fingers for some reason. If the UI does not include such a + * requirement, these elements can be ignored. + * + */ + if (command.equals("show")) { + handleShow(args); + return; + } + + if (command.equals("hide")) { + handleHide(args); + return; + } + + if (command.equals("settext")) { + // This can generally be ignored. For UIs that get invoked on + // behalf of multiple different applications, we expect to receive + // a "settext appname <app name here>" command, in case we want to + // tell the user which application is needing their fingerprint in + // order to return a credential from the database. + return; + } + + if (DEBUG) Log.w(TAG, "Unhandled command: " + command); + } + + private void toast(final String s) + { + runOnUiThread(new Runnable() { + public void run() + { + mErrorPrompt.setText(s); + mCountdownTimerToast = new CountDownTimer(1000, 1000) { + @Override + public void onFinish() { + mErrorPrompt.setText(""); + } + + @Override + public void onTick(long millisUntilFinished) { + // TODO Auto-generated method stub + } + }.start(); + } + }); + } + + public void onMusicChanged() { + // refreshPlayingTitle(); + } +} diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index 8196a62cc2e..af4e49ef610 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -374,7 +374,8 @@ public class KeyguardViewMediator implements KeyguardViewCallback, Settings.System.SCREEN_LOCK_SLIDE_DELAY_TOGGLE, 0) == 1; boolean securityLockScreenEnabled = mLockPatternUtils.isLockPasswordEnabled() - || mLockPatternUtils.isLockPatternEnabled(); + || mLockPatternUtils.isLockPatternEnabled() + || mLockPatternUtils.isLockFingerEnabled(); boolean slideLockTimeoutShouldBeConsidered = separateSlideLockTimeoutEnabled && securityLockScreenEnabled; diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index ca12c8795b4..8e7a27694e7 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -113,6 +113,11 @@ public class LockPatternKeyguardView extends KeyguardViewBase { Pattern, /** + * Unlock by swiping a finger. + */ + Finger, + + /** * Unlock by entering a sim pin. */ SimPin, @@ -170,6 +175,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase { */ private Configuration mConfiguration; + /** + * Used to dismiss the timeout dialog + */ + private AlertDialog mTimeoutDialog; + + private Runnable mRecreateRunnable = new Runnable() { public void run() { recreateScreens(); @@ -209,6 +220,17 @@ public class LockPatternKeyguardView extends KeyguardViewBase { mUpdateMonitor = updateMonitor; mLockPatternUtils = lockPatternUtils; mWindowController = controller; + mTimeoutDialog = null; + + // By design, this situation should never happen. + // If finger lock is in use, we should only allow the finger settings menu + // to delete all enrolled fingers. Other applications, like TSMDemo, should + // not get access to the database. + // + // Simply disable the finger key guard since it will fail to work in this case. + if (mLockPatternUtils.isLockFingerEnabled() && !mLockPatternUtils.savedFingerExists()) { + mLockPatternUtils.setLockFingerEnabled(false); + } int LockscreenDisableOnSecurityValue = Settings.System.getInt( mContext.getContentResolver(), Settings.System.LOCKSCREEN_DISABLE_ON_SECURITY, 3); @@ -284,6 +306,13 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } public boolean isVerifyUnlockOnly() { + // This is a good place to dismiss the timeout dialog if there is one. + if (mTimeoutDialog != null) { + if (mTimeoutDialog.isShowing()) { + mTimeoutDialog.dismiss(); + } + mTimeoutDialog = null; + } return mIsVerifyUnlockOnly; } @@ -330,11 +359,13 @@ public class LockPatternKeyguardView extends KeyguardViewBase { " (enableFallback=" + mEnableFallback + ")"); final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; - if (usingLockPattern && mEnableFallback && failedAttempts == + final boolean usingLockFinger = mLockPatternUtils.getKeyguardStoredPasswordQuality() + == DevicePolicyManager.PASSWORD_QUALITY_FINGER; + if ((usingLockPattern || usingLockFinger) && mEnableFallback && failedAttempts == (LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { showAlmostAtAccountLoginDialog(); - } else if (usingLockPattern && mEnableFallback + } else if ((usingLockPattern || usingLockFinger) && mEnableFallback && failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { mLockPatternUtils.setPermanentlyLocked(true); updateScreen(mMode); @@ -457,6 +488,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { @Override public void onScreenTurnedOff() { + Log.d(TAG, "onScreenTurnedOff()"); mScreenOn = false; mForgotPattern = false; if (mMode == Mode.LockScreen) { @@ -468,6 +500,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { @Override public void onScreenTurnedOn() { + Log.d(TAG, "onScreenTurnedOn()"); mScreenOn = true; if (mMode == Mode.LockScreen) { ((KeyguardScreen) mLockScreen).onResume(); @@ -566,6 +599,9 @@ public class LockPatternKeyguardView extends KeyguardViewBase { case Pattern: secure = mLockPatternUtils.isLockPatternEnabled(); break; + case Finger: + secure = mLockPatternUtils.isLockFingerEnabled(); + break; case SimPin: secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED || mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED; @@ -674,6 +710,18 @@ public class LockPatternKeyguardView extends KeyguardViewBase { "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback); view.setEnableFallback(mEnableFallback); unlockView = view; + } else if (unlockMode == UnlockMode.Finger) { + FingerUnlockScreen view = new FingerUnlockScreen( + mContext, + mConfiguration, + mLockPatternUtils, + mUpdateMonitor, + mKeyguardScreenCallback, + mUpdateMonitor.getFailedAttempts()); + if (DEBUG) Log.d(TAG, + "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback); + view.setEnableFallback(mEnableFallback); + unlockView = view; } else if (unlockMode == UnlockMode.SimPin) { unlockView = new SimUnlockScreen( mContext, @@ -727,9 +775,12 @@ public class LockPatternKeyguardView extends KeyguardViewBase { if (stuckOnLockScreenBecauseSimMissing() || (simState == IccCard.State.PUK_REQUIRED)) { return Mode.LockScreen; } else { - // Disable LockScreen if security lockscreen is active and option in CMParts set - // Also don't show the slider lockscreen if pin is required - if (mLockscreenDisableOnSecurity && isSecure() || (simState == IccCard.State.PIN_REQUIRED)) { + // Show LockScreen first for any screen other than Pattern unlock and Finger unlock. + final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() + == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; + final boolean usingLockFinger = mLockPatternUtils.getKeyguardStoredPasswordQuality() + == DevicePolicyManager.PASSWORD_QUALITY_FINGER; + if (mLockscreenDisableOnSecurity && isSecure() && (usingLockPattern || usingLockFinger) || (simState == IccCard.State.PIN_REQUIRED)) { return Mode.UnlockScreen; } else { return Mode.LockScreen; @@ -753,11 +804,14 @@ public class LockPatternKeyguardView extends KeyguardViewBase { case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: currentMode = UnlockMode.Password; break; + case DevicePolicyManager.PASSWORD_QUALITY_FINGER: case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: - // "forgot pattern" button is only available in the pattern mode... + // "forgot pattern" button is only available in the pattern mode and finger mode... if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) { currentMode = UnlockMode.Account; + } else if (mode == DevicePolicyManager.PASSWORD_QUALITY_FINGER) { + currentMode = UnlockMode.Finger; } else { currentMode = UnlockMode.Pattern; } @@ -771,10 +825,20 @@ public class LockPatternKeyguardView extends KeyguardViewBase { private void showTimeoutDialog() { int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; - String message = mContext.getString( + String message; + + if (mUnlockScreenMode == UnlockMode.Finger) { + message = mContext.getString( + R.string.lockscreen_too_many_failed_fingers_dialog_message, + mUpdateMonitor.getFailedAttempts(), + timeoutInSeconds); + } else { + message = mContext.getString( R.string.lockscreen_too_many_failed_attempts_dialog_message, mUpdateMonitor.getFailedAttempts(), timeoutInSeconds); + } + final AlertDialog dialog = new AlertDialog.Builder(mContext) .setTitle(null) .setMessage(message) @@ -787,17 +851,30 @@ public class LockPatternKeyguardView extends KeyguardViewBase { WindowManager.LayoutParams.FLAG_BLUR_BEHIND, WindowManager.LayoutParams.FLAG_BLUR_BEHIND); } + mTimeoutDialog = dialog; dialog.show(); } private void showAlmostAtAccountLoginDialog() { int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; - String message = mContext.getString( + String message; + + if (mUnlockScreenMode == UnlockMode.Finger) { + message = mContext.getString( + R.string.lockscreen_failed_fingers_almost_glogin, + LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET + - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, + LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, + timeoutInSeconds); + } else { + message = mContext.getString( R.string.lockscreen_failed_attempts_almost_glogin, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); + } + final AlertDialog dialog = new AlertDialog.Builder(mContext) .setTitle(null) .setMessage(message) @@ -810,6 +887,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase { WindowManager.LayoutParams.FLAG_BLUR_BEHIND, WindowManager.LayoutParams.FLAG_BLUR_BEHIND); } + mTimeoutDialog = dialog; dialog.show(); } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 1538003450b..e1138462019 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -503,6 +503,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { switch (quality) { case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: + case DevicePolicyManager.PASSWORD_QUALITY_FINGER: case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: @@ -749,16 +750,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_RESET_PASSWORD); quality = getPasswordQuality(null); - if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { - int realQuality = LockPatternUtils.computePasswordQuality(password); - if (realQuality < quality) { - Slog.w(TAG, "resetPassword: password quality 0x" - + Integer.toHexString(quality) - + " does not meet required quality 0x" - + Integer.toHexString(quality)); + + if ((flags == DevicePolicyManager.ENABLE_FINGER_LOCK) || + (flags == DevicePolicyManager.DISABLE_FINGER_LOCK)) { + // Log.d("DevicePolicyManagerService", "Enable/Disable finger lock"); + // do nothing. + } else { + if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { + int realQuality = LockPatternUtils.computePasswordQuality(password); + + if (realQuality < quality) { + Slog.w(TAG, "resetPassword: password quality 0x" + + Integer.toHexString(quality) + + " does not meet required quality 0x" + + Integer.toHexString(quality)); + return false; + } + quality = realQuality; + } + + int length = getPasswordMinimumLength(null); + if (password.length() < length) { + Slog.w(TAG, "resetPassword: password length " + password.length() + + " does not meet required length " + length); return false; } - quality = realQuality; } int length = getPasswordMinimumLength(null); if (password.length() < length) { @@ -773,19 +789,35 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slog.w(TAG, "resetPassword: already set by another uid and not entered by user"); return false; } - + // Don't do this with the lock held, because it is going to call // back in to the service. long ident = Binder.clearCallingIdentity(); try { LockPatternUtils utils = new LockPatternUtils(mContext); - utils.saveLockPassword(password, quality); - synchronized (this) { - int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) - != 0 ? callingUid : -1; - if (mPasswordOwner != newOwner) { - mPasswordOwner = newOwner; - saveSettingsLocked(); + + if ((flags == DevicePolicyManager.ENABLE_FINGER_LOCK) || + (flags == DevicePolicyManager.DISABLE_FINGER_LOCK)) { + if ((flags == DevicePolicyManager.ENABLE_FINGER_LOCK) && + utils.savedFingerExists() && + !utils.isLockFingerEnabled()) { + utils.setLockFingerEnabled(true); + } else if ((flags == DevicePolicyManager.DISABLE_FINGER_LOCK) && + utils.isLockFingerEnabled() && + !utils.savedFingerExists()) { + utils.setLockFingerEnabled(false); + } + + return true; + } else { + utils.saveLockPassword(password, quality); + synchronized (this) { + int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) + != 0 ? callingUid : -1; + if (mPasswordOwner != newOwner) { + mPasswordOwner = newOwner; + saveSettingsLocked(); + } } } } finally { |