AndroidL分析之Keyguard

文章详细分析了Android L锁屏的实现原理,包括锁屏与SystemUI的关系、启动过程及界面加载显示等内容,并探讨了锁屏与KK版本的不同之处。

AndroidL 锁屏与SystemUI

AndroidL出来都这么久,AndroidM现在都有了,现在出来写这个是有点晚了,这里仅是当作自己的一个总结吧,希望新接触系统开发的人看到,能对他们有一点帮助。

在AndroidL之后(看了下M代码,基本还是跟L一样,没变化),谷歌把之前相对独立的Keyguard整合进了SystemUI之中,打开SystemUI目录可以看到很多Keyguard命名的文件。这应该是为了可以复用SystemUI通知部分代码,使系统通知可以在锁屏上显示。

但是这么一来,锁屏与SystemUI界面代码耦合,想要为系统锁屏单独增加个动画特效都变得异常艰难,对SystemUI的调整也要同时考虑到会不会对锁屏有影响。

这里先分析一下锁屏代码,以后再记录下怎么反Google,怎么样可以把系统锁屏独立出来的一些想法。由于AndroidKK锁屏流程分析,网上资源比较丰富,为节省篇幅,更多的是讨论AndroidL中锁屏与KK不太一样的地方。

锁屏启动过程

我们按照AndroidKK的思路,锁屏应该有一个service通过AIDL与系统进行交互,观察一下SystemUI中的代码结构,发现了KeyguardService.java。其实keyguardService是在PhoneWindowManagersystemBooted()中启动的:

    /** {@inheritDoc} */
    @Override
    public void systemBooted() {
        if (mKeyguardDelegate != null) {
            mKeyguardDelegate.bindService(mContext);//通过mKeyguardDelegate以bind方式启动/连接锁屏服务。
            mKeyguardDelegate.onBootCompleted();
        }
        synchronized (mLock) {
            mSystemBooted = true;
        }
        wakingUp();
        screenTurningOn(null);
    }

KeyguardService.javaonCreate函数如下:

@Override
    public void onCreate() {
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
        mKeyguardViewMediator =
                ((SystemUIApplication) getApplication()).getComponent(KeyguardViewMediator.class);
    }

startServicesIfNeeded定义在SystemUIApplication中,是SystemUI用来初始化各个模块的入口。

再看下KeyguardServiceonBind函数返回的binder对象:

