SystemUI---RAT

news/2024/12/2 14:56:48/

System UI的内容很多,包括状态栏、通知栏、锁屏、Navigation bar和Recent等;本篇主要分析状态栏中的RAT图标、数据图标以及信号格的更新。每一个新版本,Google都会对这一块进行修改,所以不同版本之间会有些差异,本篇内容是基于Android O。
本文分成三部分:
1. System UI的启动
2. System UI的布局
3. 图标的更新


1. System UI的启动

下方的流程图从SystemServer启动,即SystemServer.main方法开始简单展示了StatusBar的启动流程。
中中

SystemServer.startSystemUi方法负责去启动SystemUIService:

    static final void startSystemUi(Context context, WindowManagerService windowManager) {Intent intent = new Intent();intent.setComponent(new ComponentName("com.android.systemui","com.android.systemui.SystemUIService"));intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);//Slog.d(TAG, "Starting service: " + intent);context.startServiceAsUser(intent, UserHandle.SYSTEM);windowManager.onSystemUiStarted();}

在启动SystemUIService时,SystemUIApplication会先被启动。SystemUIService的onCreate方法会调用SystemUIApplication.startServicesIfNeeded方法,后者构造了一系列SystemUI类型(子类)的对象,并调用了它们的start方法,其中就包括SystemBars,SystemBars.start方法如下:

 @Overridepublic void start() {if (DEBUG) Log.d(TAG, "start");createStatusBarFromConfig();}private void createStatusBarFromConfig() {if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");//"获取类信息,R.string.config_statusBarComponent的默认值是com.android.systemui.statusbar.phone.StatusBar"final String clsName = mContext.getString(R.string.config_statusBarComponent);if (clsName == null || clsName.length() == 0) {throw andLog("No status bar component configured", null);}Class<?> cls = null;try {cls = mContext.getClassLoader().loadClass(clsName);} catch (Throwable t) {throw andLog("Error loading status bar component: " + clsName, t);}try {mStatusBar = (SystemUI) cls.newInstance();//new一个实例} catch (Throwable t) {throw andLog("Error creating status bar component: " + clsName, t);}mStatusBar.mContext = mContext;mStatusBar.mComponents = mComponents;mStatusBar.start();//调用StatusBar.start方法if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());}

R.string.config_statusBarComponent的值是com.android.systemui.statusbar.phone.StatusBar

相比于之前的版本,Android O上没有了PhoneStatusBar.java类,取而代之的是StatusBar.java。布局文件super_status_bar.xml也发生了一些改变,不再include “status_bar.xml”,而是用CollapsedStatusBarFragment加载; CollapsedStatusBarFragment本身的加载是在StatusBar.makeStatusBarView方法中,下面即是这部分代码:

    FragmentHostManager.get(mStatusBarWindow).addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {CollapsedStatusBarFragment statusBarFragment =(CollapsedStatusBarFragment) fragment;statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);mStatusBarView = (PhoneStatusBarView) fragment.getView();mStatusBarView.setBar(this);mStatusBarView.setPanel(mNotificationPanel);mStatusBarView.setScrimController(mScrimController);setAreThereNotifications();checkBarModes();}).getFragmentManager().beginTransaction().replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),CollapsedStatusBarFragment.TAG).commit();

