欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新

程序员文章站 2022-03-29 14:27:19
SystemUI系列文章 "Android8.1 MTK平台 SystemUI源码分析之 Notification流程" "Android8.1 MTK平台 SystemUI源码分析之 电池时钟刷新" "Android 8.1平台SystemUI 导航栏加载流程解析" 一、从布局说起 前面的文章分析 ......

systemui系列文章

android8.1 mtk平台 systemui源码分析之 notification流程

android8.1 mtk平台 systemui源码分析之 电池时钟刷新

android 8.1平台systemui 导航栏加载流程解析

一、从布局说起

前面的文章分析过,网络信号栏这块属于 system_icon_area,里面包含蓝牙、wifi、vpn、网卡、sim卡网络类型、

数据流量符号、sim卡信号格、电池、时钟。

先来看下 system_icon_area 对应的布局文件 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"/>

    <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="0dp"
        />
</linearlayout>

看到里面的 signal_cluster_view.xml 正是我们要找的信号栏布局文件,内容有点多,下面只截取我们关心的

vendor\mediatek\proprietary\packages\apps\systemui\res\layout\signal_cluster_view.xml

<com.android.systemui.statusbar.signalclusterview
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/signal_cluster"
    android:layout_height="match_parent"
    android:layout_width="wrap_content"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:paddingend="@dimen/signal_cluster_battery_padding"
    >
   ... vpn 

   ... 网卡

   ... wifi

    手机信号栏
    <linearlayout
        android:id="@+id/mobile_signal_group"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        >
    </linearlayout>

    未插入sim卡
    <framelayout
        android:id="@+id/no_sims_combo"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:contentdescription="@string/accessibility_no_sims">
        <com.android.systemui.statusbar.alphaoptimizedimageview
            android:theme="?attr/lighticontheme"
            android:id="@+id/no_sims"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:src="@drawable/stat_sys_no_sims"
            />
        <com.android.systemui.statusbar.alphaoptimizedimageview
            android:theme="?attr/darkicontheme"
            android:id="@+id/no_sims_dark"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:src="@drawable/stat_sys_no_sims"
            android:alpha="0.0"
            />
    </framelayout>
    <view
        android:id="@+id/wifi_airplane_spacer"
        android:layout_width="@dimen/status_bar_airplane_spacer_width"
        android:layout_height="4dp"
        android:visibility="gone"
        />

    飞行模式
    <imageview
        android:id="@+id/airplane"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        />
</com.android.systemui.statusbar.signalclusterview>

可以看到最外层是自定义 signalclusterview,xml里包含了 vpn、网卡、wifi、手机信号栏、未插入sim卡、飞行模式对应的 view,那么接下来看下 signalclusterview 代码

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\signalclusterview.java

public class signalclusterview extends linearlayout implements networkcontrollerimpl.signalcallback, securitycontroller.securitycontrollercallback, tunable,darkreceiver 

public signalclusterview(context context, attributeset attrs, int defstyle) {
    super(context, attrs, defstyle);

    resources res = getresources();
    mmobilesignalgroupendpadding =
            res.getdimensionpixelsize(r.dimen.mobile_signal_group_end_padding);
    mmobiledataiconstartpadding =
            res.getdimensionpixelsize(r.dimen.mobile_data_icon_start_padding);
    mwidetypeiconstartpadding = res.getdimensionpixelsize(r.dimen.wide_type_icon_start_padding);
    msecondarytelephonypadding = res.getdimensionpixelsize(r.dimen.secondary_telephony_padding);
    mendpadding = res.getdimensionpixelsize(r.dimen.signal_cluster_battery_padding);
    mendpaddingnothingvisible = res.getdimensionpixelsize(
            r.dimen.no_signal_cluster_battery_padding);

    typedvalue typedvalue = new typedvalue();
    res.getvalue(r.dimen.status_bar_icon_scale_factor, typedvalue, true);
    miconscalefactor = typedvalue.getfloat();
    //网络相关控制器
    mnetworkcontroller = dependency.get(networkcontroller.class);
    //安全相关控制器
    msecuritycontroller = dependency.get(securitycontroller.class);
    updateactivityenabled();

    /// m: add for plugin feature @ {
    mstatusbarext = opsystemuicustomizationfactorybase.getopfactory(context)
                                 .makesystemuistatusbar(context);
    /// @ }
    miswfcenable = systemproperties.get("persist.mtk_wfc_support").equals("1");
}

看到 signalclusterview 继承自 linearlayout,实现了 networkcontroller、securitycontroller(这俩类控制图标的刷新逻辑)

构造方法中通过 dependency.get() 实例化 networkcontroller、securitycontroller这俩核心

类,跟进 dependent 类中大概看了下 get()方法,其实就是通过单例模式来进行管理,里面维护了一

个数据类型为 arraymap 的 dependencyprovider 集合对象,通过 put和 get 来存取。

接着回到 signalclusterview 中看下控件都是怎么初始化的?

 @override
protected void onfinishinflate() {
    super.onfinishinflate();

    mvpn            = findviewbyid(r.id.vpn);
    methernetgroup  = findviewbyid(r.id.ethernet_combo);
    methernet       = findviewbyid(r.id.ethernet);
    methernetdark   = findviewbyid(r.id.ethernet_dark);
    mwifigroup      = findviewbyid(r.id.wifi_combo);
    mwifi           = findviewbyid(r.id.wifi_signal);
    mwifidark       = findviewbyid(r.id.wifi_signal_dark);
    mwifiactivityin = findviewbyid(r.id.wifi_in);
    mwifiactivityout= findviewbyid(r.id.wifi_out);
    mairplane       = findviewbyid(r.id.airplane);
    mnosims         = findviewbyid(r.id.no_sims);
    mnosimsdark     = findviewbyid(r.id.no_sims_dark);
    mnosimscombo    =             findviewbyid(r.id.no_sims_combo);
    mwifiairplanespacer =         findviewbyid(r.id.wifi_airplane_spacer);
    mwifisignalspacer =           findviewbyid(r.id.wifi_signal_spacer);
    mmobilesignalgroup =          findviewbyid(r.id.mobile_signal_group);

    maybescalevpnandnosimsicons();
}

