草庐IT

Android10.0 SystemUI—keyguard锁屏加载分析

孤街酒客0911 2023-09-17 原文

学习笔记:参考资源 https://blog.csdn.net/qq_15347925/article/details/116722133

一、流程概述

1、SystemUI启动完成后,进入的第一个界面为锁屏界面。
2、锁屏keyguard属于SystemUI。
3、锁屏开机大致分为两部分,第一部分是从WindowManagerService开始,处理锁屏显示等流程。第二部分是KeyguardViewMediator的启动;


Keyguard时序图.png

二、详细介绍

1 WindowManagerService部分:

WindowManagerService在SystemService中的startOtherServices()方法里启动。

            t.traceBegin("StartWindowManagerService");
            // WMS needs sensor service ready
            ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
            mSensorServiceStart = null;
            wm = WindowManagerService.main(context, inputManager, !mFirstBoot, mOnlyCore,
                    new SprdPhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
            ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager,
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
            t.traceEnd();

SystemServer在启动SystemUI()的方法上,最后调用WindowManagerService的onSystemUiStarted方法。

    private static void startSystemUi(Context context, WindowManagerService windowManager) {
        PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
        Intent intent = new Intent();
        intent.setComponent(pm.getSystemUiServiceComponent());
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        //Slog.d(TAG, "Starting service: " + intent);
        context.startServiceAsUser(intent, UserHandle.SYSTEM);
        windowManager.onSystemUiStarted();
    }

继续跟进,会发现WindowManagerService的onSystemUiStarted方法,实际调用的是PhoneWindowManager中的onSystemUiStarted();(通过接口进行回调)

    @Override
    public void onSystemUiStarted() {
        bindKeyguard();
    }

在PhoneWindowManager中会调用bindKeyguard,KeyguardServiceDelegate作为KeyguardService的委派。

    private void bindKeyguard() {
        synchronized (mLock) {
            if (mKeyguardBound) {
                return;
            }
            mKeyguardBound = true;
        }
        mKeyguardDelegate.bindService(mContext);
    }

在KeyguardServiceDelegate的bindService方法中绑定KeyguardService。

 public void bindService(Context context) {
        Intent intent = new Intent();
        final Resources resources = context.getApplicationContext().getResources();
                //从配置文件中获取KeyguardService 
        final ComponentName keyguardComponent = ComponentName.unflattenFromString(
             resources.getString(com.android.internal.R.string.config_keyguardComponent));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        intent.setComponent(keyguardComponent);
        //绑定KeyguardService
        if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                Context.BIND_AUTO_CREATE, mHandler, UserHandle.SYSTEM)) {
            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;
            }
        } else {
            if (DEBUG) Log.v(TAG, "*** Keyguard started");
        }
    }