这段代码或许看起来不是那么清晰明了,因为里面包含了多个方法调用还夹杂了Java 8的的Lambda表达式; 其实addTagListener方法中的Lambda部分是回调,所以可以先忽略掉,只关注addTagListener方法的返回对象。
下面是FragmentHostManager.get方法的code:

    public static FragmentHostManager get(View view) {try {return Dependency.get(FragmentService.class).getFragmentHostManager(view);} catch (ClassCastException e) {// TODO: Some auto handling here?throw e;}}

这个方法只是调用了FragmentService的getFragmentHostManager方法,code如下:

    public FragmentHostManager getFragmentHostManager(View view) {View root = view.getRootView();FragmentHostState state = mHosts.get(root);if (state == null) {state = new FragmentHostState(root);mHosts.put(root, state);}return state.getFragmentHostManager();}

FragmentService.getFragmentHostManager方法创建了一个FragmentHostState对象,然后调用这个对象的getFragmentHostManager方法,下面是FragmentHostState的相关code:

        public FragmentHostState(View view) {mView = view;//创建一个FragmentHostManager对象mFragmentHostManager = new FragmentHostManager(mContext, FragmentService.this, mView);}public FragmentHostManager getFragmentHostManager() {return mFragmentHostManager;}

通过上面的一些列调用,我们可以看到FragmentHostManager.get方法使用参数View创建了一个新的FragmentHostManager对象,而FragmentHostManager.addTagListener方法返回的仍是对象本身。

那么FragmentHostManager.getFragmentManager()方法返回的又是什么呢?
下面是看下FragmentHostManager的构造方法和createFragmentHost方法:

    FragmentHostManager(Context context, FragmentService manager, View rootView) {mContext = context;mManager = manager;mRootView = rootView;mConfigChanges.applyNewConfig(context.getResources());createFragmentHost(null);}private void createFragmentHost(Parcelable savedState) {mFragments = FragmentController.createController(new HostCallbacks());mFragments.attachHost(null);mLifecycleCallbacks = new FragmentLifecycleCallbacks() {@Overridepublic void onFragmentViewCreated(FragmentManager fm, Fragment f, View v,Bundle savedInstanceState) {FragmentHostManager.this.onFragmentViewCreated(f);}@Overridepublic void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {FragmentHostManager.this.onFragmentViewDestroyed(f);}@Overridepublic void onFragmentDestroyed(FragmentManager fm, Fragment f) {Dependency.get(LeakDetector.class).trackGarbage(f);}};mFragments.getFragmentManager().registerFragmentLifecycleCallbacks(mLifecycleCallbacks,true);if (savedState != null) {mFragments.restoreAllState(savedState, (FragmentManagerNonConfig) null);}// For now just keep all fragments in the resumed state.mFragments.dispatchCreate();mFragments.dispatchStart();mFragments.dispatchResume();}

FragmentHostManager的构造函数没什么重要内容,只是调用了内部Private 方法createFragmentHost,该方法构造了一个FragmentController类型的对象mFragments,并设置了HostCallbacks对象作为回调; HostCallbacks(继承了FragmentHostCallback)内部有FragmentManagerImpl对象,FragmentManagerImpl是FragmentManager.java的内部类,继承了FragmentManager类。
下面是FragmentHostManager.getFragmentManager()的代码:

    public FragmentManager getFragmentManager() {return mFragments.getFragmentManager();}

又调用了FragmentController的getFragmentManager()方法,对应code如下:

    /*** Returns a {@link FragmentManager} for this controller.*/public FragmentManager getFragmentManager() {return mHost.getFragmentManagerImpl();}

代码中的mHost即是HostCallbacks对象,HostCallbacks.getFragmentManagerImpl()方法返回的就是FragmentManagerImpl对象。即FragmentHostManager.getFragmentManager()方法返回了一个FragmentManagerImpl对象。再回到StatusBar.makeStatusBarView方法中:

    FragmentHostManager.get(mStatusBarWindow).addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {CollapsedStatusBarFragment statusBarFragment =(CollapsedStatusBarFragment) fragment;statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);mStatusBarView = (PhoneStatusBarView) fragment.getView();mStatusBarView.setBar(this);mStatusBarView.setPanel(mNotificationPanel);mStatusBarView.setScrimController(mScrimController);setAreThereNotifications();checkBarModes();}).getFragmentManager().beginTransaction().replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),CollapsedStatusBarFragment.TAG).commit();

FragmentManagerImpl.beginTransaction方法会返回一个BackStackRecord对象; BackStackRecord继承了FragmentTransaction, 折腾了一圈还是回到了FragmentTransaction,然后使用replace方法将CollapsedStatusBarFragment放进布局中super_status_bar.xml中; CollapsedStatusBarFragment会加载R.layout.status_bar布局(PhoneStatusBarView)。

代码流程是看懂了,但是由于对Android UI这套理解不深,所以不明白这个设计的初衷,是为了解耦,模块化?

Dependency.java

SystemUIApplication启动的service就包含Dependency, Dependency包含一个ArrayMap <Object, DependencyProvider>类型的成员变量mProviders, Dependency.start方法中会重要类的对象放到mProviders中, 下面是部分代码:

        mProviders.put(NetworkController.class, () ->new NetworkControllerImpl(mContext, getDependency(BG_LOOPER),getDependency(DeviceProvisionedController.class)));mProviders.put(ZenModeController.class, () ->new ZenModeControllerImpl(mContext, getDependency(MAIN_HANDLER)));mProviders.put(HotspotController.class, () ->new HotspotControllerImpl(mContext));mProviders.put(CastController.class, () ->new CastControllerImpl(mContext));mProviders.put(FlashlightController.class, () ->new FlashlightControllerImpl(mContext));

2. System UI的布局