这里初始化了一堆刚刚布局文件里的控件,onfinishinflate() 在 xml 布局文件被加载完成后就会调

用,我们看到布局文件中都没有给控件设置对应的 background icon,而且有的 visibility 为

gone,那么信号栏图标是如何设置对应的icon和显示的呢?

二、signalcluterview 详解

Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新

signalcluterview 中调用频率很高的方法 apply() 就是幕后黑手,通过该方法控制一系列图标的更

新, 然而 signalcallback 的如下每个回调最终都调用 apply()

1、setwifiindicators() wifi开关状态、流量上下行

2、setmobiledataindicators() 手机网络类型、信号强度、流量上下行、volte图标

3、setsubs() sim卡识别结束

4、setnosims() 未插入sim卡状态

5、setethernetindicators() 网卡状态

6、setisairplanemode() 飞行模式是否打开

7、setmobiledataenabled() sim卡数据流量是否开启

1、apply()

private void apply() {
    if (mwifigroup == null) return;

    //vpn图标
    if (mvpnvisible) {
        if (mlastvpniconid != mvpniconid) {
            seticonforview(mvpn, mvpniconid);
            mlastvpniconid = mvpniconid;
        }
        miconlogger.oniconshown(slot_vpn);
        mvpn.setvisibility(view.visible);
    } else {
        miconlogger.oniconhidden(slot_vpn);
        mvpn.setvisibility(view.gone);
    }
    if (debug) log.d(tag, string.format("vpn: %s", mvpnvisible ? "visible" : "gone"));

    //网卡图标
    if (methernetvisible) {
        if (mlastetherneticonid != metherneticonid) {
            seticonforview(methernet, metherneticonid);
            seticonforview(methernetdark, metherneticonid);
            mlastetherneticonid = metherneticonid;
        }
        methernetgroup.setcontentdescription(methernetdescription);
        miconlogger.oniconshown(slot_ethernet);
        methernetgroup.setvisibility(view.visible);
    } else {
        miconlogger.oniconhidden(slot_ethernet);
        methernetgroup.setvisibility(view.gone);
    }

    if (debug) log.d(tag,
            string.format("ethernet: %s",
                (methernetvisible ? "visible" : "gone")));
    //wifi图标
    if (mwifivisible) {
        if (mwifistrengthid != mlastwifistrengthid) {
            seticonforview(mwifi, mwifistrengthid);
            seticonforview(mwifidark, mwifistrengthid);
            mlastwifistrengthid = mwifistrengthid;
        }
        miconlogger.oniconshown(slot_wifi);
        mwifigroup.setcontentdescription(mwifidescription);
        mwifigroup.setvisibility(view.visible);
    } else {
        miconlogger.oniconhidden(slot_wifi);
        mwifigroup.setvisibility(view.gone);
    }

    if (debug) log.d(tag,
            string.format("wifi: %s sig=%d",
                (mwifivisible ? "visible" : "gone"),
                mwifistrengthid));
    //wifi数据上下行图标
    mwifiactivityin.setvisibility(mwifiin ? view.visible : view.gone);
    mwifiactivityout.setvisibility(mwifiout ? view.visible : view.gone);

    boolean anymobilevisible = false;
    /// m: support for [network type on statusbar]
    /// a spacer is set between networktype and wifi icon @ {
    if (featureoptions.mtk_cta_set) {
        anymobilevisible = true;
    }
    /// @ }
    //sim 卡组图标
    int firstmobiletypeid = 0;
    for (phonestate state : mphonestates) {
        //phonestate中的另一个apply()方法,对应网络类型、信号格等
        if (state.apply(anymobilevisible)) {
            if (!anymobilevisible) {
                firstmobiletypeid = state.mmobiletypeid;
                anymobilevisible = true;
            }
        }
    }
    if (anymobilevisible) {
        miconlogger.oniconshown(slot_mobile);
    } else {
        miconlogger.oniconhidden(slot_mobile);
    }
    //飞行模式图标
    if (misairplanemode) {
        if (mlastairplaneiconid != mairplaneiconid) {
            seticonforview(mairplane, mairplaneiconid);
            mlastairplaneiconid = mairplaneiconid;
        }
        mairplane.setcontentdescription(mairplanecontentdescription);
        miconlogger.oniconshown(slot_airplane);
        mairplane.setvisibility(visible);
    } else {
        miconlogger.oniconhidden(slot_airplane);
        mairplane.setvisibility(view.gone);
    }
    //wifi和飞行模式间隔
    if (misairplanemode && mwifivisible) {
        mwifiairplanespacer.setvisibility(view.visible);
    } else {
        mwifiairplanespacer.setvisibility(view.gone);
    }

    if (((anymobilevisible && firstmobiletypeid != 0) || mnosimsvisible) && mwifivisible) {
        mwifisignalspacer.setvisibility(view.visible);
    } else {
        mwifisignalspacer.setvisibility(view.gone);
    }
    //未插入sim卡图标组
    if (mnosimsvisible) {
        miconlogger.oniconshown(slot_mobile);
        mnosimscombo.setvisibility(view.visible);
        if (!objects.equals(msimdetected, mnosimscombo.gettag())) {
            mnosimscombo.settag(msimdetected);
            /// m:alps03596830 don't show lack of signal when airplane mode is on.
            if (msimdetected && !misairplanemode) {
                signaldrawable d = new signaldrawable(mnosims.getcontext());
                d.setdarkintensity(0);
                mnosims.setimagedrawable(d);
                mnosims.setimagelevel(signaldrawable.getemptystate(4));

                signaldrawable dark = new signaldrawable(mnosims.getcontext());
                dark.setdarkintensity(1);
                mnosimsdark.setimagedrawable(dark);
                mnosimsdark.setimagelevel(signaldrawable.getemptystate(4));
            } else {
                mnosims.setimageresource(r.drawable.stat_sys_no_sims);
                mnosimsdark.setimageresource(r.drawable.stat_sys_no_sims);
            }
        }
    } else {
        miconlogger.oniconhidden(slot_mobile);
        mnosimscombo.setvisibility(view.gone);
    }

    /// m: add for plugin feature @ {
    mstatusbarext.setcustomizednosimsvisible(mnosimsvisible);
    mstatusbarext.setcustomizedairplaneview(mnosimscombo, misairplanemode);
    /// @ }

    boolean anythingvisible = mnosimsvisible || mwifivisible || misairplanemode
            || anymobilevisible || mvpnvisible || methernetvisible;
    setpaddingrelative(0, 0, anythingvisible ? mendpadding : mendpaddingnothingvisible, 0);
}