在ServiceConnection的连接成功回调中,创建KeyguardService包装类KeyguardServiceWrapper。包装类除了KeyguardService,还有KeyguardStateMonitor状态监视器。实际调用还是通过KeyguardService。

 private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
     @Override
     public void onServiceConnected(ComponentName name, IBinder service) {
         if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");
         // KeyguardServiceWrapper包装类调用KeyguardService的Binder实例
         mKeyguardService = new KeyguardServiceWrapper(mContext,
                 IKeyguardService.Stub.asInterface(service), mCallback);
         if (mKeyguardState.systemIsReady) {
             // If the system is ready, it means keyguard crashed and restarted.
             mKeyguardService.onSystemReady();
             if (mKeyguardState.currentUser != UserHandle.USER_NULL) {
                 // There has been a user switch earlier
                 mKeyguardService.setCurrentUser(mKeyguardState.currentUser);
             }
            // 调用KeyguardService的IPC接口
           
            ..
            ..
     }

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

在绑定以后,PhoneWindowManager可以调用代理类KeyguardServiceDelegate间接调用KeyguardService的binder接口进行各种锁屏相关状态回调。

2 KeyguardViewMediato 部分

初次开机Keyguard showLock流程:

系统启动完成-->PhoneWindowManager.systemReady()-->mKeyguardDelegate.onSystemReady()
-->mKeyguardService.onSystemReady()-->KeyguardService.onSystemReady()

在KeyguardService绑定成功后调用了onSystemReady方法。onSystemReady最终的处理流程是在KeyguardViewMediator的onSystemReady方法

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

        @Override // Binder interface
        public void onSystemReady() {
            Trace.beginSection("KeyguardService.mBinder#onSystemReady");
            checkPermission();    //权限的检查.
            mKeyguardViewMediator.onSystemReady();
            Trace.endSection();
        }

KeyguardViewMediator中的onSystemReady方法发送了一条handler消息。经过消息传递会由handleSystemReady方法处理。handleSystemReady方法的关键调用是doKeyguardLocked。

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

    public void onSystemReady() {
        mHandler.obtainMessage(SYSTEM_READY).sendToTarget();
    }

   private void handleSystemReady() {
        synchronized (this) {
            if (DEBUG) Log.d(TAG, "onSystemReady");
            mSystemReady = true;
            //关键处理
            doKeyguardLocked(null);  // handleSystemReady方法的关键调用是doKeyguardLocked。
            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();
    }

    /**
     * Enable the keyguard if the settings are appropriate.
     */
   private void doKeyguardLocked(Bundle options) {
        //如果其他应用阻止我们显示,那么就不显示。。例如:接打电话
        if (!mExternallyEnabled || PowerOffAlarmManager.isAlarmBoot()) {
            return;
        }
        
        //如果锁屏正在显示,那我们就不去显示
        if (mStatusBarKeyguardViewManager.isShowing()) {
            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
            resetStateLocked();
            if (DEBUG) {
                Log.d(TAG, "doKeyguard: not showing because it is already showing");
            }
            return;
        }
       // 判断是否无sim卡也可使用手机
       // Settings中没有启用锁屏
        ......
        //经过上述判断后,再去显示锁屏
        showLocked(options);
    }

   
    /**
      * showLocked显示锁屏方法主要处理:请求CPU不休眠,发送显示锁屏消息。
      */
    private void showLocked(Bundle options) {
       Trace.beginSection("KeyguardViewMediator#showLocked aqcuiring mShowKeyguardWakeLock");
        if (DEBUG) Log.d(TAG, "showLocked");
        // ensure we stay awake until we are finished displaying the keyguard
        // 获取PARTIAL_WAKE_LOCK,不受电源键影响,不让CPU进入休眠状态 
        mShowKeyguardWakeLock.acquire();    // 获取之后是无法让CPU休眠,不要忘记释放,不让会增加系统功耗。
        Message msg = mHandler.obtainMessage(SHOW, options);
        mHandler.sendMessage(msg);
        Trace.endSection();
    }

    /**
     * handleShow() 处理锁屏消息的方法
     */
    private void handleShow(Bundle options) {
        ..
        synchronized (KeyguardViewMediator.this) {
            ..
            setShowingLocked(true);
            //  显示keyguard
            mStatusBarKeyguardViewManager.show(options);
            ..
            // 释放mShowKeyguardWakeLock
            mShowKeyguardWakeLock.release();
        }
        mKeyguardDisplayManager.show();
        Trace.endSection();
    }

在handleShow中调用StatusBarKeyguardViewManager方法,锁屏处理由KeyguardViewMediator转移到StatusBarKeyguardViewManager。

3 其他各重要部分

3.1 StatusBarKeyguardViewManager

StatusBarKeyguardViewManager中show方法设置keyguard是否显示,通知statusbar显示锁屏,重置view的状态,进行锁屏。

 public void show(Bundle options) {
        mShowing = true;
        mStatusBarWindowManager.setKeyguardShowing(true);
        //重置状态
        reset(true /* hideBouncerWhenShowing */);
        StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
            StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
    }

StatusBarKeyguardViewManager中reset方法,主要调用showBouncerOrKeyguard方法。判断显示默认锁屏界面还是显示密码锁屏。默认锁屏界面由StatusBar管理,而密码解锁则调用KeyguardBouncer类。

// 判断是调用安全锁屏还是调用滑动锁屏
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
   //是否需要显示密码锁屏界面,即获得当前是哪一种安全模式
    if (mBouncer.needsFullscreenBouncer() && !mDozing) { 
        // The keyguard might be showing (already). So we need to hide it.
        //隐藏锁屏,显示密码解锁界面
        mStatusBar.hideKeyguard(); 
        mBouncer.show(true /* resetSecuritySelection */);
    } else {
        mStatusBar.showKeyguard();  //显示锁屏,隐藏密码解锁界面
        if (hideBouncerWhenShowing) {
            hideBouncer(shouldDestroyViewOnReset() /* destroyView */);
            mBouncer.prepare();
        }
    }
    updateStates();
}

在我这手机打日志,发现走的是else分支,mStatusBar.showKeyguard()应该是手机锁解锁界面的前面那个屏幕锁屏界面。因为我将else分支的mStatusBar.showKeyguard()改为了mStatusBar.hideKeyguard()就没有了屏幕锁屏界面。

3.2 KeyguardBouncer

在StatusBarKeyguardViewManager类中,StatusBar类则管理默认锁屏界面,KeyguardBouncer类控制密码解锁界面的,KeyguardBouncer会进行锁屏view的填充,KeyguardHostView是自定义容器,内部锁屏相关的处理在KeyguardSecurityContainer中。

private void showPrimarySecurityScreen() {
        mKeyguardView.showPrimarySecurityScreen();
    }

继续往下跟进,会发现最终会调用到KeyguardSecurityContainer类中,在showSecurityScreen方法中会根据锁屏的类型获得锁屏的view,并添加到KeyguardSecurityViewFlipper 。

  // 该方法showSecurityScreen(),用来判断加载哪种解锁界面,例如:pin锁、密码锁。如果当前类型,与传过来的参数类型一样,则是同一类型,不需要重新加载,否则重新添加视图。
  private void showSecurityScreen(SecurityMode securityMode) {
        if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
        
        //判断此参数安全模式是否与当前安全模式相同,如果相同则直接返回。
        if (securityMode == mCurrentSecuritySelection) return;
                
        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
        KeyguardSecurityView newView = getSecurityView(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();
        //寻找当前安全模式对应的view,并进行展示。(此时PIN码解锁解锁已在getSecurityView()中添加至mSecurityViewFlipper)
        //到这里view的展示也到达了本文流程的终点。
        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());
    }
    
    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
        // 获取对应类型锁的视图 id,
        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
        KeyguardSecurityView view = null;
        final int children = mSecurityViewFlipper.getChildCount();
        // 从mSecurityViewFlipper中取出此安全模式对应view id的view,按照开机初次抵达这里的情况,此时获取的view为null
        for (int child = 0; child < children; child++) {
            if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
                view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
                break;
            }
        }
        //根据安全模式获得对应的layoutId
        int layoutId = getLayoutIdFor(securityMode);
        //如果mSecurityViewFlipper还没有此view并且存在此安全模式对应的layoutId
        if (view == null && layoutId != 0) {
            //inflater Layout
            final LayoutInflater inflater = LayoutInflater.from(mContext);
            View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);//view在这里被绘制,进行各项初始化。
            view = (KeyguardSecurityView) v;//
            //如果是KeyguardSimPinPukMeView则需要设置phoneid,KeyguardSimPinPukMeView将根据此phoneid展示对应资源
            if (view instanceof KeyguardSimPinPukMeView) {
                KeyguardSimPinPukMeView pinPukView = (KeyguardSimPinPukMeView) view;
                final int phoneId = mSecurityModel.getPhoneIdUsingSecurityMode(securityMode);
                pinPukView.setPhoneId(phoneId);
            }
            //将此view添加入mSecurityViewFlipper中
            mSecurityViewFlipper.addView(v);//在这里添加view至mSecurityViewFlipper
            updateSecurityView(v); //更新KeyguardSecurityView
        }
        return view;
    }
   

