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

目录


锁屏时序图

研究了将近两天的Android5.1 Keyguard锁屏机制,不得不说,各种饶。这里先把锁屏流程时序图贡献给大家:
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的展示过程。


  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 4.0 及以上版本中,可以使用 DevicePolicyManager 类来实现一键锁屏功能。以下是实现的步骤: 1. 在 AndroidManifest.xml 文件中添加如下权限: ```xml <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/> <uses-permission android:name="android.permission.RESET_PASSWORD"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WRITE_SETTINGS"/> ``` 2. 创建一个继承自 DeviceAdminReceiver 的广播接收器类: ```java public class MyDeviceAdminReceiver extends DeviceAdminReceiver { } ``` 3. 在项目中获取 DevicePolicyManager 对象: ```java DevicePolicyManager mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); ``` 4. 在 Activity 中注册广播接收器(可以在 onCreate 方法中调用): ```java ComponentName mAdminName = new ComponentName(this, MyDeviceAdminReceiver.class); Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminName); startActivityForResult(intent, 0); ``` 5. 调用 DevicePolicyManager 的 lockNow 方法实现一键锁屏: ```java if (mDPM.isAdminActive(mAdminName)) { mDPM.lockNow(); } else { Toast.makeText(this, "请先激活设备管理器", Toast.LENGTH_SHORT).show(); } ``` 注意:在实现一键锁屏功能时,需要用户同意激活设备管理器。如果用户没有激活设备管理器,则无法实现一键锁屏功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值