通过上面的代码发现 apply 控制了 vpn、网卡、wifi、飞行模式、未插入sim 这几种图标的显示和隐

藏,我们看到里面有另外一个 state.apply(anymobilevisible) 用来控制 sim卡相关的图标,接下

来看下都有哪些图标呢?

2、内部类 phonestate

phonestate 是 signalclusterview 中一个内部类,控制volte、网络类型、数据是否打开、信号格

数、漫游等图标

private class phonestate {
    //sim卡id
    private final int msubid;
    //sim卡组是否可见
    private boolean mmobilevisible = false;
    //信号格数图标、数据流量是否打开图标(关闭是x,打开是网络类型小图标4g/3g)
    private int mmobilestrengthid = 0, mmobiletypeid = 0;
    ///m: add for [network type and volte on statusbar]
    //网络类型图标
    private int mnetworkicon = 0;
    //volte图标
    private int mvolteicon = 0;
    
    private int mlastmobilestrengthid = -1;
    private int mlastmobiletypeid = -1;
    private boolean mismobiletypeiconwide;
    //网络类型描述,运营商类型,或只能拨打紧急号码
    private string mmobiledescription, mmobiletypedescription;

    //整个phonestate根本局,sim信号栏组
    private viewgroup mmobilegroup;
    //信号格控件、流量图标控件、是否漫游控件
    private imageview mmobile, mmobiledark, mmobiletype, mmobileroaming;
    public boolean mroaming;
    //手机流量上下行控件
    private imageview mmobileactivityin;
    private imageview mmobileactivityout;

    public boolean mactivityin;
    public boolean mactivityout;
    /// m: add for new features @ {
    // add for [network type and volte on statusbar]
    //网络类型控件
    private imageview mnetworktype;
    //volte控件
    private imageview mvoltetype;
    private boolean miswfccase;
    /// @ }

    /// m: add for plugin features. @ {
    private boolean mdataactivityin, mdataactivityout;
    private isystemuistatusbarext mphonestateext;
    /// @ }

    public phonestate(int subid, context context) {
        //加载 mobile_signal_group_ext 布局文件
        viewgroup root = (viewgroup) layoutinflater.from(context)
                .inflate(r.layout.mobile_signal_group_ext, null);

        /// m: add data group for plugin feature. @ {
        mphonestateext = opsystemuicustomizationfactorybase.getopfactory(context)
                            .makesystemuistatusbar(context);
        mphonestateext.addcustomizedview(subid, context, root);
        /// @ }

        setviews(root);
        msubid = subid;
    }

    //控件初始化
    public void setviews(viewgroup root) {
        mmobilegroup    = root;
        mmobile         = root.findviewbyid(r.id.mobile_signal);
        mmobiledark     = root.findviewbyid(r.id.mobile_signal_dark);
        mmobiletype     = root.findviewbyid(r.id.mobile_type);
       ///m: add for [network type and volte on statusbar]
        mnetworktype    = (imageview) root.findviewbyid(r.id.network_type);
        mvoltetype      = (imageview) root.findviewbyid(r.id.volte_indicator_ext);
        mmobileroaming  = root.findviewbyid(r.id.mobile_roaming);
        mmobileactivityin = root.findviewbyid(r.id.mobile_in);
        mmobileactivityout = root.findviewbyid(r.id.mobile_out);

        // todo: remove the 2 instances because now the drawable can handle darkness.
        mmobile.setimagedrawable(new signaldrawable(mmobile.getcontext()));
        signaldrawable drawable = new signaldrawable(mmobiledark.getcontext());
        drawable.setdarkintensity(1);
        mmobiledark.setimagedrawable(drawable);
    }

