Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 34963839 authored by Todd Gurzick's avatar Todd Gurzick Committed by Emilio López
Browse files

Implement basic framework for fingerprint lockscreen

Uses reflection not to depend on proprietary jars

Change-Id: I86acf878ff51a14b3ca7d9962b62305f010e0b29
parent 05871713
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -194,7 +194,8 @@ LOCAL_SRC_FILES += \
	vpn/java/android/net/vpn/IVpnService.aidl \
	vpn/java/android/net/vpn/IVpnService.aidl \
	voip/java/android/net/sip/ISipSession.aidl \
	voip/java/android/net/sip/ISipSession.aidl \
	voip/java/android/net/sip/ISipSessionListener.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
#
#




+14 −1
Original line number Original line Diff line number Diff line
@@ -191,6 +191,13 @@ public class DevicePolicyManager {
     */
     */
    public static final int PASSWORD_QUALITY_SOMETHING = 0x10000;
    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
     * Constant for {@link #setPasswordQuality}: the user must have entered a
     * password containing at least numeric characters.  Note that quality
     * password containing at least numeric characters.  Note that quality
@@ -419,6 +426,12 @@ public class DevicePolicyManager {
     */
     */
    public static final int RESET_PASSWORD_REQUIRE_ENTRY = 0x0001;
    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
     * Force a new device unlock password (the password needed to access the
     * entire device, not for individual accounts) on the user.  This takes
     * entire device, not for individual accounts) on the user.  This takes
+60 −23
Original line number Original line Diff line number Diff line
@@ -101,8 +101,9 @@ public class LockPatternUtils {
    private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
    private final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently";
    private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
    private final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline";
    private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
    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_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 Context mContext;
    private final ContentResolver mContentResolver;
    private final ContentResolver mContentResolver;
@@ -250,6 +251,14 @@ public class LockPatternUtils {
        return sHaveNonZeroPasswordFile.get();
        return sHaveNonZeroPasswordFile.get();
    }
    }


    /**
     * 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
     * Return true if the user has ever chosen a pattern.  This is true even if the pattern is
     * currently cleared.
     * currently cleared.
@@ -287,6 +296,11 @@ public class LockPatternUtils {
                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
                }
                }
                break;
                break;
            case DevicePolicyManager.PASSWORD_QUALITY_FINGER:
                if (isLockFingerEnabled()) {
                    activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_FINGER;
                }
                break;
        }
        }
        return activePasswordQuality;
        return activePasswordQuality;
    }
    }
@@ -559,6 +573,15 @@ public class LockPatternUtils {
                        == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
                        == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
    }
    }


    /**
     * @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.
     * Set whether the lock pattern is enabled.
     */
     */
@@ -566,6 +589,18 @@ public class LockPatternUtils {
        setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
        setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled);
    }
    }


    /**
     * 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.
     * @return Whether the visible pattern is enabled.
     */
     */
@@ -897,10 +932,12 @@ public class LockPatternUtils {
    public boolean isSecure() {
    public boolean isSecure() {
        long mode = getKeyguardStoredPasswordQuality();
        long mode = getKeyguardStoredPasswordQuality();
        final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
        final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
        final boolean isFinger = mode == DevicePolicyManager.PASSWORD_QUALITY_FINGER;
        final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
        final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
                || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC;
        final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
        final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists()
                || isFinger && isLockFingerEnabled() && savedFingerExists()
                || isPassword && savedPasswordExists();
                || isPassword && savedPasswordExists();
        return secure;
        return secure;
    }
    }
+231 −0
Original line number Original line Diff line number Diff line
/*
 * 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());
    }
}
+127 −0
Original line number Original line Diff line number Diff line
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();
        }
    }
}
Loading