private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {

        private boolean mIsOccluded;

        @Override
        public boolean isShowing() {
            return mKeyguardViewMediator.isShowing();
        }

        @Override
        public boolean isSecure() {
            return mKeyguardViewMediator.isSecure();
        }

        @Override
        public boolean isShowingAndNotOccluded() {
            return mKeyguardViewMediator.isShowingAndNotOccluded();
        }
        ... ...

锁屏这样就跟系统服务PhoneWindowManager绑定了,系统可以控制锁屏的显示,在来电时或者其他应用窗口请求暂时隐藏锁屏时,可以进行响应。
KeyguardService只是为系统提供一个查询和控制锁屏状态的入口。那么锁屏界面到底是如何加载显示到屏幕上的呢?

锁屏界面的加载显示

我们再倒回去看一下,startServicesIfNeeded到底初始化了哪些模块。

    /**
     * Makes sure that all the SystemUI services are running. If they are already running, this is a
     * no-op. This is needed to conditinally start all the services, as we only need to have it in
     * the main process.
     *
     * <p>This method must only be called from the main thread.</p>
     */
    public void startServicesIfNeeded() {
        if (mServicesStarted) {
            return;
        }

        //省略判断开机状态代码

        Log.v(TAG, "Starting SystemUI services.");
        final int N = SERVICES.length;
        for (int i=0; i<N; i++) {
            Class<?> cl = SERVICES[i];//各个模块Class
            if (DEBUG) Log.d(TAG, "loading: " + cl);
            try {
                mServices[i] = (SystemUI)cl.newInstance();//实例化
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InstantiationException ex) {
                throw new RuntimeException(ex);
            }
            mServices[i].mContext = this;//统一设置mContext
            mServices[i].mComponents = mComponents;//统一设置mComponents
            if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
            mServices[i].start();//调用每个模块的start方法。

            if (mBootCompleted) {
                mServices[i].onBootCompleted();
            }
        }
        mServicesStarted = true;
    }
    /**
     * The classes of the stuff to start.
     */
    private final Class<?>[] SERVICES = new Class[] {
            com.android.systemui.keyguard.KeyguardViewMediator.class,
            com.android.systemui.recent.Recents.class,
            com.android.systemui.volume.VolumeUI.class,
            com.android.systemui.statusbar.SystemBars.class,
            com.android.systemui.usb.StorageNotification.class,
            com.android.systemui.power.PowerUI.class,
            com.android.systemui.media.RingtonePlayer.class
    };//所有的这些类都继承自`SystemUI`这个父类。Google的这个做法非常好的把每个模块分开初始化,又统一设置了同样的Context和Components。这个应该算是哪种设计模式呢???

我们找到了KeyguardViewMediator这个重要的类,它现在被放到了

SystemUI\src\com\android\systemui\keyguard\

中。职责跟KK时代相比基本没变,负责管理锁屏界面的加载,控制密码锁、pin码锁等界面的显示。

KeyguardViewMediator的start方法很简单:

@Override
    public void start() {
        setup();//这个方法负责初始化变量和生成实例,AndroidKK是放到KeyguardViewMediator的onCreate中调用的。
        putComponent(KeyguardViewMediator.class, this);//由于在startServicesIfNeeded中统一设置了统一的`mComponents`,所以当其他模块需要用到KeyguardViewMediator的时候,只需要简单的调用`getComponent`方法就可以取到同一个对象,这个设计真是·简单好用·。
    }

setUp()初始化AlarmManagerPowerManager等并加载了锁屏音,进行了一系列跟锁屏相关的初始化工作。

继续分析KeyguardViewMediator代码handleShow:

    /**
     * Handle message sent by {@link #showLocked}.
     * @see #SHOW
     */
    private void handleShow(Bundle options) {
        ... ...
        mStatusBarKeyguardViewManager.show(options);
        //忽略判断系统状态和其他的代码,关注这句,这个是跟布局显示相关的调用。
        ... ...
    }

继续向下追踪代码到了StatusBarKeyguardViewManager中的reset函数:

    /**
     * Reset the state of the view.
     */
    public void reset() {
        if (mShowing) {
            if (mOccluded) {//occluded 变量判断当前锁屏是否需要被隐藏。(当前台Window有SHOW_WHEN_LOCKED属性的时候)
                mPhoneStatusBar.hideKeyguard();
                mBouncer.hide(false /* destroyView */);
            } else {
                showBouncerOrKeyguard();//这一句。
            }
            updateStates();
        }
    }

往下看:

    /**
     * Shows the notification keyguard or the bouncer depending on
     * {@link KeyguardBouncer#needsFullscreenBouncer()}.
     */
    private void showBouncerOrKeyguard() {
        if (mBouncer.needsFullscreenBouncer()) {//当需要显示手机卡sim pin码锁屏的时候,返回true。

            // The keyguard might be showing (already). So we need to hide it.
            mPhoneStatusBar.hideKeyguard();
            mBouncer.show();//显示安全锁屏
        } else {
            mPhoneStatusBar.showKeyguard();//显示传统意义上的锁屏。
            mBouncer.hide(false /* destroyView */);//hide了什么?
            mBouncer.prepare();
        }
    }

看到这里,我们可能会有疑惑:这个hideKeyguardhide的是哪个keyguard?这个mBouncer又是个什么?也是锁屏吗?难道有两个锁屏在互相切换吗?

前面已经讲过,在AndroidL之后的代码中,锁屏布局的加载显示已经移到了SystemUI模块中。SystemUI这里的Keyguard指的是:
Keyguard

Bouncer是加载安全锁屏的容器:
Bouncer

找到了PhoneStatusBarBouncer的调用以后,我们查看他们的布局文件,可以发现,锁屏是依附于SystemUI的布局文件进行加载的。我们看到的系统锁屏, 其实是把SystemUI拉下展开之后的一个特殊状态,在它上面可以很方便的显示系统通知 。这个是Google在AndroidL之后重新设计的。

这样可以方便的显示系统通知,但是锁屏View和SystemUI绑定到一起,如果要调整锁屏的布局和动画特效,就要小心可能会对SystemUI产生的影响,如果要更改锁屏的触摸操作、交互逻辑,很难不影响SystemUI。
我们看到华为荣耀的锁屏效果,解锁效果非常酷炫,UI元素和交互方式也比原生锁屏丰富,他们肯定是把锁屏独立出来了。


原文地址:http://www.codingpuppy.com/post/androidl/m-suo-ping-fen-xi
<think>嗯,用户这次的问题涉及Android设备调试的多个技术点,从日志分析到adb权限管理再到设备状态监控。用户可能正在开发或测试一个自动化框架,遇到了设备授权和root权限相关的障碍。 注意到用户提供的引用信息非常具体:那个"device still authorizing"的错误很典型,通常发生在设备USB调试授权弹窗未确认时。而引用2中提到的ro.secure和ro.debuggable属性说明用户可能尝试过修改系统属性来获取root权限——这确实是高级Android开发者的常见操作。 用户提到的几个关键词值得深入分析: - NativeDeviceStateMonitor 明显是自动化测试框架里的组件 - enable-root 和 adb root 指向权限问题核心 - getprop ro.build.version.sdk 这个命令失败正是引用1里的报错点 - disable keyguard 则关联到测试时解除的需求 特别要注意用户引用的Git讨论和adb源码,说明ta已经做过一定调研。因此回答需要包含: 1. 设备授权问题的解决方案(物理操作层面) 2. adb权限机制解析(技术原理层面) 3. 自动化测试时的设备状态管理(框架应用层面) 那个密钥保护(keyguard)的需求可能被用户遗漏了,应该补充说明。在生成相关问题时要侧重自动化测试场景,比如Appium/Espresso等工具集成时的典型问题。 最后检查技术细节:ro.build.version.sdk获取失败除了授权问题,还可能是USB线接触不良或ADB版本不匹配;而service.adb.root属性在Android 10以上需要刷机才能修改,这些都要在回答中区分说明。</think>以下是针对 Android 设备日志分析、权限管理及设备状态监控的综合指南,结合技术原理和问题排查方法: --- ### **1. 关键问题分析:`adb shell getprop` 报错 "device still authorizing"** #### **原因与解决方案** - **物理设备操作** 首次连接设备时,需在设备幕点击 **"允许USB调试"** 授权弹窗(图1)。若未授权,会触发 `error: device still authorizing`。 ```bash # 检查授权状态 adb devices -l # 若设备ID后显示 "unauthorized",需重新拔插USB线并授权 ``` - **ADB服务重启** 授权后仍报错时,重启ADB服务: ```bash adb kill-server && adb start-server ``` - **开发者选项重置** 进入设备 **设置 > 系统 > 开发者选项**,关闭再重新开启 **"USB调试"** 和 **"OEM解"**(如存在)。 --- ### **2. `adb root` 权限失效分析** #### **核心属性机制** Android 通过系统属性控制 adb 权限: | **属性** | **路径** | **作用** | **修改条件** | |----------------------|------------------------|---------------------------------------|---------------------| | `ro.secure` | `/default.prop` | 为 `1` 时禁止 adb root | 需刷入自定义 ROM | | `ro.debuggable` | `/system/build.prop` | 为 `1` 时允许调试模式获取临时 root | 需 `userdebug` 固件 | | `service.adb.root` | 运行时动态设置 | `setprop service.adb.root 1` 启用 root | 需已解 Bootloader | #### **源码逻辑解析** ([^2]) ```cpp // adb_main.cpp 核心逻辑 #if defined(ALLOW_ADBD_ROOT) property_get("service.adb.root", value, ""); bool adb_root = (strcmp(value, "1") == 0); bool adb_unroot = (strcmp(value, "0") == 0); if (ro_debuggable && adb_root) { drop = false; // 保留 root 权限 } if (adb_unroot) { drop = true; // 主动降权 } #endif ``` - **生效条件**:需同时满足 **`ro.debuggable=1`** 和 **`service.adb.root=1`** - **常见限制**:厂商正式版固件 (`user` 版本) 通常设置 `ro.secure=1` 且 `ro.debuggable=0`,永久禁用 adb root。 --- ### **3. 自动化测试设备状态管理** #### **禁用密钥保护 (Keyguard)** 在测试脚本中强制关闭: ```java // Android 代码示例 DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); dpm.lockNow(); // 立即(需设备管理员权限) dpm.setKeyguardDisabled(true); // 禁用界面 ``` ```python # Appium 脚本示例 driver.execute_script('mobile: shell', { 'command': 'cmd deviceidle disable', 'args': ['keyguard'] # 禁用 Keyguard 服务 }) ``` #### **监控设备状态 (`NativeDeviceStateMonitor`)** - **功能**:自动化框架(如 Appium)通过此模块监听: - 设备在线/离线状态 - adb 连接稳定性 - 系统服务崩溃重启 - **关键日志过滤**: ```bash adb logcat | grep -E "DeviceMonitor|NativeDevice" # 关注 "Device not responding" 或 "Connection reset" 错误 ``` --- ### **4. 疑难排查流程** ```mermaid graph TD A[adb devices 显示 unauthorized] --> B{设备端授权确认} B -->|已授权| C[重启 adb 服务] B -->|未授权| D[重新拔插USB并授权] C --> E[仍报错?] E -->|是| F[检查 USB 线/端口] E -->|否| G[执行 adb root] G --> H[adb root 失败?] H -->|是| I[检查 ro.debuggable 属性] I --> J[属性值为 0] J --> K[刷入 userdebug 固件] ``` --- ### **相关问题** 1. 如何在不刷机的情况下临时启用 `adb root` 权限? 2. `ro.secure` 和 `ro.debuggable` 属性在 Android 安全启动流程中如何被加载? 3. 自动化测试中如何动态捕获并解析 `NativeDeviceStateMonitor` 的异常事件日志? 4. 当设备处于 "authorizing" 状态时,能否通过脚本自动点击授权弹窗? --- **引用说明** [^1]: 设备授权超时问题源于 ADB 协议未收到确认信号,需物理交互解决。 [^2]: Android 权限控制源码逻辑详见 `system/core/adb/adb_main.cpp`,降权机制由 `ro.secure` 和 SELinux 策略共同约束。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值