    public boolean apply(boolean issecondaryicon) {
        log.e(tag, "apply()  mmobilevisible = " + mmobilevisible
                            + ", misairplanemode = " + misairplanemode
                            + ", miswfcenable = " + miswfcenable
                            + ", miswfccase = " + miswfccase
                            + ", mvolteicon = " + mvolteicon);
        if (mmobilevisible && !misairplanemode) {
             log.e(tag, "apply() into this code 1.. mmobilestrengthid=="+mmobilestrengthid);
            //设置信号格数
            if (mlastmobilestrengthid != mmobilestrengthid) {
                mmobile.getdrawable().setlevel(mmobilestrengthid);
                mmobiledark.getdrawable().setlevel(mmobilestrengthid);
                
                mlastmobilestrengthid = mmobilestrengthid;
            }
            
            //设置流量是否打开
            if (mlastmobiletypeid != mmobiletypeid) {
                if (!mphonestateext.disablehostfunction()) {
                    mmobiletype.setimageresource(mmobiletypeid);
                }
                mlastmobiletypeid = mmobiletypeid;
            }

            mmobilegroup.setcontentdescription(mmobiletypedescription
                    + " " + mmobiledescription);
            mmobilegroup.setvisibility(view.visible);
            showviewinwfccase();
        } else {
            if (misairplanemode && (miswfcenable && mvolteicon != 0)) {
                 log.e(tag, "apply() into this code 2..");
                /// m:bug fix for show vowifi icon in flight mode
                mmobilegroup.setvisibility(view.visible);
                hideviewinwfccase();
            } else {
                 log.e(tag, "apply() into this code 3..");
                if (debug) {
                    log.d(tag, "setvisibility as gone, this = " + this
                            + ", mmobilevisible = " + mmobilevisible
                            + ", misairplanemode = " + misairplanemode
                            + ", miswfcenable = " + miswfcenable
                            + ", mvolteicon = " + mvolteicon);
                }
                mmobilegroup.setvisibility(view.gone);
            }
        }

        /// m: set all added or customised view. @ {
        //更新网络类型和volte图标
        setcustomizeviewproperty();
        /// @ }

        // when this isn't next to wifi, give it some extra padding between the signals.
        mmobilegroup.setpaddingrelative(issecondaryicon ? msecondarytelephonypadding : 0,
                0, 0, 0);
        mmobile.setpaddingrelative(
                mismobiletypeiconwide ? mwidetypeiconstartpadding : mmobiledataiconstartpadding,
                0, 0, 0);
        mmobiledark.setpaddingrelative(
                mismobiletypeiconwide ? mwidetypeiconstartpadding : mmobiledataiconstartpadding,
                0, 0, 0);

        if (true) log.d(tag, string.format("mobile: %s sig=%d typ=%d",
                    (mmobilevisible ? "visible" : "gone"), mmobilestrengthid, mmobiletypeid));


         log.e(tag, "mactivityin="+mactivityin+" mactivityout="+mactivityout);   
        if(!miswfccase) {
            //更新流量是否打开可见性
            mmobiletype.setvisibility(mmobiletypeid != 0 ? view.visible :view.gone);
            //漫游图标
            mmobileroaming.setvisibility(mroaming ? view.visible : view.gone);
            //流量上下行图标
            mmobileactivityin.setvisibility(mactivityin ? view.visible : view.gone);
            mmobileactivityout.setvisibility(mactivityout ? view.visible : view.gone);
        }

        /// m: add for support plugin featurs. @ {
        //可通过op01/2/3等加载systemui,从6.0延伸来的
        setcustomizedopviews();
        /// @ }

        return mmobilevisible;
    }

 ......
}

phonestate 中的成员变量较多,我在代码里都已经加了注释了,就不细说了。加载的布局文件为

vendor\mediatek\proprietary\packages\apps\systemui\res_ext\layout\mobile_signal_group_ext.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- support [network type on statusbar] the layout to wrap original
mobile_signal_group and add image view for show network type -->
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    >
    <imageview
        android:id="@+id/volte_indicator_ext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"
        />
    <imageview
        android:id="@+id/network_type"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:visibility="gone"
        /> 
    <include layout="@layout/mobile_signal_group"/>
  
</linearlayout>

布局文件中对应了 volte 和当前网络类型大图标, 再包含了 mobile_signal_group,里面就是上面提到的各种控件

vendor\mediatek\proprietary\packages\apps\systemui\res\layout\mobile_signal_group.xml

3、state.apply(anymobilevisible) 调用流程

通过上面的分析可以总结下 信号栏的调用流程

networkcontrollerimpl.java 中注册了sim卡状态改变广播(action_sim_state_changed),当收

到广播通知后调用到 notifyalllisteners()

通知所有的监听,mobilesignalcontroller、mwifisignalcontroller、

methernetsignalcontroller,分别对应手机信号显示、wifi信号、网卡显示通知

我们看到 mobilesignalcontroller 中的回调 notifylisteners(signalcallback callback),

在进行一系列的赋值操作后,最终回调到

signalclusterview 中的 setmobiledataindicators(),给 phonestate 的成员变量赋值,

最后通过 apply()进行更新

4、phonestate创建

通过刚刚对 phonestate 类的介绍,发现通过构造方法 phonestate(int subid, context context) 可获取 phonestate 对象

搜索当前文件找到在 inflatephonestate(int subid) 中实例化 phonestate

@override
public void setsubs(list<subscriptioninfo> subs) {
    if (debug) {
        log.d(tag, "setsubs, this = " + this + ", size = " + subs.size()
            + ", subs = " + subs);
    }
    if (hascorrectsubs(subs)) {//判断sim卡是否改变
        if (debug) {
            log.d(tag, "setsubs, hascorrectsubs and return");
        }
        return;
    }
    mphonestates.clear();
    if (mmobilesignalgroup != null) {
        mmobilesignalgroup.removeallviews();//移除手机信号组中的所有view 
    }
    final int n = subs.size();//sim卡数量
    log.d(tag, "setsubs-clear subsize:" + subs.size() + "mstes" + mphonestates + ":" + this);
    for (int i = 0; i < n; i++) {
        inflatephonestate(subs.get(i).getsubscriptionid());
    }
    if (isattachedtowindow()) {
        applyicontint();//图标根据背景色动态变化
    }
}

private phonestate inflatephonestate(int subid) {
    phonestate state = new phonestate(subid, mcontext);
    if (mmobilesignalgroup != null) {
        mmobilesignalgroup.addview(state.mmobilegroup);//添加view到手机信号组中
    }
    log.d(tag, "inflatephonestate add subid:" + subid + ",state" + state + ":" + this);
    mphonestates.add(state);//存储phonestate 对象,方便快速修改状态
    return state;
}

当sim卡插入识别后将回调 setsubs(),先判断sim卡是否改变,有效则移除 mobilegroup 中已添加

