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

Commit 69aa4a95 authored by Jim Miller's avatar Jim Miller
Browse files

Fix 2332563: Add password-lock support to lockscreen

parent 6d40ee33
Loading
Loading
Loading
Loading
+158 −17
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ public class LockPatternUtils {
    private static final String TAG = "LockPatternUtils";

    private static final String LOCK_PATTERN_FILE = "/system/gesture.key";
    private static final String LOCK_PASSWORD_FILE = "/system/password.key";

    /**
     * The maximum number of incorrect attempts before the user is prevented
@@ -69,6 +70,16 @@ public class LockPatternUtils {
     */
    public static final int MIN_LOCK_PATTERN_SIZE = 4;

    /**
     * Type of password being stored.
     * pattern = pattern screen
     * pin = digit-only password
     * password = alphanumeric password
     */
    public static final int MODE_PATTERN = 0;
    public static final int MODE_PIN = 1;
    public static final int MODE_PASSWORD = 2;

    /**
     * The minimum number of dots the user must include in a wrong pattern
     * attempt for it to be counted against the counts that affect
@@ -78,11 +89,13 @@ 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 = "lockscreen.patterneverchosen";
    private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen";
    public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type";

    private final ContentResolver mContentResolver;

    private static String sLockPatternFilename;
    private static String sLockPasswordFilename;

    /**
     * @param contentResolver Used to look up and save settings.
@@ -93,14 +106,17 @@ public class LockPatternUtils {
        if (sLockPatternFilename == null) {
            sLockPatternFilename = android.os.Environment.getDataDirectory()
                    .getAbsolutePath() + LOCK_PATTERN_FILE;
            sLockPasswordFilename = android.os.Environment.getDataDirectory()
                    .getAbsolutePath() + LOCK_PASSWORD_FILE;
        }

    }

    /**
     * Check to see if a pattern matches the saved pattern.  If no pattern exists,
     * always returns true.
     * @param pattern The pattern to check.
     * @return Whether the pattern matchees the stored one.
     * @return Whether the pattern matches the stored one.
     */
    public boolean checkPattern(List<LockPatternView.Cell> pattern) {
        try {
@@ -122,13 +138,40 @@ public class LockPatternUtils {
    }

    /**
     * Check to see if the user has stored a lock pattern.
     * @return Whether a saved pattern exists.
     * Check to see if a password matches the saved password.  If no password exists,
     * always returns true.
     * @param password The password to check.
     * @return Whether the password matches the stored one.
     */
    public boolean savedPatternExists() {
    public boolean checkPassword(String password) {
        try {
            // Read all the bytes from the file
            RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "r");
            final byte[] stored = new byte[(int) raf.length()];
            int got = raf.read(stored, 0, stored.length);
            raf.close();
            if (got <= 0) {
                return true;
            }
            // Compare the hash from the file with the entered password's hash
            return Arrays.equals(stored, LockPatternUtils.passwordToHash(password));
        } catch (FileNotFoundException fnfe) {
            return true;
        } catch (IOException ioe) {
            return true;
        }
    }

    /**
     * Checks to see if the given file exists and contains any data. Returns true if it does,
     * false otherwise.
     * @param filename
     * @return true if file exists and is non-empty.
     */
    private boolean nonEmptyFileExists(String filename) {
        try {
            // Check if we can read a byte from the file
            RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "r");
            RandomAccessFile raf = new RandomAccessFile(filename, "r");
            byte first = raf.readByte();
            raf.close();
            return true;
@@ -139,6 +182,22 @@ public class LockPatternUtils {
        }
    }

    /**
     * Check to see if the user has stored a lock pattern.
     * @return Whether a saved pattern exists.
     */
    public boolean savedPatternExists() {
        return nonEmptyFileExists(sLockPatternFilename);
    }

    /**
     * Check to see if the user has stored a lock pattern.
     * @return Whether a saved pattern exists.
     */
    public boolean savedPasswordExists() {
        return nonEmptyFileExists(sLockPasswordFilename);
    }

    /**
     * Return true if the user has ever chosen a pattern.  This is true even if the pattern is
     * currently cleared.
@@ -146,7 +205,7 @@ public class LockPatternUtils {
     * @return True if the user has ever chosen a pattern.
     */
    public boolean isPatternEverChosen() {
        return getBoolean(PATTERN_EVER_CHOSEN);
        return getBoolean(PATTERN_EVER_CHOSEN_KEY);
    }

    /**
@@ -166,7 +225,8 @@ public class LockPatternUtils {
                raf.write(hash, 0, hash.length);
            }
            raf.close();
            setBoolean(PATTERN_EVER_CHOSEN, true);
            setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
            setLong(PASSWORD_TYPE_KEY, MODE_PATTERN);
        } catch (FileNotFoundException fnfe) {
            // Cant do much, unless we want to fail over to using the settings provider
            Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename);
@@ -176,6 +236,38 @@ public class LockPatternUtils {
        }
    }

    /**
     * Save a lock password.
     * @param password The password to save
     */
    public void saveLockPassword(String password) {
        // Compute the hash
        boolean numericHint = password != null ? TextUtils.isDigitsOnly(password) : false;
        final byte[] hash  = LockPatternUtils.passwordToHash(password);
        try {
            // Write the hash to file
            RandomAccessFile raf = new RandomAccessFile(sLockPasswordFilename, "rw");
            // Truncate the file if pattern is null, to clear the lock
            if (password == null) {
                raf.setLength(0);
            } else {
                raf.write(hash, 0, hash.length);
            }
            raf.close();
            setLong(PASSWORD_TYPE_KEY, numericHint ? MODE_PIN : MODE_PASSWORD);
        } catch (FileNotFoundException fnfe) {
            // Cant do much, unless we want to fail over to using the settings provider
            Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
        } catch (IOException ioe) {
            // Cant do much
            Log.e(TAG, "Unable to save lock pattern to " + sLockPasswordFilename);
        }
    }

    public int getPasswordMode() {
        return (int) getLong(PASSWORD_TYPE_KEY, MODE_PATTERN);
    }

    /**
     * Deserialize a pattern.
     * @param string The pattern serialized with {@link #patternToString}
@@ -218,7 +310,7 @@ public class LockPatternUtils {
     * @param pattern the gesture pattern.
     * @return the hash of the pattern in a byte array.
     */
    static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
    private static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
        if (pattern == null) {
            return null;
        }
@@ -238,11 +330,55 @@ public class LockPatternUtils {
        }
    }

    /*
     * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
     * Not the most secure, but it is at least a second level of protection. First level is that
     * the file is in a location only readable by the system process.
     * @param password the gesture pattern.
     * @return the hash of the pattern in a byte array.
     */
     public static byte[] passwordToHash(String password) {
        if (password == null) {
            return null;
        }
        String algo = null;
        byte[] hashed = null;
        try {
            long salt = 0x2374868151054924L; // TODO: make this unique to device
            byte[] saltedPassword = (password + Long.toString(salt)).getBytes();
            byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
            byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
            hashed = (toHex(sha1) + toHex(md5)).getBytes();
        } catch (NoSuchAlgorithmException e) {
            Log.w(TAG, "Failed to encode string because of missing algorithm: " + algo);
        }
        return hashed;
    }

    private static String toHex(byte[] ary) {
        final String hex = "0123456789ABCDEF";
        String ret = "";
        for (int i = 0; i < ary.length; i++) {
            ret += hex.charAt((ary[i] >> 4) & 0xf);
            ret += hex.charAt(ary[i] & 0xf);
        }
        return ret;
    }

    /**
     * @return Whether the lock password is enabled.
     */
    public boolean isLockPasswordEnabled() {
        long mode = getLong(PASSWORD_TYPE_KEY, 0);
        return savedPasswordExists() && (mode == MODE_PASSWORD || mode == MODE_PIN);
    }

    /**
     * @return Whether the lock pattern is enabled.
     */
    public boolean isLockPatternEnabled() {
        return getBoolean(Settings.System.LOCK_PATTERN_ENABLED);
        return getBoolean(Settings.System.LOCK_PATTERN_ENABLED)
                && getLong(PASSWORD_TYPE_KEY, MODE_PATTERN) == MODE_PATTERN;
    }

    /**
@@ -361,5 +497,10 @@ public class LockPatternUtils {
        android.provider.Settings.System.putLong(mContentResolver, systemSettingKey, value);
    }


    public boolean isSecure() {
        long mode = getPasswordMode();
        boolean secure = mode == MODE_PATTERN && isLockPatternEnabled() && savedPatternExists()
            || (mode == MODE_PIN || mode == MODE_PASSWORD) && savedPasswordExists();
        return secure;
    }
}
+120 −0
Original line number Diff line number Diff line
<?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.
*/
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/background_dark">

    <!-- displays dots as user enters pin -->
    <LinearLayout android:id="@+id/pinDisplayGroup"
        android:orientation="horizontal"
        android:layout_centerInParent="true"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:addStatesFromChildren="true"
        android:gravity="center_vertical"
        android:baselineAligned="false"
        android:paddingRight="0dip"
        android:layout_marginRight="30dip"
        android:layout_marginLeft="30dip"
        android:layout_marginTop="6dip"
        android:background="@android:drawable/edit_text">

        <EditText android:id="@+id/pinDisplay"
            android:layout_width="0dip"
            android:layout_weight="1"
            android:layout_height="fill_parent"
            android:maxLines="1"
            android:background="@null"
            android:textSize="32sp"
            android:inputType="textPassword"
        />

        <ImageButton android:id="@+id/backspace"
             android:src="@android:drawable/ic_input_delete"
             android:layout_width="wrap_content"
             android:layout_height="fill_parent"
             android:layout_marginTop="2dip"
             android:layout_marginRight="2dip"
             android:layout_marginBottom="2dip"
             android:gravity="center"
        />

    </LinearLayout>

    <!-- header text ('Enter Pin Code') -->
    <TextView android:id="@+id/headerText"
        android:layout_above="@id/pinDisplayGroup"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="30dip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="24sp"
    />

    <LinearLayout
        android:orientation="horizontal"
        android:layout_alignParentBottom="true"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dip"
        android:layout_marginLeft="8dip"
        android:layout_marginRight="8dip">

        <Button android:id="@+id/ok"
            android:text="@android:string/ok"
            android:layout_alignParentBottom="true"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:layout_marginBottom="8dip"
            android:layout_marginRight="8dip"
            android:textSize="18sp"
            />

        <Button android:id="@+id/emergencyCall"
            android:text="@android:string/lockscreen_emergency_call"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:layout_marginBottom="8dip"
            android:layout_marginLeft="8dip"
            android:textSize="18sp"
            android:drawableLeft="@drawable/ic_emergency"
            android:drawablePadding="8dip"
        />
    </LinearLayout>

    <!-- Not currently visible on this screen -->
    <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:textAppearance="?android:attr/textAppearanceMedium"
        android:visibility="gone"
    />

</RelativeLayout>
+158 −0
Original line number Diff line number Diff line
<?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:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="#70000000"
    android:gravity="center_horizontal">

    <LinearLayout android:id="@+id/topDisplayGroup"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">

            <com.android.internal.widget.DigitalClock android:id="@+id/time"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="6dip"
                android:layout_marginLeft="6dip"
                android:layout_alignParentTop="true"
                android:layout_alignParentLeft="true">

                <TextView android:id="@+id/timeDisplay"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:gravity="bottom"
                    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="fill_parent"
                    android:gravity="bottom"
                    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/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:textAppearance="?android:attr/textAppearanceMedium"
            />

        </RelativeLayout>

        <!-- password entry -->
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginRight="6dip"
            android:layout_marginLeft="6dip"
            android:gravity="center_vertical"
            android:hint="@android:string/keyguard_password_enter_password_code"
            android:background="@android:drawable/edit_text">

            <!-- displays dots as user enters pin -->
            <TextView android:id="@+id/pinDisplay"
                android:layout_width="0dip"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:maxLines="1"
                android:textAppearance="?android:attr/textAppearanceLargeInverse"
                android:textStyle="bold"
                android:inputType="textPassword"
            />

            <ImageButton android:id="@+id/backspace"
                android:src="@android:drawable/ic_input_delete"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="-3dip"
                android:layout_marginBottom="-3dip"
            />
        </LinearLayout>

    </LinearLayout>

    <include
        android:id="@+id/keyPad"
        layout="@android:layout/twelve_key_entry"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/topDisplayGroup"
        android:layout_marginTop="10dip"
    />

    <!-- spacer below keypad -->
    <View
        android:id="@+id/spacerBottom"
        android:layout_width="fill_parent"
        android:layout_height="1dip"
        android:layout_marginTop="6dip"
        android:layout_above="@id/emergencyCall"
        android:background="@android:drawable/divider_horizontal_dark"
    />

    <!-- The emergency button should take the rest of the space and be centered vertically -->
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:gravity="center"
        android:orientation="vertical">

        <!-- emergency call button -->
        <Button
            android:id="@+id/emergencyCall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawableLeft="@android:drawable/ic_emergency"
            android:drawablePadding="8dip"
            android:text="@android:string/lockscreen_emergency_call"
        />
    </LinearLayout>

</LinearLayout>
+0 −138

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −145

File deleted.

Preview size limit exceeded, changes collapsed.

Loading