AndroidL 开机展示Keyguard锁屏机制初探

目录
目录
锁屏时序图
开机启动到PhoneWindowManager的systemReady方法
锁屏加载流程
PhoneWindowManager
KeyguardServiceDelegate
KeyguardServiceWrapper
KeyguardService
KeyguardViewMediator
onSystemReady
onKeyguardLocked
showLocked
handleShow
重点一
重点二
StatusBarKeyguardViewManager
show
reset
showBouncerOrKeyguard
锁屏时序图
研究了将近两天的Android5.1 Keyguard锁屏机制,不得不说,各种饶。这里先把锁屏流程时序图贡献给大家: 


使用的是线编辑工具ProcessOn,用来编辑时序图效果看起来不是太好。不过没有太大关系,这个时序图只是为了方便我们能清晰的对锁屏流程有个大致的了解,接下来,我会详细的分析每个类的具体流程。

声明:本文基于AndroidLollipop 5.1.1_r6版本进行的源码分析。

开机启动到PhoneWindowManager的systemReady方法
准备先从开机启动到PhoneWindowManager类的systemReady方法调用开始介绍。开机启动流程其实也很复杂,但是本文重点在于Keyguard锁屏的展示,所以这里只是大体列出如何从开机启动调用到PhoneWindowManager的systemReady()方法。具体流程如下: 
init进程->zygote进程(java世界)->system server进程。 
而在system server进程中,它的SystemServer.java中main函数会调用startOtherServices()方法,相关源码如下:

private void startOtherServices() {
    WindowManagerService wm = null;
    wm = WindowManagerService.main(context, inputManager,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
            !mFirstBoot, mOnlyCore);
    try {
        wm.systemReady();
    } catch (Throwable e) {
        reportWtf("makeing Window Manager Service ready", e);
    }
}

既然wm是WindowManagerServer类的实例,那就需要继续看一下这个类关于systemReady()方法的实现:

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
    public void systemReady() {
        mPolicy.systemReady();
    }
}

其中,mPolicy实例的生成依赖于Java的反射机制,具体流程如下:

public final class PolicyManager {
    private static final String POLICY_IMPL_CLASS_NAME = 
        "com.android.internal.policy.impl.Policy";
    private static final IPolicy sPolicy;
    static {
        try {
            // 获取了Policy的类类型
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            // 通过Policy的类类型获取Policy的对象实例
            sPolicy = (IPolicy)policyClass.newInstance();
        } catch (Exception e) {
        }
    }
    public static Window makeNewWindowManager(Context context) {
        return sPolicy.makeNewWindowManager(context);
    }
}

// Policy中继续调用反射机制进行预加载
public class Policy implements IPolicy {
    public Window makeNewWindowManager(Context context) {
        return new PhoneWindowManager();
    }
}

通过上述代码的跟踪,终于来到了我们时序图的第一个类PhonewWindowManager的systemReady()方法了。

锁屏加载流程
既然上述分析到了PhoneWindowManager,我们也就是按照时序图,根据时序图上的每个类,进行相关源码分析(重点难点的源码我会中文注释)。

PhoneWindowManager
路径

frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java

源码:

public class PhoneWindowManager implements WindowManagerPolicy {
    public void systemReady() {
        // 调用Keygurad代理类的onSystemReady方法
        mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
        mKeyguardDelegate.onSystemReady();
        // ... 省略不相关源码 
    }
}

KeyguardServiceDelegate
路径:

frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java

源码:

public class KeyguardServiceDelegate {
    protected KeyguardServiceWrapper mKeyguardService;

    public void onSystemReady() {
        if (mKeyguardService != null) {
            mKeyguardService.onSystemReady();
        } else {
            mKeyguardState.systemIsReady = true;
        }   
    } 
}

KeyguardServiceWrapper
路径:

frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java

源码:

public class KeyguardServiceWrapper implements IKeyguardService {
    private IkeyguardService mService;
    public KeyguardServiceWrapper(Context context, IKeyguardService service) {
        // 构造函数中对mService进行了初始化
        mService = service;
    }
    public void onSystemReady() {
        try {
            mService.onSystemReady();
        } catch (RemoteException e) {
            Slog.w(TAG , "Remote Exception", e); 
        }   
    }
}

由于mService是IKeyguardService接口实现类的实例,并且mService又是在KeyguardServiceWrapper的构造函数中传递进来初始化的。所以,我们又需要回到KeyguardServiceDelegate类,去看一下KeyguardServiceWrapper初始化的过程,相关代码如下:

public class KeyguardServiceDelegate {
    public static final String KEYGUARD_PACKAGE = "com.android.systemui";
    public static final String KEYGUARD_CLASS = "com.android.systemui.keyguard.KeyguardService";