System UI的布局大致分成两部分,左边用来显示通知信息,右边用来显示时钟、信号格等信息; 而且这两部分向相反方向扩展,即左边部分从右向左扩展,右边部分从左向右扩展。本文主要关注右边部分的布局,下图简单的展示了相关的布局关系:
这里写图片描述
super_status_bar.xml没有直接include “status_bar.xml”,而是用CollapsedStatusBarFragment加载到了下面的FrameLayout布局中:

    <FrameLayout
        android:id="@+id/status_bar_container"android:layout_width="match_parent"android:layout_height="wrap_content" />

status_bar.xml部分布局如下:

<!--省略-->
<!--下面是一个线性布局-->
<LinearLayout android:id="@+id/status_bar_contents"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingStart="6dp"android:paddingEnd="8dp"android:orientation="horizontal"><!-- The alpha of this area is controlled from both PhoneStatusBarTransitions andPhoneStatusBar (DISABLE_NOTIFICATION_ICONS). --><com.android.systemui.statusbar.AlphaOptimizedFrameLayout
            android:id="@+id/notification_icon_area"android:layout_width="0dip"android:layout_height="match_parent"android:layout_weight="1"android:orientation="horizontal" /><!--系统icon显示区域--><com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"android:layout_width="wrap_content"android:layout_height="match_parent"android:orientation="horizontal"><!--include system_icons显示信号格,RAT等--><include layout="@layout/system_icons" /><!--时钟--><com.android.systemui.statusbar.policy.Clock
                android:id="@+id/clock"android:textAppearance="@style/TextAppearance.StatusBar.Clock"android:layout_width="wrap_content"android:layout_height="match_parent"android:singleLine="true"android:paddingStart="@dimen/status_bar_clock_starting_padding"android:paddingEnd="@dimen/status_bar_clock_end_padding"android:gravity="center_vertical|start"/></com.android.keyguard.AlphaOptimizedLinearLayout></LinearLayout><!--省略-->

system_icons.xml布局比较简单,如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/system_icons"android:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center_vertical"><com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"android:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center_vertical"android:orientation="horizontal"/><!--加载signal_cluster_view布局--><include layout="@layout/signal_cluster_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="@dimen/signal_cluster_margin_start"/><!--电量显示--><com.android.systemui.BatteryMeterView android:id="@+id/battery"android:layout_height="match_parent"android:layout_width="wrap_content"/>
</LinearLayout>

signal_cluster_view.xml对应的是SignalClusterView对象,当SignalClusterView.onAttachedToWindow方法被系统调用时,SignalClusterView会调用NetworkControllerImpl.addCallback方法,在这个方法中SignalClusterView.setSubs方法会被调用:

    @Overridepublic void setSubs(List<SubscriptionInfo> subs) {//如果已经包含了subs信息,那么直接returnif (hasCorrectSubs(subs)) {return;}//下面两句清空了之前的数据mPhoneStates.clear();if (mMobileSignalGroup != null) {mMobileSignalGroup.removeAllViews();}final int n = subs.size();//根据subscription信息开始加载布局for (int i = 0; i < n; i++) {inflatePhoneState(subs.get(i).getSubscriptionId());}if (isAttachedToWindow()) {applyIconTint();}}//PhoneState是SignalClusterView的内部类,每个PhoneState对象都会加载一个mobile_signal_group.xml布局,//所以mobile_signal_group.xml布局数量和active Subscription数量(SIM 数量)一致。private PhoneState inflatePhoneState(int subId) {PhoneState state = new PhoneState(subId, mContext);if (mMobileSignalGroup != null) {mMobileSignalGroup.addView(state.mMobileGroup);}mPhoneStates.add(state);return state;}

3. 图标更新

下面的流程图简单展示了UI更新的过程:
这里写图片描述
NetworkControllerImpl的构造函数使用mReceiverHandler.post方法将Runnable对象mRegisterListeners放到了消息队列里面,当mRegisterListeners执行的时候run方法会调用NetworkControllerImpl.registerListeners方法,这个方法监听了很多广播(NetworkControllerImpl是BroadcastReceiver的子类)。

    private void registerListeners() {for (int i = 0; i < mMobileSignalControllers.size(); i++) {MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);mobileSignalController.registerListener();}if (mSubscriptionListener == null) {mSubscriptionListener = new SubListener();}mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);// broadcastsIntentFilter filter = new IntentFilter();filter.addAction(WifiManager.RSSI_CHANGED_ACTION);filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);mContext.registerReceiver(this, filter, null, mReceiverHandler);mListening = true;updateMobileControllers();}