所有view,遍历sim卡数量,通过 subid 创建 phonestate,并将对应的view添加到 mobilegroup 中。

然后将 phonestate对象存储到集合中,方便快速修改状态

这里简单说下 subid subid对应卡,slotid对应卡槽

slotid或者phoneid是指卡槽,双卡机器的卡槽1值为0,卡槽2值为1,依次类推

subid的值从1开始,每插入一个新卡,subid的值就会加1。

插入双卡后数据库中就会有subid值为1和2的两个数据条目,

拔卡插卡交换卡槽后,数据库并不会增加新项,只有插入一张新的sim卡才会增加一条id为3的数据条目

详细的介绍请看这篇 subid、slotid

5、sim卡插入后更新图标流程

phonestate 创建成功了并存到集合中,当收到 setmobiledataindicators()回调后

给 phonestate 成员变量赋值,赋值结束通过apply()更新

还记得上面说过的 apply() 中更新sim卡图标的逻辑吧,

遍历 mphonestates 集合,调用phonestate的apply()将成员变量值设置给对应的控件

int firstmobiletypeid = 0;
    for (phonestate state : mphonestates) {
        if (state.apply(anymobilevisible)) {
            if (!anymobilevisible) {
                firstmobiletypeid = state.mmobiletypeid;
                anymobilevisible = true;
            }
        }
    }

那么 setmobiledataindicators() 是从哪里回调过来的呢?

分析找到 mobilesignalcontroller 中的 notifylisteners()

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\policy\mobilesignalcontroller.java

@override
public void notifylisteners(signalcallback callback) {
    //获取资源id组
    mobileicongroup icons = geticons();

    string contentdescription = getstringifexists(getcontentdescription());
    string datacontentdescription = getstringifexists(icons.mdatacontentdescription);
    //移动数据是否开启
    final boolean datadisabled = mcurrentstate.icongroup == telephonyicons.data_disabled
            && mcurrentstate.usersetup;

    /// m: customize the signal strength icon id. @ {
    //当前手机信号格数资源id
    int iconid = getcurrenticonid();
    //用户可自定义的资源id,该方法将目前的iconid原值赋给了iconid,如需定制可在此修改
    iconid = mstatusbarext.getcustomizesignalstrengthicon(
                msubscriptioninfo.getsubscriptionid(),
                iconid,
                msignalstrength,
                mdatanettype,
                mservicestate);
    /// @ }

    // show icon in qs when we are connected or data is disabled.
    //是否显示移动数据图标
    boolean showdataicon = mcurrentstate.dataconnected || datadisabled;
    //是否显示 mobilegroup、信号格数、sim卡信息描述
    iconstate statusicon = new iconstate(mcurrentstate.enabled && !mcurrentstate.airplanemode,
            iconid, contentdescription);

    int qstypeicon = 0;
    iconstate qsicon = null;
    string description = null;
    // only send data sim callbacks to qs.
    if (mcurrentstate.datasim) {
        qstypeicon = showdataicon ? icons.mqsdatatype : 0;
        qsicon = new iconstate(mcurrentstate.enabled
                && !mcurrentstate.isemergency, getqscurrenticonid(), contentdescription);
        //状态栏显示只能拨打紧急电话或当前的网络类型
        description = mcurrentstate.isemergency ? null : mcurrentstate.networkname;
    }
    //数据下行
    boolean activityin = mcurrentstate.dataconnected
            && !mcurrentstate.carriernetworkchangemode
            && mcurrentstate.activityin;
    //数据上行
    boolean activityout = mcurrentstate.dataconnected
            && !mcurrentstate.carriernetworkchangemode
            && mcurrentstate.activityout;
    showdataicon &= mcurrentstate.isdefault || datadisabled;
    //移动数据类型资源id,关闭是x,打开是小的网络类型4g/3g/2g
    int typeicon = showdataicon ? icons.mdatatype : 0;

    /// m: add for lwa.
    typeicon = mcurrentstate.lwaregstate == networktypeutils.lwa_state_conncted
            && showdataicon ? networktypeutils.lwa_icon : typeicon;
    /** m: support [network type on statusbar], change the implement methods.
      * get the network icon base on service state.
      * add one more parameter for network type.
      * @ { **/
    //当前网络类型资源id
    int networkicon = mcurrentstate.networkicon;
    /// m: support volte icon.bug fix when airplane mode is on go to hide volte icon
    //volte资源id
    int volteicon = mcurrentstate.airplanemode && !isimsoverwfc()
            ? 0 : mcurrentstate.volteicon;

    /// m: when data disabled, common show data icon as x, but op do not need show it @ {
    mstatusbarext.isdatadisabled(msubscriptioninfo.getsubscriptionid(), datadisabled);
    /// @ }

    /// m: customize the data type icon id. @ {
    //可自定义移动数据类型资源id(比如常见的上下箭头)
    typeicon = mstatusbarext.getdatatypeicon(
                    msubscriptioninfo.getsubscriptionid(),
                    typeicon,
                    mdatanettype,
                    mcurrentstate.dataconnected ? telephonymanager.data_connected :
                        telephonymanager.data_disconnected,
                    mservicestate);
    /// @ }
    /// m: customize the network type icon id. @ {
    //可自定义网络类型资源id
    networkicon = mstatusbarext.getnetworktypeicon(
                    msubscriptioninfo.getsubscriptionid(),
                    networkicon,
                    mdatanettype,
                    mservicestate);

    callback.setmobiledataindicators(statusicon, qsicon, typeicon, networkicon, volteicon,
            qstypeicon,activityin, activityout, datacontentdescription, description,
             icons.miswide, msubscriptioninfo.getsubscriptionid(), mcurrentstate.roaming);

    /// m: update plmn label @{
    mnetworkcontroller.refreshplmncarrierlabel();
    /// @}
}