private int getSecurityViewIdForMode(SecurityMode securityMode) {
        switch (securityMode) {
            case Pattern: return R.id.keyguard_pattern_view;
            case PIN: return R.id.keyguard_pin_view;
            case Password: return R.id.keyguard_password_view;
            case SimPinPukMe1:
            case SimPinPukMe2:
            case SimPinPukMe3:
            case SimPinPukMe4:
                return R.id.keyguard_sim_pin_puk_me_view ;
        }
        return 0;
}
 
 
protected int getLayoutIdFor(SecurityMode securityMode) {
        switch (securityMode) {
            case Pattern: return R.layout.keyguard_pattern_view;//手势
            case PIN: return R.layout.keyguard_pin_view;//PIN码
            case Password: return R.layout.keyguard_password_view;//密码解锁
 
            case SimPinPukMe1:
            case SimPinPukMe2:
            case SimPinPukMe3:
            case SimPinPukMe4:
                return R.layout.mtk_keyguard_sim_pin_puk_me_view;//sim_pin_puk_me
            
            default:
                return 0;
        }
}

到这里,view展示,其启动流程也到此结束。

3.3 StatusBar

StatusBar也是继承SystemUI,启动流程和SystemUI一致。并在start的时候添加创建StatusBar相关的view。

public void start() {

    // 省略部分代码...

    // 创建整个SystemUI视图并添加到WindowManager中
    createAndAddWindows(result);

    // 省略部分代码...
}

