zoukankan      html  css  js  c++  java
  • Android 7.1.1 锁屏界面启动流程

          前几天遇到一个低概率复现锁屏界面不显示,仅仅显示状态栏的问题,跟了下锁屏界面启动显示的流程,在这分享下,也方便以后自己查看。前面简介了下Zygote启动流程 Zygote进程启动后会首先创建一个SystemServer进程,SystemServer进程在调用startOtherServices同一时候也会调用WindowManagerService的systemReady()方法

      //frameworks/base/services/java/com/android/server/SystemServer.java  
      private void startOtherServices() {
           ...
           wm = WindowManagerService.main(context, inputManager,
                        mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                        !mFirstBoot, mOnlyCore);
            ...
            try {
                wm.systemReady();
                Slog.i("jason11", "SystemServer wm.systemReady");
            } catch (Throwable e) {
                reportWtf("making Window Manager Service ready", e);
            }
            ...
        }

     在WindowManagerService中直接调用了PhoneWindowManager里的systemReady()

      //frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
      //final WindowManagerPolicy mPolicy = new PhoneWindowManager();  
        public void systemReady() {
            mPolicy.systemReady();
        }
    PhoneWindowManagersystemReady()会依据一个Boolean值bindKeyguardNow来决定是否绑定keyguard service

      //frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java  
      /** {@inheritDoc} */
        @Override
        public void systemReady() {
            mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
            mKeyguardDelegate.onSystemReady();
    
            readCameraLensCoverState();
            updateUiMode();
            boolean bindKeyguardNow;
            synchronized (mLock) {
                updateOrientationListenerLp();
                mSystemReady = true;
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        updateSettings();
                    }
                });
    
                bindKeyguardNow = mDeferBindKeyguard;
                if (bindKeyguardNow) {
                    // systemBooted ran but wasn't able to bind to the Keyguard, we'll do it now.
                    mDeferBindKeyguard = false;
                }
            }
            if (bindKeyguardNow) {
                mKeyguardDelegate.bindService(mContext);
                mKeyguardDelegate.onBootCompleted();
            }
            mSystemGestures.systemReady();
        }

    看到这里,可能会想到假设bindKeyguardNow为false就会不绑定,后面通过继续跟踪发如今PhoneWindowManager的systemBooted()里也会去绑定keyguard service。假设在systemBooted里绑定了就不在systemReady里再去绑定,自己測试的时候是在systemBooted绑定的

      //frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java  
         /** {@inheritDoc} */
        @Override
        public void systemBooted() {
            boolean bindKeyguardNow = false;
            synchronized (mLock) {
                // Time to bind Keyguard; take care to only bind it once, either here if ready or
                // in systemReady if not.
                if (mKeyguardDelegate != null) {
                    bindKeyguardNow = true;
                } else {
                    // Because mKeyguardDelegate is null, we know that the synchronized block in
                    // systemReady didn't run yet and setting this will actually have an effect.
                    mDeferBindKeyguard = true;
                }
            }
            if (bindKeyguardNow) {
                mKeyguardDelegate.bindService(mContext);
                mKeyguardDelegate.onBootCompleted();
            }
            synchronized (mLock) {
                mSystemBooted = true;
            }
            startedWakingUp();
            screenTurningOn(null);
            screenTurnedOn();
        }

    以下就通过例如以下的时序图看看是怎样调用到systemBooted的,就不在一步步跟了

    通过上面的分析知道。不管是在systemReady或systemBooted,都调用了KeyguardServiceDelegate对象的bindService方法,以下就以这种方法開始。看看锁屏界面是怎么显示出来的,先看看以下的时序图,再来分步解说


    1、先来看看在KeyguardServiceDelegate怎样绑定KeyguardService的

    //frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
    public class KeyguardServiceDelegate {
        ...
        public void bindService(Context context) {
            Intent intent = new Intent();
            final Resources resources = context.getApplicationContext().getResources();
    
            final ComponentName keyguardComponent = ComponentName.unflattenFromString(
                    resources.getString(com.android.internal.R.string.config_keyguardComponent));
            intent.setComponent(keyguardComponent);
    
            if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                    Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
                Log.v(TAG, "*** Keyguard: can't bind to " + keyguardComponent);
                mKeyguardState.showing = false;
                mKeyguardState.showingAndNotOccluded = false;
                mKeyguardState.secure = false;
                synchronized (mKeyguardState) {
                    // TODO: Fix synchronisation model in this class. The other state in this class
                    // is at least self-healing but a race condition here can lead to the scrim being
                    // stuck on keyguard-less devices.
                    mKeyguardState.deviceHasKeyguard = false;
                    hideScrim();
                }
            } else {
                if (DEBUG) Log.v(TAG, "*** Keyguard started");
            }
        }
        ...
    }
    在bindService中调用了bindServiceAsUser绑定指定intent的service,config_keyguardComponent的定义例如以下

    //frameworks/base/core/res/res/values/config.xml
        <!-- Keyguard component -->
        <string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
    当绑定成功后会调用mKeyguardConnection里的onServiceConnected方法

    //frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
    public class KeyguardServiceDelegate {
        ...
          private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");
                mKeyguardService = new KeyguardServiceWrapper(mContext,
                        IKeyguardService.Stub.asInterface(service));
                if (mKeyguardState.systemIsReady) {
                    // If the system is ready, it means keyguard crashed and restarted.
                    mKeyguardService.onSystemReady();
                    // This is used to hide the scrim once keyguard displays.
                    if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
                        mKeyguardService.onStartedWakingUp();
                    }
                    if (mKeyguardState.screenState == SCREEN_STATE_ON
                            || mKeyguardState.screenState == SCREEN_STATE_TURNING_ON) {
                        mKeyguardService.onScreenTurningOn(
                                new KeyguardShowDelegate(mDrawnListenerWhenConnect));
                    }
                    if (mKeyguardState.screenState == SCREEN_STATE_ON) {
                        mKeyguardService.onScreenTurnedOn();
                    }
                    mDrawnListenerWhenConnect = null;
                }
                if (mKeyguardState.bootCompleted) {
                    mKeyguardService.onBootCompleted();
                }
                if (mKeyguardState.occluded) {
                    mKeyguardService.setOccluded(mKeyguardState.occluded);
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
                mKeyguardService = null;
            }
    
        };
        ...
    }
    当mKeyguardState.systemIsReady为true是。就会通过KeyguardServiceWrapper的实例mKeyguardService调用onSystemReady方法。在KeyguardServiceWrapper的onSystemReady里调用了上面刚刚绑定成功的KeyguardService的onSystemReady方法

    //frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
    public class KeyguardServiceWrapper implements IKeyguardService {
        ...
        @Override // Binder interface
        public void onSystemReady() {
            try {
                mService.onSystemReady();
            } catch (RemoteException e) {
                Slog.w(TAG , "Remote Exception", e);
            }
        }
        ...
    }
    

    KeyguardService的onSystemReady里调用了KeyguardViewMediator里的onSystemReady,在这里就不贴这个代码了,直接看看KeyguardViewMediator.onSystemReady这个里面干啥了

    2、KeyguardViewMediator.onSystemReady

    //frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
    public class KeyguardViewMediator extends SystemUI {
        ...
        public void onSystemReady() {
            mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
            synchronized (this) {
                if (DEBUG) Log.d(TAG, "onSystemReady");
                mSystemReady = true;
                doKeyguardLocked(null);
                mUpdateMonitor.registerCallback(mUpdateCallback);
            }
            // Most services aren't available until the system reaches the ready state, so we
            // send it here when the device first boots.
            maybeSendUserPresentBroadcast();
        }
        ...
    }
    
    在这种方法里主要调用了doKeyguardLocked和注冊了KeyguardUpdateMonitorCallback

    3、通过调用doKeyguardLocked显示锁屏界面

    //frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
    public class KeyguardViewMediator extends SystemUI {
        ...
        private void doKeyguardLocked(Bundle options) {
            // if another app is disabling us, don't show
            if (!mExternallyEnabled) {
                if (DEBUG) Log.d(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 (mStatusBarKeyguardViewManager.isShowing()) {
                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
                resetStateLocked();
                return;
            }
    
            // if the setup wizard hasn't run yet, don't show
            final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
            final boolean absent = SubscriptionManager.isValidSubscriptionId(
                    mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));
            final boolean disabled = SubscriptionManager.isValidSubscriptionId(
                    mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
            final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()
                    || ((absent || disabled) && requireSim);
    
            if (!lockedOrMissing && shouldWaitForProvisioning()) {
                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
                        + " and the sim is not locked or missing");
                return;
            }
    
            if (mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
                    && !lockedOrMissing) {
                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
                return;
            }
    
            if (mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())) {
                if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
                // Without this, settings is not enabled until the lock screen first appears
                setShowingLocked(false);
                hideLocked();
                mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
                return;
            }
    
            if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
            showLocked(options);
        }
        ...
    }
    
    这段代码主要是在是否要显示锁屏之前做了5个推断:1.假设启用第三方锁屏界面。不显示原生界面;2.锁屏界面已经显示了话,又一次更新下状态;3.假设第一次开机引导界面setup wizard 还没有执行,也先不显示;4.屏幕没有亮不显示;5.当前正在解密界面不显示。

    假设这几个条件都不满足。则调用showLocked显示锁屏界面。在showLocked通过mHandler发送Message。在handleMessage里“case SHOW:”时调用handleShow

    4、在handleShow里设置一些锁屏状态和显示锁屏界面

    //frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
    public class KeyguardViewMediator extends SystemUI {
        ...
            private void handleShow(Bundle options) {
            synchronized (KeyguardViewMediator.this) {
                if (!mSystemReady) {
                    if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready.");
                    return;
                } else {
                    if (DEBUG) Log.d(TAG, "handleShow");
                }
    
                setShowingLocked(true);
                mStatusBarKeyguardViewManager.show(options);
                mHiding = false;
                mWakeAndUnlocking = false;
                resetKeyguardDonePendingLocked();
                mHideAnimationRun = false;
                updateActivityLockScreenState();
                adjustStatusBarLocked();
                userActivity();
    
                mShowKeyguardWakeLock.release();
            }
            mKeyguardDisplayManager.show();
        }
        ...
    }
    

    5、通过调用StatusBarKeyguardViewManager的show重置当前状态显示keyguard

    //frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
    public class StatusBarKeyguardViewManager {
        ...
        public void show(Bundle options) {
            mShowing = true;
            mStatusBarWindowManager.setKeyguardShowing(true);
            mScrimController.abortKeyguardFadingOut();
            reset();
        }
        ...
    }
    
    在reset里调用本类的showBouncerOrKeyguard,在这种方法里通过KeyguardBouncer的实例mBouncer调用prepare(),在prepare里调用了KeyguardHostView的showPrimarySecurityScreen

    6、KeyguardSecurityContainer.showPrimarySecurityScreen

    在KeyguardHostView的showPrimarySecurityScreen里调用KeyguardSecurityContainer的showPrimarySecurityScreen方法。例如以下

    //frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
    public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
        ...
        void showPrimarySecurityScreen(boolean turningOff) {
            SecurityMode securityMode = mSecurityModel.getSecurityMode();
            if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
            showSecurityScreen(securityMode);
        }
        ...
    }
    
    在这种方法里调用了showSecurityScreen,依据mSecurityModel.getSecurityMode()获取的SecurityMode来显示不同界面,SecurityMode定义例如以下

    //frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
    public class KeyguardSecurityModel {
        public enum SecurityMode {
            Invalid, // NULL state
            None, // No security enabled
            Pattern, // Unlock by drawing a pattern.
            Password, // Unlock by entering an alphanumeric password
            PIN, // Strictly numeric password
            SimPin, // Unlock by entering a sim pin.
            SimPuk // Unlock by entering a sim puk
        }
        ...
    }
    

    showSecurityScreen方法例如以下:

    //frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
    public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
        ...
        private void showSecurityScreen(SecurityMode securityMode) {
            if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
    
            if (securityMode == mCurrentSecuritySelection) return;
    
            KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
            KeyguardSecurityView newView = getSecurityView(securityMode);//依据securityMode获取相应的view
    
            // Emulate Activity life cycle
            if (oldView != null) {
                oldView.onPause();
                oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
            }
            if (securityMode != SecurityMode.None) {
                newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
                newView.setKeyguardCallback(mCallback);
            }
    
            // Find and show this child.
            final int childCount = mSecurityViewFlipper.getChildCount();
    
            final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
            for (int i = 0; i < childCount; i++) {
                if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
                    mSecurityViewFlipper.setDisplayedChild(i);
                    break;
                }
            }
    
            mCurrentSecuritySelection = securityMode;
            mSecurityCallback.onSecurityModeChanged(securityMode,
                    securityMode != SecurityMode.None && newView.needsInput());
        }
        ...
    }

    到这里锁屏就启动完毕了,这里简单总结一下:
       1. 在KeyguardServiceDelegate里绑定KeyguardService,并调用onSystemReady方法。
       2. KeyguardViewMediator里调用doKeyguardLocked来决定是否须要显示锁屏界面。假设显示则调用StatusBarKeyguardViewManager的show,最后调用到KeyguardHostView的showPrimarySecurityScreen()。


       3. 在KeyguardSecurityContainer的showPrimarySecurityScreen利用mSecurityModel.getSecurityMode()获取当前的securityMode,传入showSecurityScreen来显示不同锁屏界面。

  • 相关阅读:
    Java辅助类持续汇总~
    Java关于数字工具类~持续汇总~
    Java关于字符串工具类~持续汇总~
    Java关于日期的计算持续汇总~
    antlr v4 使用指南连载4——词法规则入门之黄金定律
    antlr v4 使用指南连载3——g4文件概览
    antlr v4 使用指南连载2——准备环境
    antlr 4新特性总结及与antlr v3的不同
    antlr v4 使用指南连载1——简介
    Angularjs中使用$location获取url参数时,遇到的坑~~~
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7288867.html
Copyright © 2011-2022 走看看