这个方法比较重要,上面写了简单的注释,接下来我们会详细看下每个资源id都是如何获取的?

在这之前我们先介绍下几个重要的 bean 类

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\policy\signalcontroller.java

state

static class state {
    boolean connected;
    boolean enabled;
    boolean activityin;
    boolean activityout;
    int level;
    icongroup icongroup;
    int inetcondition;
    int rssi;
    ...
}

icongroup

static class icongroup {
    final int[][] msbicons;
    final int[][] mqsicons;
    final int[] mcontentdesc;
    final int msbnullstate;
    final int mqsnullstate;
    final int msbdiscstate;
    final int mqsdiscstate;
    final int mdisccontentdesc;
    // for logging.
    final string mname;
    ....
}

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\policy\mobilesignalcontroller.java

mobilestate

static class mobilestate extends signalcontroller.state {
    string networkname;//当前网络类型
    string networknamedata;//移动数据网络类型
    boolean datasim;
    boolean dataconnected;//数据是否连接
    boolean isemergency;//是否是紧急电话模式
    boolean airplanemode;//是否是飞行模式
    boolean carriernetworkchangemode;//sim卡网络类型是否改变
    boolean isdefault;
    boolean usersetup;//是否是用户操作
    boolean roaming;//是否漫游

    /// m: add for 4g+w
    int lwaregstate = networktypeutils.lwa_state_unknown;
    /// m: for network type big icon.
    int networkicon;//网络类型大图标资源id
    /// m: add for data network type.
    int datanettype;//移动数据网络类型
    /// m: add for op network tower type.
    int customizedstate;//自定义状态
    /// m: add for op signal strength tower icon.
    int customizedsignalstrengthicon;//自定义信号格资源id
    /// m: add for volte @{
    int imsregstate = servicestate.state_power_off;
    int imscap;
    int volteicon;//volte资源id

    ......  
}

mobileicongroup

static class mobileicongroup extends signalcontroller.icongroup {
    final int mdatacontentdescription; // mcontentdescriptiondatatype
    final int mdatatype;//移动数据网络类型资源id
    final boolean miswide;
    final int mqsdatatype;//下拉快捷访问资源

    ...     
}

好了重要的bean类介绍完了,接下来又要说一个重要的方法了 updatetelephony()

还是在 mobilesignalcontroller.java 中

private final void updatetelephony() {
    if (debug && featureoptions.log_enable) {
        log.d(mtag, "updatetelephonysignalstrength: hasservice=" + hasservice()
                + " ss=" + msignalstrength);
    }
    //连接状态,是否在服务中
    mcurrentstate.connected = hasservice() && msignalstrength != null;
    handleiwlannetwork();
    if (mcurrentstate.connected) {
        //sim 卡信号格数级别 0~4格
        if (!msignalstrength.isgsm() && mconfig.alwaysshowcdmarssi) {
            mcurrentstate.level = msignalstrength.getcdmalevel();
        } else {
            mcurrentstate.level = msignalstrength.getlevel();
        }
        /// m: customize the signal strength level. @ {
        //客户可自定义
        mcurrentstate.level = mstatusbarext.getcustomizesignalstrengthlevel(
                mcurrentstate.level, msignalstrength, mservicestate);
        /// @ }
    }
    //当前网络类型获取对应的图标组
    if (mnetworktoiconlookup.indexofkey(mdatanettype) >= 0) {
        mcurrentstate.icongroup = mnetworktoiconlookup.get(mdatanettype);
    } else {
        mcurrentstate.icongroup = mdefaulticons;
    }
    /// m: add for data network type.
    //数据网络类型
    mcurrentstate.datanettype = mdatanettype;
    //数据状态
    mcurrentstate.dataconnected = mcurrentstate.connected
            && mdatastate == telephonymanager.data_connected;
    /// m: add for op network tower type.
    mcurrentstate.customizedstate = mstatusbarext.getcustomizecsstate(mservicestate,
            mcurrentstate.customizedstate);
    /// m: add for op signal strength tower icon.
    mcurrentstate.customizedsignalstrengthicon = mstatusbarext.getcustomizesignalstrengthicon(
            msubscriptioninfo.getsubscriptionid(),
            mcurrentstate.customizedsignalstrengthicon,
            msignalstrength,
            mdatanettype,
            mservicestate);

    mcurrentstate.roaming = isroaming();
    if (iscarriernetworkchangeactive()) {
        mcurrentstate.icongroup = telephonyicons.carrier_network_change;
    } else if (isdatadisabled()) {//数据未打开,对应x
        mcurrentstate.icongroup = telephonyicons.data_disabled;
    }
    if (isemergencyonly() != mcurrentstate.isemergency) {
        mcurrentstate.isemergency = isemergencyonly();
        mnetworkcontroller.recalculateemergency();
    }
    // fill in the network name if we think we have it.
    //当前网络运营商
    if (mcurrentstate.networkname == mnetworknamedefault && mservicestate != null
            && !textutils.isempty(mservicestate.getoperatoralphashort())) {
        mcurrentstate.networkname = mservicestate.getoperatoralphashort();
    }
    /// m: for network type big icon. 网络类型大图标
    mcurrentstate.networkicon =
        networktypeutils.getnetworktypeicon(mservicestate, mconfig, hasservice());
    /// m: for volte type icon. volte图标
    mcurrentstate.volteicon = getvolteicon();
    
    //通知更新,最终回调到notifylisteners()中
    notifylistenersifnecessary();
}

基本上获取资源id的方法都在 updatetelephony()中了,那么都在那里调用了 updatetelephony()?

mobilesignalcontroller中构造方法初始化了 mobilephonestatelistener 分别监听了