public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {

    // 创建整个SystemUI视图

    makeStatusBarView(result);

    // 把视图添加到Window中

    mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);

    mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());

}

makeStatusBarView()负责创建整个SystemUI视图,其中包括状态栏。
代码路径: packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {

      // ...

      // 1. 实例化整个SystemUI视图,包括状态栏,通知面版, 锁屏

      mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable(

      LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null);

}


有关Android10.0 SystemUI—keyguard锁屏加载分析的更多相关文章

  1. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  4. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  5. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  6. 由于 libgmp.10.dylib 的问题,Ruby 2.2.0 无法运行 - 2

    我刚刚安装了带有RVM的Ruby2.2.0,并尝试使用它得到了这个:$rvmuse2.2.0--defaultUsing/Users/brandon/.rvm/gems/ruby-2.2.0dyld:Librarynotloaded:/usr/local/lib/libgmp.10.dylibReferencedfrom:/Users/brandon/.rvm/rubies/ruby-2.2.0/bin/rubyReason:Incompatiblelibraryversion:rubyrequiresversion13.0.0orlater,butlibgmp.10.dylibpro

  7. ruby - ri 有空文件 – Ubuntu 11.10, Ruby 1.9 - 2

    我正在运行Ubuntu11.10并像这样安装Ruby1.9:$sudoapt-getinstallruby1.9rubygems一切都运行良好,但ri似乎有空文档。ri告诉我文档是空的,我必须安装它们。我执行此操作是因为我读到它会有所帮助:$rdoc--all--ri现在,当我尝试打开任何文档时:$riArrayNothingknownaboutArray我搜索的其他所有内容都是一样的。 最佳答案 这个呢?apt-getinstallri1.8编辑或者试试这个:(非rvm)geminstallrdocrdoc-datardoc-da

  8. ruby-on-rails - gem install rmagick -v 2.13.1 错误 Failed to build gem native extension on Mac OS 10.9.1 - 2

    我已经通过提供MagickWand.h的路径尝试了一切,我安装了命令工具。谁能帮帮我?$geminstallrmagick-v2.13.1Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingrmagick:ERROR:Failedtobuildgemnativeextension./Users/ghazanfarali/.rvm/rubies/ruby-1.8.7-p357/bin/rubyextconf.rbcheckingforRubyversion>=1.8.5...yescheckingfor/

  9. ruby-on-rails - 使用 gmaps4rails 动态加载谷歌地图标记 - 2

    如何只加载map边界内的标记gmaps4rails?当然,在平移和/或缩放后加载新的。与此直接相关的是,如何获取map的当前边界和缩放级别? 最佳答案 我是这样做的,我只在用户完成平移或缩放后替换标记,如果您需要不同的行为,请使用不同的事件监听器:在你看来(index.html.erb):{"zoom"=>15,"auto_adjust"=>false,"detect_location"=>true,"center_on_user"=>true}},false,true)%>在View的底部添加:functiongmaps4rail

  10. ruby - 安装 tiny_tds 在 mac os 10.10.5 上出现错误 - 2

    我正在使用macos,我想使用ruby​​驱动程序连接到sqlserver。我想使用tiny_tds,但它给出了缺少free_tds的错误,但它已经安装了。怎么能过这个?~brewinstallfreetdsWarning:freetds-0.91.112alreadyinstalled~sudogeminstalltiny_tdsBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtiny_tds:ERROR:Failedtobuildgemnativeextension.完整日志如下:/System

随机推荐