    /**
     * 使用bindService的方式来绑定服务。利用bindService的方式:
     * 调用者与服务绑定在一起,调用者退出,服务即终止。
     * ps => bind方式绑定服务,服务的执行顺序为:
     * onCreate()->onBind()->onUnbind()->onDestroy()
     */
    public void bindService(Context context) {
        Intent intent = new Intent();
        intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS);
        if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
            Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS);
            mKeyguardState.showing = false;
            mKeyguardState.showingAndNotOccluded = false;
            mKeyguardState.secure = false;
            mKeyguardState.deviceHasKeyguard = false;
            hideScrim();
        } else {
            if (DEBUG) Log.v(TAG, "*** Keyguard started");
        }   
    }   

    private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");
            // 当和服务绑定后,这IKeyguardService.Stub.asInterface(service)获取的就是KeyguardService的类实例
            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.
                mKeyguardService.onScreenTurnedOn(new KeyguardShowDelegate(
                        mShowListenerWhenConnect));
                mShowListenerWhenConnect = null;
            }
            if (mKeyguardState.bootCompleted) {
                mKeyguardService.onBootCompleted();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
            mKeyguardService = null;
        }
    };
}

接下来,就需要去看一下KeyguardService类的onSystemReady方法了。

KeyguardService
路径:

/frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java

源码:

public class KeyguardService extends Service {
    private KeyguardViewMediator mKeyguardViewMediator;

    @Override // Binder interface
    public void onSystemReady() {
        // 检查调用进程是否具体SYSTEM权限
        checkPermission();
        // 真正的锁屏入口
        mKeyguardViewMediator.onSystemReady();
    }
}

Wow,终于到了我们的主角KeyguardViewMediator登场了。

KeyguardViewMediator
路径:

frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java

虽然KeyguardViewMediator是锁屏的入口,但是从这里到锁屏的真正展现还有很长一段路。接下来,为了方便,我们都是基于当前类的函数进行分析。

onSystemReady
public class KeyguardViewMediator extends SystemUI {
    // 开机显示锁屏入口函数   
    public void onSystemReady() {
        synchronized (this) {
            if (DEBUG) Log.d(TAG, "onSystemReady");
            mSystemReady = true;            

            // 判断是否使用生物识别解锁(类似:人脸识别、声音识别等)
            if (mLockPatternUtils.usingBiometricWeak()
                    && mLockPatternUtils.isBiometricWeakInstalled()) {
                if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");
                mUpdateMonitor.setAlternateUnlockEnabled(false);
            } else {
                mUpdateMonitor.setAlternateUnlockEnabled(true);
            }
            // 进行锁屏预处理判断等操作
            doKeyguardLocked(null);
        }
    }
}

onKeyguardLocked()
其中,doKeyguardLocked是来做启动锁屏界面的预处理方法,我们来看一下这个函数的具体实现:

public class KeyguardViewMediator extends SystemUI {
    private void doKeyguardLocked(Bundle options) {
        if (!mExternallyEnabled) {
            // 其他应用禁止锁屏呈现,例如接电话等操作.
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
            return;
        }

        // 判断锁屏是否正在展示
        if (mStatusBarKeyguardViewManager.isShowing()) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
            resetStateLocked();
            return;
        }

        // 判断是否无sim卡也可使用手机
        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
        // 获取sim卡状态
        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() && !lockedOrMissing) {
            // Settings中没有启用锁屏
            return;
        }

        if (mLockPatternUtils.checkVoldPassword()) {
            setShowingLocked(false);
            hideLocked();
            return;
        }

        // 经过上述判断后,去展示锁屏
        showLocked(options);
    }
}

注意showLocked(options)方法调用,这个是启动锁屏的关键方法。这里的options传递的值为null。

showLocked()
public class KeyguardViewMediator extends SystemUI {
   private void showLocked(Bundle options) {
        if (DEBUG) Log.d(TAG, "showLocked");
        // 获取PARTIAL_WAKE_LOCK,不受电源键影响,不让CPU进入休眠状态 
        mShowKeyguardWakeLock.acquire();
        // 发送msg.what为SHOW类型的message
        Message msg = mHandler.obtainMessage(SHOW, options);
        mHandler.sendMessage(msg);
    }
}

注意:

mShowKeyguardWakeLock.acquire(); ⇒ 获取之后是无法让CPU休眠,不要忘记释放,不让会增加系统功耗。

跟mShowKeyguardWakeLock相关的代码如下:

public class KeyguardViewMediator extends SystemUI {
    private PowerManager.WakeLock mShowKeyguardWakeLock;
    private void setupLocked() {
        // 获取了PARTIAL_WAKE_LOCK锁,即不受电源键控制,即使按下电源键也不能使系统进入休眠状态
        mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
        mShowKeyguardWakeLock.setReferenceCounted(false);
    }

    private void showLocked(Bundle options) {
        // 获取PARTIAL_WAKE_LOCK
        mShowKeyguardWakeLock.acquire();
    }