mphone.listen(mphonestatelistener,
phonestatelistener.listen_service_state//服务状态改变,可用、不可用
        | phonestatelistener.listen_signal_strengths//信号强度改变,用于获取dbm、asu
        | phonestatelistener.listen_call_state//电话状态改变,空闲、来电、通话
        | phonestatelistener.listen_data_connection_state//数据网络连接状态,网络断开、正在连接中、已连接上
        | phonestatelistener.listen_data_activity//数据上下行状态
        | phonestatelistener.listen_carrier_network_change);//网络状态发送改变

class mobilephonestatelistener extends phonestatelistener {
    public mobilephonestatelistener(int subid, looper looper) {
        super(subid, looper);
    }

    @override
    public void onsignalstrengthschanged(signalstrength signalstrength) {
        ...
        updatetelephony();
    }

    @override
    public void onservicestatechanged(servicestate state) {
        ...
        updatetelephony();
    }

    @override
    public void ondataconnectionstatechanged(int state, int networktype) {
        ...
        updatetelephony();
    }

    @override
    public void ondataactivity(int direction) {
        ...
        setactivity(direction);
    }

    @override
    public void oncarriernetworkchange(boolean active) {
       ...
        updatetelephony();
    }

    /// m: add for plugin feature. @{
    @override
    public void oncallstatechanged(int state, string incomingnumber) {
        ...
        updatetelephony();
       
    }
    /// @}
};

listen_signal_strengths、listen_call_state、listen_carrier_network_change

这三个监听应该是我们平常用较多的,好了说了这么久,接下来重要看获取资源id的具体方法了

5.1、vlote资源id

mcurrentstate.volteicon = getvolteicon();

private int getvolteicon() {
    int icon = 0;
    if (isimsoverwfc()) {
        boolean needshowwfcsysicon = mstatusbarext.needshowwfcicon();
        if (needshowwfcsysicon) {
            icon = networktypeutils.wfc_icon;
        }
    } else if (isimsovervoice() && isltenetwork()) {
        if (mcurrentstate.imsregstate == servicestate.state_in_service) {
            //volte可用
            icon = networktypeutils.volte_icon;
        } else if(featureoptions.mtk_ct_mixed_volte_support &&
            simhelper.issecondarycsimformixedvolte(msubscriptioninfo.getsubscriptionid()) &&
            mcurrentstate.imsregstate == servicestate.state_out_of_service) {
            if (debug) {
                log.d(mtag, "set dis volte icon");
            }//volte不可用
            icon = networktypeutils.volte_dis_icon;
        }
    }
    /// m: add for disconnected volte feature. @{
    mstatusbarext.setimsreginfo(msubscriptioninfo.getsubscriptionid(),
            mcurrentstate.imsregstate, isimsoverwfc(), isimsovervoice());
    /// @}
    return icon;
}

vendor\mediatek\proprietary\packages\apps\systemui\src\com\mediatek\systemui\statusbar\networktype\networktypeutils.java

public static final int volte_icon = r.drawable.stat_sys_volte;

Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新

5.2、网络类型大图标资源id

vendor\mediatek\proprietary\packages\apps\systemui\src\com\mediatek\systemui\statusbar\networktype\networktypeutils.java

mcurrentstate.networkicon =
        networktypeutils.getnetworktypeicon(mservicestate, mconfig, hasservice());

public static int getnetworktypeicon(servicestate servicestate, config config,
        boolean hasservice) {
    if (!hasservice) {
        // not in service, no network type. 未注册成功,比如废卡、停机卡
        return 0;
    }
    //通过 servicestate 获取当前注册的网络类型
    int tempnetworktype = getnetworktype(servicestate);

    integer iconid = snetworktypeicons.get(tempnetworktype);
    if (iconid == null) {
        iconid = tempnetworktype == telephonymanager.network_type_unknown ? 0 :
                 config.showatleast3g ? r.drawable.stat_sys_network_type_3g :
                                        r.drawable.stat_sys_network_type_g;
    }
    return iconid.intvalue();
}

private static int getnetworktype(servicestate servicestate) {
    int type = telephonymanager.network_type_unknown;
    if (servicestate != null) {
        type = servicestate.getdatanetworktype() != telephonymanager.network_type_unknown ?
                servicestate.getdatanetworktype() : servicestate.getvoicenetworktype();
    }
    return type;
}

//网络类型-资源id    4g/3g/2g/e/1x
static final map<integer, integer> snetworktypeicons = new hashmap<integer, integer>() {
    {
        // for cdma 3g
        put(telephonymanager.network_type_evdo_0, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_evdo_a, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_evdo_b, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_ehrpd, r.drawable.stat_sys_network_type_3g);
        // for cdma 1x
        put(telephonymanager.network_type_cdma, r.drawable.stat_sys_network_type_1x);
        put(telephonymanager.network_type_1xrtt, r.drawable.stat_sys_network_type_1x);
        // edge
        put(telephonymanager.network_type_edge, r.drawable.stat_sys_network_type_e);
        // 3g
        put(telephonymanager.network_type_umts, r.drawable.stat_sys_network_type_3g);
        // for 4g
        put(telephonymanager.network_type_lte, r.drawable.stat_sys_network_type_4g);
        // 3g
        put(telephonymanager.network_type_hsdpa, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_hsupa, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_hspa, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_hspap, r.drawable.stat_sys_network_type_3g);
        put(telephonymanager.network_type_iwlan, 0);
    }
};

Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新

5.3、移动数据类型资源id

//mnetworktoiconlookup 和上面的网络类型 map有点类似,键都是网络类型,
//不同的是,这次的key是上面介绍过的 mobileicongroup
if (mnetworktoiconlookup.indexofkey(mdatanettype) >= 0) {
    mcurrentstate.icongroup = mnetworktoiconlookup.get(mdatanettype);
} else {
    mcurrentstate.icongroup = mdefaulticons;
}

