Android4.2锁屏流程【Android锁屏解析三】

来源:互联网 发布:在淘宝上买女士秋衣 编辑:程序博客网 时间:2024/05/19 02:18

谷歌对Android4.2的版本做了一些改动,突出的改动就是锁屏可以添加widget,即锁屏widget,这个是谷歌的一个大的改动,先来说说android4.2做了哪些改动?

  (1)Lock screen widgets如图:

2)屏保模式的添加

简单介绍如下:互助屏保是一个互动屏幕保护程序模式,当用户的设备开始停靠或充电。在这种模式下,系统将启动一枕黄粱 - 远程安装的应用程序提供内容服务 - 设备的屏幕保护程序。用户可以设置应用程序启用白日梦,然后选择显示遐想。


(3)更多显示的支持

(4)Native RTL support从右向左支持,例如印度语,就是这种显示格式。

等等,更多特性请参考官网:http://developer.android.com/about/versions/jelly-bean.html#42-external-display


好了,言归正传,我们来说说Android4.2锁屏的流程:咱们一步一步来说:

Step1:先看第一次开机的加载锁屏的过程,通过PhoneWindowManager.java这个类的systemReady()这个方法,当系统开机准备好的情况下会调用这个方法,如下:

 public void systemReady() {        if (mKeyguardMediator != null) {            // tell the keyguard            mKeyguardMediator.onSystemReady();        }        synchronized (mLock) {            updateOrientationListenerLp();            mSystemReady = true;            mHandler.post(new Runnable() {                public void run() {                    updateSettings();                }            });        }    }


Step2:看注释就知道下一步该干什么了,告诉锁屏的管理者,我准备好了,该你来控制加载锁屏界面了。接着调用到了KeyguardViewMediator.java这个类的onSystemReady()方法,如下:

/**     * Let us know that the system is ready after startup.     */    public void onSystemReady() {        mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);        synchronized (this) {            if (DEBUG) Log.d(TAG, "onSystemReady");            mSystemReady = true;            mUpdateMonitor.registerCallback(mUpdateCallback);            // Suppress biometric unlock right after boot until things have settled if it is the            // selected security method, otherwise unsuppress it.  It must be unsuppressed if it is            // not the selected security method for the following reason:  if the user starts            // without a screen lock selected, the biometric unlock would be suppressed the first            // time they try to use it.            //            // Note that the biometric unlock will still not show if it is not the selected method.            // Calling setAlternateUnlockEnabled(true) simply says don't suppress it if it is the            // selected method.            if (mLockPatternUtils.usingBiometricWeak()                    && mLockPatternUtils.isBiometricWeakInstalled()                    || mLockPatternUtils.usingVoiceWeak()                    && FeatureOption.MTK_VOICE_UNLOCK_SUPPORT) {                if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");                mUpdateMonitor.setAlternateUnlockEnabled(false);            } else {                mUpdateMonitor.setAlternateUnlockEnabled(true);            }            /// M: power-off alarm @{            if (!KeyguardUpdateMonitor.isAlarmBoot()) {                doKeyguardLocked();            }            /// @}        }        // Most services aren't available until the system reaches the ready state, so we        // send it here when the device first boots.        maybeSendUserPresentBroadcast();    }

Step3:接着由doKeyguardLocked()这个方法来做启动锁屏界面的预处理,来看看这个方法都做了什么:

    private void doKeyguardLocked() {        doKeyguardLocked(null);    }    /**     * Enable the keyguard if the settings are appropriate.     */    private void doKeyguardLocked(Bundle options) {        // if another app is disabling us, don't show        if (!mExternallyEnabled || KeyguardUpdateMonitor.isAlarmBoot()) {            if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because externally disabled");            // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes            // for an occasional ugly flicker in this situation:            // 1) receive a call with the screen on (no keyguard) or make a call            // 2) screen times out            // 3) user hits key to turn screen back on            // instead, we reenable the keyguard when we know the screen is off and the call            // ends (see the broadcast receiver below)            // TODO: clean this up when we have better support at the window manager level            // for apps that wish to be on top of the keyguard            return;        }        // if the keyguard is already showing, don't bother        if (mKeyguardViewManager.isShowing()) {            if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: not showing because it is already showing");            return;        }        // if the setup wizard hasn't run yet, don't show        if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: get keyguard.no_require_sim property before");        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",                false);        if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: get keyguard.no_require_sim property after");        final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();        final IccCardConstants.State state = mUpdateMonitor.getSimState();        boolean lockedOrMissing = false;        /// M: Support GeminiPlus        for (int i = PhoneConstants.GEMINI_SIM_1; i <= KeyguardUtils.getMaxSimId(); i++) {            lockedOrMissing = (lockedOrMissing || isLockedOrMissingGemini(i, requireSim));            if (lockedOrMissing) {                break;            }        }                if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: get sim state after");        /// M: MTK MOTA UPDATE when on ics2 keygaurd set none,update to JB,the keyguard will show LockScreen.        /// MTK MOTA UPDATE when the phone first boot,check the settingDB mirged or not ,because mota update,        /// the settingdb migrate slow than keygaurd(timing sequence problem) @{        boolean keyguardDisable = false;                /////*************************************TODO        boolean motaUpdateFirst = true;//mLockPatternUtils.isDbMigrated();        if (motaUpdateFirst) {            /// DB mogi done            keyguardDisable = mLockPatternUtils.isLockScreenDisabled();        } else {            /// DB not mogi            final ContentResolver cr = mContext.getContentResolver();            String value = Settings.Secure.getString(cr, "lockscreen.disabled");            boolean booleanValue = false;            if( null!=value ){                booleanValue = value.equals("1") ? true :false;            }            keyguardDisable = (!mLockPatternUtils.isSecure()) && booleanValue;        }        /// @}                if (DEBUG) KeyguardUtils.xlogD(TAG, "doKeyguard: keyguardDisable query end");                /// M: Add new condition DM lock is not true        boolean dmLocked = KeyguardUpdateMonitor.getInstance(mContext).dmIsLocked();        KeyguardUtils.xlogD(TAG, "lockedOrMissing is " + lockedOrMissing + ", requireSim=" + requireSim            + ", provisioned=" + provisioned + ", keyguardisable=" + keyguardDisable + ", dmLocked=" + dmLocked);                if (!lockedOrMissing && !provisioned && !dmLocked) {            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"                    + " and the sim is not locked or missing");            return;        }        /// M: Add a new condition DM lock is not on, or user can still bypass dm lock when Keygaurd is disabled        if (mUserManager.getUsers(true).size() < 2                && keyguardDisable && !lockedOrMissing && !KeyguardUpdateMonitor.getInstance(mContext).dmIsLocked()) {            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");            return;        }        if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");        showLocked(options);    }

Step4、来注意最后调用的这个方法showLocked(options),这个方法是启动锁屏关键的方法,来看看:

 /**     * Send message to keyguard telling it to show itself     * @see #handleShow()     */    private void showLocked(Bundle options) {        if (DEBUG) KeyguardUtils.xlogD(TAG, "showLocked");        // ensure we stay awake until we are finished displaying the keyguard        mShowKeyguardWakeLock.acquire();        Message msg = mHandler.obtainMessage(SHOW, options);        mHandler.sendMessage(msg);    }

Step5、这下就通过发送消息来进一步启动锁屏界面,来看看这个mHandler中的SHOW都做了什么:

public void handleMessage(Message msg) {            if (DBG_MESSAGE) KeyguardUtils.xlogD(TAG, "handleMessage enter msg name=" + getMessageString(msg));            switch (msg.what) {                case SHOW:                    handleShow((Bundle) msg.obj);                    break;
调用的是handleShow()这个方法:

/**     * Handle message sent by {@link #showLocked}.     * @see #SHOW     */    private void handleShow(Bundle options) {        synchronized (KeyguardViewMediator.this) {            if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow enter");            if (!mSystemReady) return;            /// M: if already showing, just return            if (mShowing) return;            mKeyguardViewManager.show(options);                        if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow mKeyguardViewManager Show exit");                        mShowing = true;            mKeyguardDonePending = false;            updateActivityLockScreenState();            adjustStatusBarLocked();            userActivity();            try {                ActivityManagerNative.getDefault().closeSystemDialogs("lock");            } catch (RemoteException e) {            }            if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow query AlarmBoot before");            // Do this at the end to not slow down display of the keyguard.            if (!KeyguardUpdateMonitor.isAlarmBoot()) {                playSounds(true);            } else {                new Handler().postDelayed(new Runnable() {                    public void run() {                        sendRemoveIPOWinBroadcast();                        startAlarm();                    }                }, 250);            }            mShowKeyguardWakeLock.release();            if (DEBUG) KeyguardUtils.xlogD(TAG, "handleShow exit");        }    }

Step6、接着看mKeyguardViewManager.show(options);这个方法都干了什么:

 /**     * Show the keyguard.  Will handle creating and attaching to the view manager     * lazily.     */    public synchronized void show(Bundle options) {        if (DEBUG) KeyguardUtils.xlogD(TAG, "show(); mKeyguardView=" + mKeyguardView);        boolean enableScreenRotation = shouldEnableScreenRotation();        if (DEBUG) KeyguardUtils.xlogD(TAG, "show() query screen rotation after");        /// M: Incoming Indicator for Keyguard Rotation @{        KeyguardUpdateMonitor.getInstance(mContext).setQueryBaseTime();        /// @}        maybeCreateKeyguardLocked(enableScreenRotation, false, options);                if (DEBUG) KeyguardUtils.xlogD(TAG, "show() maybeCreateKeyguardLocked finish");                maybeEnableScreenRotation(enableScreenRotation);        // Disable common aspects of the system/status/navigation bars that are not appropriate or        // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED        // activities. Other disabled bits are handled by the KeyguardViewMediator talking        // directly to the status bar service.        final int visFlags = View.STATUS_BAR_DISABLE_HOME;        if (DEBUG) KeyguardUtils.xlogD(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");        mKeyguardHost.setSystemUiVisibility(visFlags);        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);        mKeyguardHost.setVisibility(View.VISIBLE);        mKeyguardView.show();        mKeyguardView.requestFocus();        if (DEBUG) KeyguardUtils.xlogD(TAG, "show() exit; mKeyguardView=" + mKeyguardView);    }

Step7,、这下终于看到如山真面目了,看里面的方法maybeCreateKeyguardLocked()这个是真正起作用的地方:

private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force,            Bundle options) {        final boolean isActivity = (mContext instanceof Activity); // for test activity        if (mKeyguardHost != null) {            mKeyguardHost.saveHierarchyState(mStateContainer);        }        if (mKeyguardHost == null) {            if (DEBUG) KeyguardUtils.xlogD(TAG, "keyguard host is null, creating it...");            mKeyguardHost = new ViewManagerHost(mContext);            int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR                    | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN                    | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;            /// M: Modify to support DM lock, hide statusbr when dm lock power on @{            KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);            if (monitor.dmIsLocked()) { //in the first created                if (DEBUG) KeyguardUtils.xlogD(TAG, "show(); dmIsLocked ");                flags &= ~WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;                flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;                flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;            } else if (KeyguardUpdateMonitor.isAlarmBoot()) {                if (DEBUG) KeyguardUtils.xlogD(TAG, "show(); AlarmBoot ");                flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;                flags &= ~WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;                flags |= WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;            }            /// M: @}            if (!mNeedsInput) {                flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;            }            if (ActivityManager.isHighEndGfx()) {                flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;            }            final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;            final int type = isActivity ? WindowManager.LayoutParams.TYPE_APPLICATION                    : WindowManager.LayoutParams.TYPE_KEYGUARD;            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(                    stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);            lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;            lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;            if (ActivityManager.isHighEndGfx()) {                lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;                lp.privateFlags |=                        WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;            }            lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;            if (isActivity) {                lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;            }            /// M: Poke user activity when operating Keyguard            //lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;            lp.setTitle(isActivity ? "KeyguardMock" : "Keyguard");            mWindowLayoutParams = lp;            mViewManager.addView(mKeyguardHost, lp);        }                /// M: If force and keyguardView is not null, we should relase memory hold by old keyguardview        if (force && mKeyguardView != null) {            mKeyguardView.cleanUp();        }        if (force || mKeyguardView == null) {            inflateKeyguardView(options);            mKeyguardView.requestFocus();        }        updateUserActivityTimeoutInWindowLayoutParams();        mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);        mKeyguardHost.restoreHierarchyState(mStateContainer);    }
这下通过 mViewManager.addView(mKeyguardHost, lp);这个方法真正地把锁屏界面添加到屏幕上,其实这个就是个view,挡在了手机的屏幕的最上方。而这个mKeyguardHost就是锁屏的根。而第一次加载的时候mKeyguardView为空,调用inflateKeyguardView(),初始化锁屏的view。


Step8、来看看这个inflateKeyguardView()这个方法都加载了哪个布局:

private void inflateKeyguardView(Bundle options) {        /// M: add for power-off alarm @{        int resId = R.id.keyguard_host_view;        int layoutId = R.layout.keyguard_host_view;        if(KeyguardUpdateMonitor.isAlarmBoot()){            layoutId = com.mediatek.internal.R.layout.power_off_alarm_host_view;            resId = com.mediatek.internal.R.id.keyguard_host_view;        }        /// @}        View v = mKeyguardHost.findViewById(resId);        if (v != null) {            mKeyguardHost.removeView(v);        }        // TODO: Remove once b/7094175 is fixed        if (false) Slog.d(TAG, "inflateKeyguardView: b/7094175 mContext.config="                + mContext.getResources().getConfiguration());                /// M: Save new orientation        mCreateOrientation = mContext.getResources().getConfiguration().orientation;                final LayoutInflater inflater = LayoutInflater.from(mContext);        View view = inflater.inflate(layoutId, mKeyguardHost, true);        mKeyguardView = (KeyguardHostView) view.findViewById(resId);        mKeyguardView.setLockPatternUtils(mLockPatternUtils);        mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);        // HACK        // The keyguard view will have set up window flags in onFinishInflate before we set        // the view mediator callback. Make sure it knows the correct IME state.        if (mViewMediatorCallback != null) {            KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(                    R.id.keyguard_password_view);            if (kpv != null) {                mViewMediatorCallback.setNeedsInput(kpv.needsInput());            }        }        /// Extract this block to a single function        updateKeyguardViewFromOptions(options);    }
这个加载了keyguard_host_view这个layout,来看看这个布局是怎么写的:

<com.android.internal.policy.impl.keyguard.KeyguardHostView    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:androidprv="http://schemas.android.com/apk/res/android"    android:id="@+id/keyguard_host_view"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:gravity="center_horizontal"    android:orientation="vertical">    <com.android.internal.policy.impl.keyguard.SlidingChallengeLayout        android:id="@+id/sliding_layout"        android:layout_width="match_parent"        android:layout_height="match_parent">                <FrameLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            androidprv:layout_childType="mediatekLayerBackground">        </FrameLayout>        <FrameLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            androidprv:layout_childType="pageDeleteDropTarget">            <include layout="@layout/keyguard_widget_remove_drop_target"                android:id="@+id/keyguard_widget_pager_delete_target"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_gravity="top|center_horizontal" />        </FrameLayout>        <FrameLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            androidprv:layout_childType="widgets">            <include layout="@layout/keyguard_widget_pager"                android:id="@+id/app_widget_container"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:layout_gravity="center"/>        </FrameLayout>        <View android:layout_width="match_parent"              android:layout_height="match_parent"              androidprv:layout_childType="scrim"              android:background="#99000000" />                <FrameLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            androidprv:layout_childType="mediatekLayerForeground">        </FrameLayout>        <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer            android:id="@+id/keyguard_security_container"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_maxHeight="@dimen/keyguard_security_height"            androidprv:layout_childType="challenge"            android:padding="0dp"            android:gravity="bottom|center_horizontal">            <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper                android:id="@+id/view_flipper"                android:layout_width="match_parent"                android:layout_height="match_parent"                android:clipToPadding="false"                android:paddingTop="@dimen/keyguard_security_view_margin"                android:gravity="center">            </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>        </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>        <ImageButton              android:layout_width="match_parent"              android:layout_height="@dimen/kg_widget_pager_bottom_padding"              androidprv:layout_childType="expandChallengeHandle"              android:focusable="true"              android:background="@null"              android:src="@drawable/keyguard_expand_challenge_handle"              android:scaleType="center"              android:contentDescription="@string/keyguard_accessibility_expand_lock_area" />    </com.android.internal.policy.impl.keyguard.SlidingChallengeLayout></com.android.internal.policy.impl.keyguard.KeyguardHostView>
而这个KeyguardHostView.java就是锁屏的真正的处理的view,该添加什么样的锁屏,例如:PIN,Pattern,PUK,Password等等,都是由它来控制的,最后会调用到getLayoutIdFor()这个方法,来启动那种锁屏界面,如下:

private int getLayoutIdFor(SecurityMode securityMode) {        switch (securityMode) {            case None: return R.layout.keyguard_selector_view;            case Pattern: return R.layout.keyguard_pattern_view;            case PIN: return R.layout.keyguard_pin_view;            case Password: return R.layout.keyguard_password_view;            case Biometric: return R.layout.keyguard_face_unlock_view;            case Account: return R.layout.keyguard_account_view;            /// M: Modify Sim unlock layout @{            //case SimPin: return R.layout.keyguard_sim_pin_view;            //case SimPuk: return R.layout.keyguard_sim_puk_view;            case SimPinPukMe1: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;            case SimPinPukMe2: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;            /// M: Support GeminiPlus            case SimPinPukMe3: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;            case SimPinPukMe4: return com.mediatek.internal.R.layout.keyguard_sim_pin_puk_view;            /// @}            /// M: power-off alarm @{            case AlarmBoot: return com.mediatek.internal.R.layout.power_off_alarm_view;            /// @}            ///M: add voice unlock view layout            case Voice: return R.layout.zz_keyguard_voice_unlock_view;            default:                return 0;        }    }
到这,锁屏已经初始化完了,要想下面接着分析,估计大家应该都能分析过去了;


特别说明

1、加载锁屏widget的地方在KeyguardHostView.java的onFinishInflate()中,调用的addDefaultWidget()这个方法中添加了单click事件,最后调用到KeyguardActivityLauncher.java的launcherWidgetPicker()这个方法;

2、要想你写的widget能被锁屏widget过滤出来,只需要在wdget的xml中添加一个属性即可:

android:widgetCategory="home_screen|keyguard",这样你写的桌面widget,也能在锁屏wiget过滤出来,具体布局需要你微调下;


添加一张图,





原创粉丝点击