不同的广播,处理自然也不一样。
当收到广播ACTION_SIM_STATE_CHANGED的时候,updateMobileControllers、doUpdateMobileControllers、setCurrentSubscriptions等方法会先后被调用。setCurrentSubscriptions方法会根据最新的subscriptions信息构造MobileSignalController方法,并调用新对象的registerListener方法在TelephonyManager中设置监听:

    /*** Start listening for phone state changes.*/public void registerListener() {mPhone.listen(mPhoneStateListener,PhoneStateListener.LISTEN_SERVICE_STATE| PhoneStateListener.LISTEN_SIGNAL_STRENGTHS| PhoneStateListener.LISTEN_CALL_STATE| PhoneStateListener.LISTEN_DATA_CONNECTION_STATE| PhoneStateListener.LISTEN_DATA_ACTIVITY| PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA),true, mObserver);mContext.getContentResolver().registerContentObserver(Global.getUriFor(Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),true, mObserver);}

MobileSignalController监听了service state、signal strength和call state等,当这些状态变化时MobileSignalController会通知自己的监听者,调用方法顺序是:
notifyListenersIfNecessary()
–>notifyListeners()
–>notifyListeners(mCallbackHandler)
mCallbackHandler是NetworkControllerImpl在构造MobileSignalController对象的时候传进来的,即NetworkControllerImpl.mCallbackHandler。NetworkControllerImpl.mCallbackHandler可以把状态变化通知到SignalClusterView对象,因为SignalClusterView.onAttachedToWindow方法被调用的时候,SignalClusterView调用了NetworkControllerImpl.addCallback方法在NetworkControllerImpl.mCallbackHandler中设置了监听。针对MobileSignalController的调用NetworkControllerImpl.mCallbackHandler会调用setMobileDataIndicators方法,其实这个方法只是做了赋值操作,真正更新是在apply方法中。


结束!


http://www.ppmy.cn/news/399792.html

相关文章

物联网威胁监测系统最新发现一款针对IoT设备的RAT远控木马

一、背景概述 2022年7月20日&#xff0c;天穹威胁监测系统监测到IoT蜜罐系统中的D-Link Dir 817LW路由器遭受来自澳大利亚IP的攻击。系统显示目标设备被攻击成功&#xff0c;并且下载了恶意样本。实验室相关人员第一时间对该攻击事件样本进行分析&#xff0c;发现该次攻击投递…

98rat_如何使用E4rat将Linux PC的启动时间缩短一半

98rat Linux is pretty quick to boot on modern computers, but why not pare it down some more? If you’re hurting from a lack of SSD or just want to boot faster, E4rat will easily shave down your boot time. Linux可以在现代计算机上快速启动&#xff0c;但是为什…

Text-to-SQL---RAT-SQL模型

RAT-SQL RAT-SQL(Relation-Aware Transformer-SQL)是微软在2020 ACL发表的论文RAT-SQL: Relation-Aware Schema Encoding and Linking for Text-to-SQL Parsers 提出的模型&#xff0c;这个模型在Spider数据集上的结果为65.6%。RAT-SQL代码的地址为https://github.com/Microso…

kali-TheFatRat木马生成工具安装及简单使用

声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;如因此产生的一切不良后果与文章作者无关。 kali终端下载 命令&#xff1a;git clone https://github.com/Screetsec/TheFatRat.git 推荐使用浏览器github上下载&#xff0c;比命令下载要快 浏览器下载&a…

Matlab中format rat的意思(补充说明format函数用法)

format rat &#xff1a;使用分数来表示数值 例如 >> a0.25a 0.2500>> format rat>> aa 1/4补充说明MATLAB中format函数在控制输出格式中的使用方法 format 默认格式 format short 5字长定点数 format long 15字长定点数 format short e 5字长浮点数 for…

Efficient Global 2D-3D Matching for Camera Localization in a Large-Scale 3D Map

文章目录 Efficient Global 2D-3D Matching for Camera Localization in a Large-Scale 3D Map1. 相似源码choose_solution.pyeight_point.pyepipolar_match.py Efficient Global 2D-3D Matching for Camera Localization in a Large-Scale 3D Map 1. 相似源码 由于paper并没…

顺序队列和链队列

队列也是一种线性结构&#xff0c;不同于栈的是队列为先进先出的数据结构&#xff0c;遵循一边入队一边出队。 顺序队列的底层使用的是数组&#xff0c;因此需预先申请一块足够大的内存空间初始化顺序队列。除此之外&#xff0c;为了满足顺序队列中数据从队尾进&#xff0c;队头…

electron 安装包美化

项目演示 github地址 gitee地址 视频展示 ranAdmin-electron 因为electron 自带 nsis 安装包美化还是选择从nsis 入手 还真的有一家公司专门做electron 安装包美化的 利洽科技-nsNiuniuSkinUI nsNiuniuSkinUI 刚好有免费版 免费版只需要替换一下参数 参数 基本上就能符合要…