if (iscarriernetworkchangeactive()) {
    mcurrentstate.icongroup = telephonyicons.carrier_network_change;
} else if (isdatadisabled()) {
    mcurrentstate.icongroup = telephonyicons.data_disabled;
}

vendor\mediatek\proprietary\packages\apps\systemui\src\com\android\systemui\statusbar\policy\telephonyicons.java

class telephonyicons {
    //***** data connection icons
    
    //状态栏快捷访问,其实和下面的差多不
    static final int qs_data_g = r.drawable.ic_qs_signal_g;
    static final int qs_data_3g = r.drawable.ic_qs_signal_3g;
    static final int qs_data_e = r.drawable.ic_qs_signal_e;
    static final int qs_data_h = r.drawable.ic_qs_signal_h;
    static final int qs_data_1x = r.drawable.ic_qs_signal_1x;
    static final int qs_data_4g = r.drawable.ic_qs_signal_4g;
    static final int qs_data_4g_plus = r.drawable.ic_qs_signal_4g_plus;
    static final int qs_data_lte = r.drawable.ic_qs_signal_lte;
    static final int qs_data_lte_plus = r.drawable.ic_qs_signal_lte_plus;

    static final int flight_mode_icon = r.drawable.stat_sys_airplane_mode;
    
    //此处的图标为小图标,网络类型
    static final int icon_lte = r.drawable.stat_sys_data_fully_connected_lte;
    static final int icon_lte_plus = r.drawable.stat_sys_data_fully_connected_lte_plus;
    static final int icon_g = r.drawable.stat_sys_data_fully_connected_g;
    static final int icon_e = r.drawable.stat_sys_data_fully_connected_e;
    static final int icon_h = r.drawable.stat_sys_data_fully_connected_h;
    static final int icon_3g = r.drawable.stat_sys_data_fully_connected_3g;
    static final int icon_4g = r.drawable.stat_sys_data_fully_connected_4g;
    static final int icon_4g_plus = r.drawable.stat_sys_data_fully_connected_4g_plus;
    static final int icon_1x = r.drawable.stat_sys_data_fully_connected_1x;

    //流量未打开
    static final int icon_data_disabled = r.drawable.stat_sys_data_disabled;
    static final int qs_icon_data_disabled = r.drawable.ic_qs_data_disabled;

    ...

    static final mobileicongroup data_disabled = new mobileicongroup(
        "datadisabled",
        null,
        null,
        accessibilitycontentdescriptions.phone_signal_strength,
        0, 0,
        0,
        0,
        accessibilitycontentdescriptions.phone_signal_strength[0],
        r.string.accessibility_cell_data_off,
        telephonyicons.icon_data_disabled,//这个值对应的就是 移动数据类型资源id
        false,
        telephonyicons.qs_icon_data_disabled
        );

}

mobileicongroup 的倒数第三个参数就是 移动数据类型资源id

Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新

5.4、信号格数资源id

if (mcurrentstate.connected) {
        if (!msignalstrength.isgsm() && mconfig.alwaysshowcdmarssi) {
            mcurrentstate.level = msignalstrength.getcdmalevel();
        } else {
            mcurrentstate.level = msignalstrength.getlevel();
        }
        /// m: customize the signal strength level. @ {
        mcurrentstate.level = mstatusbarext.getcustomizesignalstrengthlevel(
                mcurrentstate.level, msignalstrength, mservicestate);
        /// @ }
 }

信号格数对应的是 signaldrawable,通过 setlevel()来控制显示几格,其实以上的大部分资源id都是

通过 vector 标签绘制而来的,里面都是一堆 path,开始看可能会觉得很迷糊,可以把xml文件拷贝到

as中进行预览,再学上一些基础语法就可对简单的图形进行自定义修改。比方说6.0的信号格数是通过

vector 绘制的,格与格之间是有间隔,而8.1是通过 signaldrawable绘制,是一个填满的三角形

Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新
修改前样式
Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新
修改后样式

来看下 msignalstrength.getlevel() 方法

frameworks/base/telephony/java/android/telephony/signalstrength.java

    public int getlevel() {
    int level = 0;
    if (isgsm) { //移动或联通卡
        level = getltelevel(); //首先获取4g信号格
        if (level == signal_strength_none_or_unknown) { //未获取到
            level = gettdscdmalevel(); //获取移动或联通的3g信号格
            if (level == signal_strength_none_or_unknown) {//仍然未获取
                level = getgsmlevel(); //获取移动或联通的2g信号格
            }
        }
    } else {//电信
        int cdmalevel = getcdmalevel(); //获取电信2g信号格
        int evdolevel = getevdolevel(); //获取电信3g信号格
        if (evdolevel == signal_strength_none_or_unknown) {
            /* we don't know evdo, use cdma */
            level = cdmalevel;
        } else if (cdmalevel == signal_strength_none_or_unknown) {
            /* we don't know cdma, use evdo */
            level = evdolevel;
        } else {
            /* we know both, use the lowest level */
            level = cdmalevel < evdolevel ? cdmalevel : evdolevel;
        }
    }
    if (dbg) log("getlevel=" + level);
    return level;
}

在此介绍下手机是几模的配置:gsm是移动和联通公用的band;lte从编码方式上分为tdd和fdd,从频段上分有各种不同的band

移动:gsm、tdscdma、lte(tdd)

联通:gsm、wcdma、lte(fdd)

电信:cdma、evdo、lte(fdd)

因此如果手机支持gsm、wcdma、tdscdma、tdd-lte、fdd-lte 这是五模;加上 cdma、evdo 就是七模

5.5、漫游资源id r.drawable.stat_sys_roaming

大写的r

三、总结

信号栏的定制还是很容易的,只要理清楚了控件和对应的回调逻辑,加上日志打印,就能搞定你想要的效果。

四、相关资源

这里附上我定制使用的drawable文件

Android8.1 MTK平台 SystemUI源码分析之 网络信号栏显示刷新