    private void handleShow(Bundle options) {
        synchronized (KeyguardViewMediator.this) {
            // 释放PARTIAL_WAKE_LOCK
            mShowKeyguardWakeLock.release();
        }
    }
}

handleShow()
既然是Handler Message机制,那我们就要去看一下mHandler类实例是如何处理SHOW类型的消息了。mHandle处理SHOW类型消息的方法如下:

public class KeyguardViewMediator extends SystemUI {
    private Handler mHandler = new Handler(Looper.myLooper(), null /*callback*/, true /*async*/) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SHOW:
                    handleShow((Bundle) msg.obj);
            }
        }
    }

    private void handleShow(Bundle options) {
        synchronized (KeyguardViewMediator.this) {
            if (!mSystemReady) {
                // 系统未Ready,则不呈现锁屏
                return;
            } else {
                if (DEBUG) Log.d(TAG, "handleShow");
            }

            setShowingLocked(true);
            // 展示锁屏界面
            mStatusBarKeyguardViewManager.show(options);
            mHiding = false;
            resetKeyguardDonePendingLocked();
            mHideAnimationRun = false;
            updateActivityLockScreenState();
            adjustStatusBarLocked();
            userActivity();

            // Do this at the end to not slow down display of the keyguard.
            playSounds(true);

            mShowKeyguardWakeLock.release();
        }
        mKeyguardDisplayManager.show();
    }
}

针对这个方法,有几个重点需要强调一下。

重点一
Android5.1和Android4.4锁屏机制展示的区别?

解答:在Android5.1中,keyguard本身不再是一个独立的apk,而是跟SystemUI进行了合并,作为SystemUI的静态库进行调用。对比Android5.1和Android4.4的SystemUI模块的Android.mk文件可以更加直观的对比。

Android5.1 SystemUI Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java--files-under, src)

# 指定Keyguard作为静态库
LOCAL_STATIC_JAVA_LIBRARIES := Keyguard

LOCAL_JAVA_LIBRATIES := telephony-common
# 指定名称
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_RESOURCE_DIR := \
    frameworks/base/packages/Keyguard/res \
    $(LOCAL_PATH)/res
LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.keyguard
# 编译成apk
include $(BUILD_PACKAGE)

Android4.4 SystemUI Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
    src/com/android/systemui/EventLogTags.logtags
LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))

所以,系统调试锁屏,只需要单独编译SystemUI模块,然后替换SystemUI.apk即可。

重点二
如何替换系统锁屏,改为我们的锁屏应用?

解答:个人认为,如果能够修改SystemUI方法,那最好就是重载handleShow()方法,在这个方法中实现我们自己的锁屏界面。例如通过Activity跳转,别忘了释放PARTIAL_WEAK_LOCK。

重载这个方法,可以最大程度的不影响Android系统逻辑(ps:个人意见,大家有更好的办法可以指点我)。

StatusBarKeyguardViewManager
位置:

framework/base/package/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java

分析这个类,我们肯定是先从show方法开始入手。

show()
show方法的源码如下:

public class StatusBarKeyguardViewManager {
    public void show(Bundle options) {
        // 设置keguard是否显示的标志
        mShowing = true;
        mStatusBarWindowManager.setKeyguardShowing(true);
        // 重置view的状态,进行keyguard锁屏显示
        reset();
    }
}

reset()
源码如下:

public class StatusBarKeyguardViewManager {
    public void reset() {
        Log.e("TAG", "mShowing:" + mShowing + ", mOccluded:" + mOccluded);
        if (mShowing) {
            if (mOccluded) {
                mPhoneStatusBar.hideKeyguard();
                mBouncer.hide(false /* destroyView */);
            } else {
                // 判断是调用安全锁屏还是调用滑动锁屏
                showBouncerOrKeyguard();
            }
            updateStates();
        }
    }
}


showBouncerOrKeyguard()
public class StatusBarKeyguardViewManager {
    private void showBouncerOrKeyguard() {
        if (mBouncer.needsFullscreenBouncer()) {

            // The keyguard might be showing (already). So we need to hide it.
            mPhoneStatusBar.hideKeyguard();
            mBouncer.show(true);
        } else {
            mPhoneStatusBar.showKeyguard();
            mBouncer.hide(false);
            mBouncer.prepare();
        }
    }
}

接下来,就是view展示的过程了。其中,mBouncer是用来显示安全锁屏,例如图案、密码、PIN码等。有兴趣的同学可以继续跟踪一下mBouncer的展示或者mPhoneStatusBar的展示过程。

设置开机默认无keyguard
阅读数 58

设置开机默认无keyguard文章目录设置开机默认无keyguard1.Description2.Analysis3.solution4.summary1.Description默认情况下keygua...
博文
 

转载于:https://my.oschina.net/u/920274/blog/3052720

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值