11f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project/*
21f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
31f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project *
41f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
51f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * you may not use this file except in compliance with the License.
61f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * You may obtain a copy of the License at
71f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project *
81f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
91f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project *
101f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
111f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
121f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * See the License for the specific language governing permissions and
141f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * limitations under the License.
151f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project */
161f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
171f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectpackage com.android.internal.policy.impl;
181f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
191f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.content.Context;
205475906f53e2d79807548932dfabbf9945143740Jim Millerimport android.content.res.Configuration;
211f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.os.CountDownTimer;
221f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.os.SystemClock;
235cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstromimport android.security.KeyStore;
241f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.view.LayoutInflater;
251f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.view.View;
261f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.view.MotionEvent;
271f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport android.widget.Button;
2816541e4f88bf645408de098d1368cb49dd09e4c3Daniel Sandlerimport android.util.Log;
291f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport com.android.internal.R;
301f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient;
311f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport com.android.internal.widget.LockPatternUtils;
321f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport com.android.internal.widget.LockPatternView;
3372f230e32ec889607f7d85594396cc3b002bae8cJim Millerimport com.android.internal.widget.LockPatternView.Cell;
341f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
351f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Projectimport java.util.List;
361f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
371f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project/**
381f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * This is the screen that shows the 9 circle unlock widget and instructs
391f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project * the user how to unlock their device, or make an emergency call.
401f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project */
4142b432d84cd3bf860a03a10e9191738e05f2eaddJim Millerclass PatternUnlockScreen extends LinearLayoutWithDefaultTouchRecepient
426b05d58018c2806459c121e507c005639b74aee9Jim Miller        implements KeyguardScreen {
431f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
443421c02956c3231dd2cc514402b5f96b9f288ef5JP Abgrall    private static final boolean DEBUG = false;
451f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private static final String TAG = "UnlockScreen";
461f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
471f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    // how long before we clear the wrong pattern
481f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
491f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
506fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller    // how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
511f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
521f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
536fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller    // how long we stay awake after the user hits the first dot.
546fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller    private static final int UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS = 2000;
556fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller
5672f230e32ec889607f7d85594396cc3b002bae8cJim Miller    // how many cells the user has to cross before we poke the wakelock
5772f230e32ec889607f7d85594396cc3b002bae8cJim Miller    private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
5872f230e32ec889607f7d85594396cc3b002bae8cJim Miller
591f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private int mFailedPatternAttemptsSinceLastTimeout = 0;
601f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private int mTotalFailedPatternAttempts = 0;
611f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private CountDownTimer mCountdownTimer = null;
621f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
638b886fab5496b0b1f5193f21855220176deddc37Jim Miller    private LockPatternUtils mLockPatternUtils;
648b886fab5496b0b1f5193f21855220176deddc37Jim Miller    private KeyguardUpdateMonitor mUpdateMonitor;
658b886fab5496b0b1f5193f21855220176deddc37Jim Miller    private KeyguardScreenCallback mCallback;
661f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
67d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen    /**
68d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen     * whether there is a fallback option available when the pattern is forgotten.
69d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen     */
7063b90f28858b9d259a1d4a145f51bdd36ada8730Dianne Hackborn    private boolean mEnableFallback;
71d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen
726b05d58018c2806459c121e507c005639b74aee9Jim Miller    private KeyguardStatusViewManager mKeyguardStatusViewManager;
731f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private LockPatternView mLockPatternView;
741f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
751f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    /**
761f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     * Keeps track of the last time we poked the wake lock during dispatching
771f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     * of the touch event, initalized to something gauranteed to make us
781f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     * poke it when the user starts drawing the pattern.
7947088bbbef61e3bd8d2930f4a2b72884129b5bd5Jim Miller     * @see #dispatchTouchEvent(android.view.MotionEvent)
801f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     */
811f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
821f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
831f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    /**
841f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     * Useful for clearing out the wrong pattern after a delay
851f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     */
861f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private Runnable mCancelPatternRunnable = new Runnable() {
871f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        public void run() {
881f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            mLockPatternView.clearPattern();
891f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
901f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    };
911f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
920d244193bed0c995e2beaff5e217200457b28b9dJim Miller    private final OnClickListener mForgotPatternClick = new OnClickListener() {
930d244193bed0c995e2beaff5e217200457b28b9dJim Miller        public void onClick(View v) {
940d244193bed0c995e2beaff5e217200457b28b9dJim Miller            mCallback.forgotPattern(true);
950d244193bed0c995e2beaff5e217200457b28b9dJim Miller        }
960d244193bed0c995e2beaff5e217200457b28b9dJim Miller    };
970d244193bed0c995e2beaff5e217200457b28b9dJim Miller
981f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private Button mForgotPatternButton;
995475906f53e2d79807548932dfabbf9945143740Jim Miller    private int mCreationOrientation;
1001f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1011f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    enum FooterMode {
1021f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        Normal,
1031f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        ForgotLockPattern,
1041f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        VerifyUnlocked
1051f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
1061f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1070d244193bed0c995e2beaff5e217200457b28b9dJim Miller    private void hideForgotPatternButton() {
1080d244193bed0c995e2beaff5e217200457b28b9dJim Miller        mForgotPatternButton.setVisibility(View.GONE);
1090d244193bed0c995e2beaff5e217200457b28b9dJim Miller    }
1100d244193bed0c995e2beaff5e217200457b28b9dJim Miller
1110d244193bed0c995e2beaff5e217200457b28b9dJim Miller    private void showForgotPatternButton() {
1120d244193bed0c995e2beaff5e217200457b28b9dJim Miller        mForgotPatternButton.setVisibility(View.VISIBLE);
1130d244193bed0c995e2beaff5e217200457b28b9dJim Miller    }
1140d244193bed0c995e2beaff5e217200457b28b9dJim Miller
1151f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private void updateFooter(FooterMode mode) {
1161f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        switch (mode) {
1171f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            case Normal:
1180d244193bed0c995e2beaff5e217200457b28b9dJim Miller                if (DEBUG) Log.d(TAG, "mode normal");
1190d244193bed0c995e2beaff5e217200457b28b9dJim Miller                hideForgotPatternButton();
1201f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                break;
1211f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            case ForgotLockPattern:
1220d244193bed0c995e2beaff5e217200457b28b9dJim Miller                if (DEBUG) Log.d(TAG, "mode ForgotLockPattern");
1230d244193bed0c995e2beaff5e217200457b28b9dJim Miller                showForgotPatternButton();
1241f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                break;
1251f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            case VerifyUnlocked:
1260d244193bed0c995e2beaff5e217200457b28b9dJim Miller                if (DEBUG) Log.d(TAG, "mode VerifyUnlocked");
1270d244193bed0c995e2beaff5e217200457b28b9dJim Miller                hideForgotPatternButton();
1281f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
1291f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
1301f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1311f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    /**
1321f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     * @param context The context.
1335475906f53e2d79807548932dfabbf9945143740Jim Miller     * @param configuration
1341f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     * @param lockPatternUtils Used to lookup lock pattern settings.
1351f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     * @param updateMonitor Used to lookup state affecting keyguard.
1361f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     * @param callback Used to notify the manager when we're done, etc.
1371f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     * @param totalFailedAttempts The current number of failed attempts.
138d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen     * @param enableFallback True if a backup unlock option is available when the user has forgotten
139d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen     *        their pattern (e.g they have a google account so we can show them the account based
140d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen     *        backup option).
1411f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project     */
14242b432d84cd3bf860a03a10e9191738e05f2eaddJim Miller    PatternUnlockScreen(Context context,
1435475906f53e2d79807548932dfabbf9945143740Jim Miller                 Configuration configuration, LockPatternUtils lockPatternUtils,
144d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen                 KeyguardUpdateMonitor updateMonitor,
145d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen                 KeyguardScreenCallback callback,
14663b90f28858b9d259a1d4a145f51bdd36ada8730Dianne Hackborn                 int totalFailedAttempts) {
1471f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        super(context);
1481f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternUtils = lockPatternUtils;
1491f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mUpdateMonitor = updateMonitor;
1501f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mCallback = callback;
1511f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mTotalFailedPatternAttempts = totalFailedAttempts;
1525475906f53e2d79807548932dfabbf9945143740Jim Miller        mFailedPatternAttemptsSinceLastTimeout =
1535475906f53e2d79807548932dfabbf9945143740Jim Miller            totalFailedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
1541f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
15516541e4f88bf645408de098d1368cb49dd09e4c3Daniel Sandler        if (DEBUG) Log.d(TAG,
15616541e4f88bf645408de098d1368cb49dd09e4c3Daniel Sandler            "UnlockScreen() ctor: totalFailedAttempts="
15716541e4f88bf645408de098d1368cb49dd09e4c3Daniel Sandler                 + totalFailedAttempts + ", mFailedPat...="
15847088bbbef61e3bd8d2930f4a2b72884129b5bd5Jim Miller                 + mFailedPatternAttemptsSinceLastTimeout
15916541e4f88bf645408de098d1368cb49dd09e4c3Daniel Sandler                 );
16016541e4f88bf645408de098d1368cb49dd09e4c3Daniel Sandler
1615475906f53e2d79807548932dfabbf9945143740Jim Miller        mCreationOrientation = configuration.orientation;
1625475906f53e2d79807548932dfabbf9945143740Jim Miller
1635475906f53e2d79807548932dfabbf9945143740Jim Miller        LayoutInflater inflater = LayoutInflater.from(context);
1648171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung
1655475906f53e2d79807548932dfabbf9945143740Jim Miller        if (mCreationOrientation != Configuration.ORIENTATION_LANDSCAPE) {
1668171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung            Log.d(TAG, "portrait mode");
1675475906f53e2d79807548932dfabbf9945143740Jim Miller            inflater.inflate(R.layout.keyguard_screen_unlock_portrait, this, true);
1681f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        } else {
1698171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung            Log.d(TAG, "landscape mode");
1705475906f53e2d79807548932dfabbf9945143740Jim Miller            inflater.inflate(R.layout.keyguard_screen_unlock_landscape, this, true);
1711f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
1721f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1736b05d58018c2806459c121e507c005639b74aee9Jim Miller        mKeyguardStatusViewManager = new KeyguardStatusViewManager(this, mUpdateMonitor,
174ebcd6bb1b9ac5f898621ba25c37f2e3ccd2ff33bAdam Cohen                mLockPatternUtils, mCallback, true);
1751f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
176add3930c01370ac2b5b1e052130e578487b42cdaKarl Rosaen        mLockPatternView = (LockPatternView) findViewById(R.id.lockPattern);
1771f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1780d244193bed0c995e2beaff5e217200457b28b9dJim Miller        mForgotPatternButton = (Button) findViewById(R.id.forgotPatternButton);
1791f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mForgotPatternButton.setText(R.string.lockscreen_forgot_pattern_button_text);
1800d244193bed0c995e2beaff5e217200457b28b9dJim Miller        mForgotPatternButton.setOnClickListener(mForgotPatternClick);
1811f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1821f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // make it so unhandled touch events within the unlock screen go to the
1831f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // lock pattern view.
1841f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        setDefaultTouchRecepient(mLockPatternView);
1851f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1861f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.setSaveEnabled(false);
1871f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.setFocusable(false);
1881f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.setOnPatternListener(new UnlockPatternListener());
1891f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1901f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // stealth mode will be the same for the life of this screen
1911f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled());
1921f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1931f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // vibrate mode will be the same for the life of this screen
1941f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
1951f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1961f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // assume normal footer mode for now
1971f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        updateFooter(FooterMode.Normal);
1981f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
1991f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        setFocusableInTouchMode(true);
2001f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
2011f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
20263b90f28858b9d259a1d4a145f51bdd36ada8730Dianne Hackborn    public void setEnableFallback(boolean state) {
20316541e4f88bf645408de098d1368cb49dd09e4c3Daniel Sandler        if (DEBUG) Log.d(TAG, "setEnableFallback(" + state + ")");
20463b90f28858b9d259a1d4a145f51bdd36ada8730Dianne Hackborn        mEnableFallback = state;
20563b90f28858b9d259a1d4a145f51bdd36ada8730Dianne Hackborn    }
20647088bbbef61e3bd8d2930f4a2b72884129b5bd5Jim Miller
2071f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    @Override
2081f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    public boolean dispatchTouchEvent(MotionEvent ev) {
2091f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // as long as the user is entering a pattern (i.e sending a touch
2101f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // event that was handled by this screen), keep poking the
2111f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // wake lock so that the screen will stay on.
2121f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        final boolean result = super.dispatchTouchEvent(ev);
2131f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        if (result &&
2141f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                ((SystemClock.elapsedRealtime() - mLastPokeTime)
2151f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                        >  (UNLOCK_PATTERN_WAKE_INTERVAL_MS - 100))) {
2161f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            mLastPokeTime = SystemClock.elapsedRealtime();
2171f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
2181f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        return result;
2191f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
2201f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
22175787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn    @Override
22275787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn    protected void onAttachedToWindow() {
22375787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn        super.onAttachedToWindow();
22475787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn        if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
22575787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn            Log.v(TAG, "***** PATTERN ATTACHED TO WINDOW");
22675787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn            Log.v(TAG, "Cur orient=" + mCreationOrientation
22775787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn                    + ", new config=" + getResources().getConfiguration());
22875787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn        }
22975787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn        if (getResources().getConfiguration().orientation != mCreationOrientation) {
23075787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn            mCallback.recreateMe(getResources().getConfiguration());
23175787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn        }
23275787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn    }
23375787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn
234bf019065f0ccf7e85cfbbfa5f25b200bf326e0ebKarl Rosaen
2351f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    /** {@inheritDoc} */
2365475906f53e2d79807548932dfabbf9945143740Jim Miller    @Override
2375475906f53e2d79807548932dfabbf9945143740Jim Miller    protected void onConfigurationChanged(Configuration newConfig) {
2385475906f53e2d79807548932dfabbf9945143740Jim Miller        super.onConfigurationChanged(newConfig);
23975787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn        if (LockPatternKeyguardView.DEBUG_CONFIGURATION) {
24075787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn            Log.v(TAG, "***** PATTERN CONFIGURATION CHANGED");
24175787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn            Log.v(TAG, "Cur orient=" + mCreationOrientation
24275787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn                    + ", new config=" + getResources().getConfiguration());
24375787398fa5db9add16f3d3bc298198ed2c6671fDianne Hackborn        }
2445475906f53e2d79807548932dfabbf9945143740Jim Miller        if (newConfig.orientation != mCreationOrientation) {
2455475906f53e2d79807548932dfabbf9945143740Jim Miller            mCallback.recreateMe(newConfig);
2461f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
2471f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
2481f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
2491f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    /** {@inheritDoc} */
2501f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    public void onKeyboardChange(boolean isKeyboardOpen) {}
2511f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
2521f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    /** {@inheritDoc} */
2531f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    public boolean needsInput() {
2541f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        return false;
2551f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
25647088bbbef61e3bd8d2930f4a2b72884129b5bd5Jim Miller
2571f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    /** {@inheritDoc} */
2581f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    public void onPause() {
2591f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        if (mCountdownTimer != null) {
2601f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            mCountdownTimer.cancel();
2611f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            mCountdownTimer = null;
2621f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
2636b05d58018c2806459c121e507c005639b74aee9Jim Miller        mKeyguardStatusViewManager.onPause();
2641f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
2651f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
2661f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    /** {@inheritDoc} */
2671f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    public void onResume() {
2688171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung        // reset status
2696b05d58018c2806459c121e507c005639b74aee9Jim Miller        mKeyguardStatusViewManager.onResume();
2701f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
2711f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // reset lock pattern
2721f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.enableInput();
2731f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.setEnabled(true);
2741f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.clearPattern();
27547088bbbef61e3bd8d2930f4a2b72884129b5bd5Jim Miller
2761f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // show "forgot pattern?" button if we have an alternate authentication method
2770d244193bed0c995e2beaff5e217200457b28b9dJim Miller        if (mCallback.doesFallbackUnlockScreenExist()) {
2780d244193bed0c995e2beaff5e217200457b28b9dJim Miller            showForgotPatternButton();
2790d244193bed0c995e2beaff5e217200457b28b9dJim Miller        } else {
2800d244193bed0c995e2beaff5e217200457b28b9dJim Miller            hideForgotPatternButton();
2810d244193bed0c995e2beaff5e217200457b28b9dJim Miller        }
2821f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
2831f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // if the user is currently locked out, enforce it.
2841f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        long deadline = mLockPatternUtils.getLockoutAttemptDeadline();
2851f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        if (deadline != 0) {
2861f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            handleAttemptLockout(deadline);
2871f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
2881f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
2891f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        // the footer depends on how many total attempts the user has failed
2901f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        if (mCallback.isVerifyUnlockOnly()) {
2911f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            updateFooter(FooterMode.VerifyUnlocked);
292d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen        } else if (mEnableFallback &&
293d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen                (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
2941f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            updateFooter(FooterMode.ForgotLockPattern);
295d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen        } else {
296d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen            updateFooter(FooterMode.Normal);
2971f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
298c23024d32779b170dde32a50fbdd945ce521b874Jim Miller
2991f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
3001f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
3011f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    /** {@inheritDoc} */
3021f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    public void cleanUp() {
30379628c6662b863c220ff68159e4272f45d66cbe8Jim Miller        if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
3041f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mUpdateMonitor.removeCallback(this);
3058b886fab5496b0b1f5193f21855220176deddc37Jim Miller        mLockPatternUtils = null;
3068b886fab5496b0b1f5193f21855220176deddc37Jim Miller        mUpdateMonitor = null;
3078b886fab5496b0b1f5193f21855220176deddc37Jim Miller        mCallback = null;
30879628c6662b863c220ff68159e4272f45d66cbe8Jim Miller        mLockPatternView.setOnPatternListener(null);
3091f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
3101f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
31184999d69e0a70f5e510577b09ea1e788142252a8Karl Rosaen    @Override
31284999d69e0a70f5e510577b09ea1e788142252a8Karl Rosaen    public void onWindowFocusChanged(boolean hasWindowFocus) {
31384999d69e0a70f5e510577b09ea1e788142252a8Karl Rosaen        super.onWindowFocusChanged(hasWindowFocus);
31484999d69e0a70f5e510577b09ea1e788142252a8Karl Rosaen        if (hasWindowFocus) {
31584999d69e0a70f5e510577b09ea1e788142252a8Karl Rosaen            // when timeout dialog closes we want to update our state
31684999d69e0a70f5e510577b09ea1e788142252a8Karl Rosaen            onResume();
31784999d69e0a70f5e510577b09ea1e788142252a8Karl Rosaen        }
31884999d69e0a70f5e510577b09ea1e788142252a8Karl Rosaen    }
31984999d69e0a70f5e510577b09ea1e788142252a8Karl Rosaen
3201f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private class UnlockPatternListener
3211f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            implements LockPatternView.OnPatternListener {
3221f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
3231f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        public void onPatternStart() {
3241f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            mLockPatternView.removeCallbacks(mCancelPatternRunnable);
3251f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
3261f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
3271f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        public void onPatternCleared() {
3281f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
3291f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
33072f230e32ec889607f7d85594396cc3b002bae8cJim Miller        public void onPatternCellAdded(List<Cell> pattern) {
33172f230e32ec889607f7d85594396cc3b002bae8cJim Miller            // To guard against accidental poking of the wakelock, look for
33247088bbbef61e3bd8d2930f4a2b72884129b5bd5Jim Miller            // the user actually trying to draw a pattern of some minimal length.
33372f230e32ec889607f7d85594396cc3b002bae8cJim Miller            if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
33472f230e32ec889607f7d85594396cc3b002bae8cJim Miller                mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
3356fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller            } else {
3366fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller                // Give just a little extra time if they hit one of the first few dots
3376fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller                mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_FIRST_DOTS_MS);
33872f230e32ec889607f7d85594396cc3b002bae8cJim Miller            }
33972f230e32ec889607f7d85594396cc3b002bae8cJim Miller        }
34072f230e32ec889607f7d85594396cc3b002bae8cJim Miller
3411f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        public void onPatternDetected(List<LockPatternView.Cell> pattern) {
3421f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            if (mLockPatternUtils.checkPattern(pattern)) {
3431f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                mLockPatternView
3441f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                        .setDisplayMode(LockPatternView.DisplayMode.Correct);
3456b05d58018c2806459c121e507c005639b74aee9Jim Miller                mKeyguardStatusViewManager.setInstructionText("");
3466b05d58018c2806459c121e507c005639b74aee9Jim Miller                mKeyguardStatusViewManager.updateStatusLines(true);
3471f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                mCallback.keyguardDone(true);
3486fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller                mCallback.reportSuccessfulUnlockAttempt();
3495cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom                KeyStore.getInstance().password(LockPatternUtils.patternToString(pattern));
3501f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            } else {
35179628c6662b863c220ff68159e4272f45d66cbe8Jim Miller                boolean reportFailedAttempt = false;
35272f230e32ec889607f7d85594396cc3b002bae8cJim Miller                if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
35372f230e32ec889607f7d85594396cc3b002bae8cJim Miller                    mCallback.pokeWakelock(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
35472f230e32ec889607f7d85594396cc3b002bae8cJim Miller                }
3551f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
3561f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
3571f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                    mTotalFailedPatternAttempts++;
3581f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                    mFailedPatternAttemptsSinceLastTimeout++;
35979628c6662b863c220ff68159e4272f45d66cbe8Jim Miller                    reportFailedAttempt = true;
3601f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                }
36179628c6662b863c220ff68159e4272f45d66cbe8Jim Miller                if (mFailedPatternAttemptsSinceLastTimeout
36279628c6662b863c220ff68159e4272f45d66cbe8Jim Miller                        >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
3631f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                    long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
3641f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                    handleAttemptLockout(deadline);
3656fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller                } else {
3666fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller                    // TODO mUnlockIcon.setVisibility(View.VISIBLE);
3676b05d58018c2806459c121e507c005639b74aee9Jim Miller                    mKeyguardStatusViewManager.setInstructionText(
3688171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung                            getContext().getString(R.string.lockscreen_pattern_wrong));
3696b05d58018c2806459c121e507c005639b74aee9Jim Miller                    mKeyguardStatusViewManager.updateStatusLines(true);
3706fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller                    mLockPatternView.postDelayed(
3716fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller                            mCancelPatternRunnable,
3726fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller                            PATTERN_CLEAR_TIMEOUT_MS);
3736fe0f0e46d7863bd75f6bd23b2fa805df84b5a4dJim Miller                }
37479628c6662b863c220ff68159e4272f45d66cbe8Jim Miller
37579628c6662b863c220ff68159e4272f45d66cbe8Jim Miller                // Because the following can result in cleanUp() being called on this screen,
37679628c6662b863c220ff68159e4272f45d66cbe8Jim Miller                // member variables reset in cleanUp() shouldn't be accessed after this call.
37779628c6662b863c220ff68159e4272f45d66cbe8Jim Miller                if (reportFailedAttempt) {
37879628c6662b863c220ff68159e4272f45d66cbe8Jim Miller                    mCallback.reportFailedUnlockAttempt();
37979628c6662b863c220ff68159e4272f45d66cbe8Jim Miller                }
3801f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            }
3811f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }
3821f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
3831f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
3841f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    private void handleAttemptLockout(long elapsedRealtimeDeadline) {
3851f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.clearPattern();
3861f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mLockPatternView.setEnabled(false);
3871f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        long elapsedRealtime = SystemClock.elapsedRealtime();
3881f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) {
3891f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
3901f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            @Override
3911f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            public void onTick(long millisUntilFinished) {
3921f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                int secondsRemaining = (int) (millisUntilFinished / 1000);
3936b05d58018c2806459c121e507c005639b74aee9Jim Miller                mKeyguardStatusViewManager.setInstructionText(getContext().getString(
3941f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                        R.string.lockscreen_too_many_failed_attempts_countdown,
3958171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung                        secondsRemaining));
3966b05d58018c2806459c121e507c005639b74aee9Jim Miller                mKeyguardStatusViewManager.updateStatusLines(true);
3971f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            }
3981f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project
3991f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            @Override
4001f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            public void onFinish() {
4011f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                mLockPatternView.setEnabled(true);
4026b05d58018c2806459c121e507c005639b74aee9Jim Miller                mKeyguardStatusViewManager.setInstructionText(getContext().getString(
4038171b5182f5f07d33c9dfdf2dd8f0f6ae9588039Jae Yong Sung                        R.string.lockscreen_pattern_instructions));
4046b05d58018c2806459c121e507c005639b74aee9Jim Miller                mKeyguardStatusViewManager.updateStatusLines(true);
405bf019065f0ccf7e85cfbbfa5f25b200bf326e0ebKarl Rosaen                // TODO mUnlockIcon.setVisibility(View.VISIBLE);
4061f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project                mFailedPatternAttemptsSinceLastTimeout = 0;
407d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen                if (mEnableFallback) {
408d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen                    updateFooter(FooterMode.ForgotLockPattern);
409d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen                } else {
410d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen                    updateFooter(FooterMode.Normal);
411d322c437fe48dc03f0df94492267b3783e3411cbKarl Rosaen                }
4121f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project            }
4131f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project        }.start();
4141f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project    }
415c23024d32779b170dde32a50fbdd945ce521b874Jim Miller
4161f838aaece3cd61d07ab9e41c5c6af761d643324The Android Open Source Project}
417