【笔记】Android Telephony 漫游SPN显示定制(Roaming Alpha Tag)

一、功能名词简介和显示规则

Alpha Tag:运营商名称标识符,也是用于标识运营商的一个名称。客户需求描述常用名词,对开发而言都是SPN/PLMN功能模块的内容,状态栏左上角的运营商名称显示。

SPN相关文章:

【笔记】SPN和PLMN 运营商网络名称显示_spn plmn-CSDN博客

Android U 配置 WiFiCalling 场景下PLMN/SPN 显示的代码逻辑介绍

网络运营商名称显示规则:

MTK平台的设计,对运营商名称的显示rule 是基于sim相关协议来实现的。优先级是Eons>nitz> XML配置(spn-con.xml)。EONS具有最高优先级,如果拿不到的EONS的情况下,要去读NITZ里的name。(Reference :FAQ08919)

  • EONS (Enhanced Operator Name String,“增强型运营商名称字符串”):是在 GSM 网络中引入的一种机制,用于向移动设备发送关于当前所处位置和网络状态的更详细和更准确的信息,以便于移动设备更好地显示和呈现运营商信息。在传统的 GSM 网络中,运营商名称(即 SPN)通常只包含运营商的品牌名称或简称,例如“China Mobile”或“AT&T”。这种信息的显示可能无法反映出当前所处的具体位置或网络状态,例如是否在漫游状态、网络类型、是否处于特殊服务状态等。为了解决这个问题,EONS 引入了更多的信息,以便于移动设备能够更好地显示和呈现运营商信息。
  • NITZ (Network Identity and Time Zone,“网络识别码和时区”):它是一种用于向移动设备发送网络识别码和时区信息的协议,通常在移动设备启动时或网络状态发生变化时进行同步更新。

二、代码逻辑

基于 Android T&U 版本分析。

(一)T和U代码差异

两个版本有差异,主要是Android U 删除了 SubscriptionController类,新增SubscriptionManagerService类。在Android T代码中也有备注setPlmnSpn接口适用maxSDK是R,有过渡提示。

  • T:SubscriptionController.java setPlmnSpn
  • U:SubscriptionManagerService.java getCarrierName 
【Android T】SubscriptionController/setPlmnSpn接口
【Android T】SubscriptionController/setPlmnSpn接口

在ServiceStateTracker.java中,T上同事更新plmn,但U上已经不再更新SPN,也没有setPlmnSpn接口。

【Android T】ServiceStateTracker- onSubscriptionsChanged()
【Android T】ServiceStateTracker- onSubscriptionsChanged()-setPlmnSpn
【Android U】ServiceStateTracker- onSubscriptionsChanged()
【Android U】ServiceStateTracker- onSubscriptionsChanged()
【Android U】updateSpn
【Android U】updateSpnDisplay

(二)【Android T】setPlmnSpn

onSubscriptionsChanged() => setPlmnSpn() => setCarrierText() =>refreshCachedActiveSubscriptionInfoList() & notifySubscriptionInfoChanged()

Android T 更新SPN代码流程:

  1. onSubscriptionsChanged()
  2. setPlmnSpn()
  3. setCarrierText()
  4. refreshCachedActiveSubscriptionInfoList() & notifySubscriptionInfoChanged()

ServiceStateTracker.java​

frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java

在网络状态变化时SST内部类SstSubscriptionsChangedListener监听收到onSubscriptionsChanged() 回调,去更新PLMN和SPN。

【Android T】SubscriptionController/setPlmnSpn接口
【Android T】setPlmnSpn
onSubscriptionsChanged()
    private class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {/*** Callback invoked when there is any change to any SubscriptionInfo. Typically* this method would invoke {@link SubscriptionManager#getActiveSubscriptionInfoList}*/@Overridepublic void onSubscriptionsChanged() {if (DBG) log("SubscriptionListener.onSubscriptionInfoChanged");final int curSubId = mPhone.getSubId();// If the sub info changed, but the subId is the same, then we're done.if (mSubId == curSubId) return;// If not, then the subId has changed, so we need to remember the old subId,// even if the new subId is invalid (likely).mPrevSubId = mSubId;mSubId = curSubId;mPhone.notifyPhoneStateChanged();setDataNetworkTypeForPhone(mSS.getRilDataRadioTechnology());//setPlmnSpn 更新PLMN/SPNif (mSpnUpdatePending) {mSubscriptionController.setPlmnSpn(mPhone.getPhoneId(), mCurShowPlmn,mCurPlmn, mCurShowSpn, mCurSpn);mSpnUpdatePending = false;}//更新相关Settings设置内容。// Remove old network selection sharedPreferences since SP key names are now// changed to include subId. This will be done only once when upgrading from an// older build that did not include subId in the names.SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);String oldNetworkSelection = sp.getString(Phone.NETWORK_SELECTION_KEY, "");String oldNetworkSelectionName = sp.getString(Phone.NETWORK_SELECTION_NAME_KEY, "");String oldNetworkSelectionShort = sp.getString(Phone.NETWORK_SELECTION_SHORT_KEY, "");if (!TextUtils.isEmpty(oldNetworkSelection)|| !TextUtils.isEmpty(oldNetworkSelectionName)|| !TextUtils.isEmpty(oldNetworkSelectionShort)) {SharedPreferences.Editor editor = sp.edit();editor.putString(Phone.NETWORK_SELECTION_KEY + mSubId,oldNetworkSelection);editor.putString(Phone.NETWORK_SELECTION_NAME_KEY + mSubId,oldNetworkSelectionName);editor.putString(Phone.NETWORK_SELECTION_SHORT_KEY + mSubId,oldNetworkSelectionShort);editor.remove(Phone.NETWORK_SELECTION_KEY);editor.remove(Phone.NETWORK_SELECTION_NAME_KEY);editor.remove(Phone.NETWORK_SELECTION_SHORT_KEY);editor.commit();}// Once sub id becomes valid, we need to update the service provider name// displayed on the UI again. The old SPN update intents sent to// MobileSignalController earlier were actually ignored due to invalid sub id.updateSpnDisplay();}};

SubscriptionController.java

    /*** Generate and set carrier text based on input parameters* @param showPlmn flag to indicate if plmn should be included in carrier text* @param plmn plmn to be included in carrier text* @param showSpn flag to indicate if spn should be included in carrier text* @param spn spn to be included in carrier text* @return true if carrier text is set, false otherwise*/@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)public boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn,String spn) {synchronized (mLock) {int subId = getSubIdUsingPhoneId(slotIndex);if (mContext.getPackageManager().resolveContentProvider(SubscriptionManager.CONTENT_URI.getAuthority(), 0) == null ||!SubscriptionManager.isValidSubscriptionId(subId)) {// No place to store this info. Notify registrants of the change anyway as they// might retrieve the SPN/PLMN text from the SST sticky broadcast.// TODO: This can be removed once SubscriptionController is not running on devices// that don't need it, such as TVs.if (DBG) logd("[setPlmnSpn] No valid subscription to store info");notifySubscriptionInfoChanged();return false;}String carrierText = "";if (showPlmn) {carrierText = plmn;if (showSpn) {//当PLMN和SPN不相同时,就会显示PLMN-SPN// Need to show both plmn and spn if both are not same.if(!Objects.equals(spn, plmn)) {String separator = mContext.getString(com.android.internal.R.string.kg_text_message_separator).toString();carrierText = new StringBuilder().append(carrierText).append(separator).append(spn).toString();}}} else if (showSpn) {carrierText = spn;}setCarrierText(carrierText, subId);return true;}}/*** Set carrier text by simInfo index* @param text new carrier text* @param subId the unique SubInfoRecord index in database* @return the number of records updated*/private int setCarrierText(String text, int subId) {if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId);enforceModifyPhoneState("setCarrierText");// Now that all security checks passes, perform the operation as ourselves.final long identity = Binder.clearCallingIdentity();try {boolean update = true;int result = 0;SubscriptionInfo subInfo = getSubscriptionInfo(subId);if (subInfo != null) {update = !TextUtils.equals(text, subInfo.getCarrierName());}if (update) {ContentValues value = new ContentValues(1);value.put(SubscriptionManager.CARRIER_NAME, text);result = mContext.getContentResolver().update(SubscriptionManager.getUriForSubscriptionId(subId), value, null, null);// Refresh the Cache of Active Subscription Info ListrefreshCachedActiveSubscriptionInfoList();notifySubscriptionInfoChanged();} else {if (DBG) logd("[setCarrierText]: no value update");}return result;} finally {Binder.restoreCallingIdentity(identity);}}

(三) 【Android U】SetCarrierName

pollStateDone() 等 => updateSpnDisplay() => updateSpnDisplayCdnr() => notifySpnDisplayUpdate() => mSubscriptionManagerService.setCarrierName =>  mSubscriptionDatabaseManager.setCarrierName(subId, carrierName);

Android U 更新SPN代码流程:

  1. updateSpnDisplay()
  2. updateSpnDisplayCdnr()
  3. notifySpnDisplayUpdate()——先获取spn再set
  4. setCarrierName() 从manager到database

ServiceStateTracker.java

/frameworks/opt/telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java

    private void notifySpnDisplayUpdate(CarrierDisplayNameData data) {int subId = mPhone.getSubId();// Update ACTION_SERVICE_PROVIDERS_UPDATED if any value changesif (mSubId != subId|| data.shouldShowPlmn() != mCurShowPlmn|| data.shouldShowSpn() != mCurShowSpn|| !TextUtils.equals(data.getSpn(), mCurSpn)|| !TextUtils.equals(data.getDataSpn(), mCurDataSpn)|| !TextUtils.equals(data.getPlmn(), mCurPlmn)) {final String log = String.format("updateSpnDisplay: changed sending intent, "+ "rule=%d, showPlmn='%b', plmn='%s', showSpn='%b', spn='%s', "+ "dataSpn='%s', subId='%d'",getCarrierNameDisplayBitmask(mSS),data.shouldShowPlmn(),data.getPlmn(),data.shouldShowSpn(),data.getSpn(),data.getDataSpn(),subId);mCdnrLogs.log(log);if (DBG) log("updateSpnDisplay: " + log);Intent intent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);intent.putExtra(TelephonyManager.EXTRA_SHOW_SPN, data.shouldShowSpn());intent.putExtra(TelephonyManager.EXTRA_SPN, data.getSpn());intent.putExtra(TelephonyManager.EXTRA_DATA_SPN, data.getDataSpn());intent.putExtra(TelephonyManager.EXTRA_SHOW_PLMN, data.shouldShowPlmn());intent.putExtra(TelephonyManager.EXTRA_PLMN, data.getPlmn());SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());mPhone.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);if (SubscriptionManager.isValidSubscriptionId(subId)) {mSubscriptionManagerService.setCarrierName(subId, TextUtils.emptyIfNull(getCarrierName(data.shouldShowPlmn(), data.getPlmn(),data.shouldShowSpn(), data.getSpn())));}}mCurShowSpn = data.shouldShowSpn();mCurShowPlmn = data.shouldShowPlmn();mCurSpn = data.getSpn();mCurDataSpn = data.getDataSpn();mCurPlmn = data.getPlmn();}@NonNullprivate String getCarrierName(boolean showPlmn, String plmn, boolean showSpn, String spn) {String carrierName = "";if (showPlmn) {carrierName = plmn;if (showSpn) {// Need to show both plmn and spn if both are not same.if (!Objects.equals(spn, plmn)) {String separator = mPhone.getContext().getString(com.android.internal.R.string.kg_text_message_separator).toString();carrierName = new StringBuilder().append(carrierName).append(separator).append(spn).toString();}}} else if (showSpn) {carrierName = spn;}return carrierName;}private void updateSpnDisplayCdnr() {log("updateSpnDisplayCdnr+");CarrierDisplayNameData data = mCdnr.getCarrierDisplayNameData();notifySpnDisplayUpdate(data);log("updateSpnDisplayCdnr-");}@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)@VisibleForTestingpublic void updateSpnDisplay() {if (mCarrierConfig.getBoolean(CarrierConfigManager.KEY_ENABLE_CARRIER_DISPLAY_NAME_RESOLVER_BOOL)) {updateSpnDisplayCdnr();} else {updateSpnDisplayLegacy();}}

会触发SPN更新的场景(即调用updateSpnDisplay)
  •  BroadcastReceiveronReceive()
    • Intent.ACTION_LOCALE_CHANGED
    • TelephonyManager.ACTION_NETWORK_COUNTRY_CHANGED
  • handleMessage()
    • EVENT_ICC_CHANGED
    • EVENT_SIM_RECORDS_LOADED
    • EVENT_IMS_CAPABILITY_CHANGED
    • EVENT_RUIM_RECORDS_LOADED
  • setImsRegistrationState()
  • pollStateDone()

三、开发方案

  • T上只用subscriptions状态变化的时候会通过setPlmnSpn(其showPlmn和showSpn的逻辑再U上的getCarrierName接口中)更新名称内容,因此可以在设置spn的入口定制。
  • U上SST中包含很多carriername更新的场景,都是在notifySpnDisplayUpdate生效SPN更新 ,而此接口中都是通过get获取后再set设置信的,因此可以在getCarrierName获取的时候再定制内容。
【Android U】SST  notifySpnDisplayUpdate()
【Android U】SST  notifySpnDisplayUpdate()

(一)Android T 定制在 setPlmnSpn

Android T 上,可以在  SubscriptionController.java 中修改setPlmnSpn()接口内部逻辑,定制CarrierText(最终显示的字符串内容)。

    /*** Generate and set carrier text based on input parameters* @param showPlmn flag to indicate if plmn should be included in carrier text* @param plmn plmn to be included in carrier text* @param showSpn flag to indicate if spn should be included in carrier text* @param spn spn to be included in carrier text* @return true if carrier text is set, false otherwise*/@UnsupportedAppUsagepublic boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn,String spn) {synchronized (mLock) {int subId = getSubIdUsingPhoneId(slotIndex);//原生逻辑 if (showPlmn) {//...}//在最终更新运营商字符串之前实现定制carrierText = customizeCarrierText(carrierText, subId);setCarrierText(carrierText, subId); //原生逻辑return true;}}//客制化private String customizeCarrierText(String carrierText, int subId) {String customizeCarrierText= carrierText;TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);if (tm != NULL) {String simNumeric = tm.getSimOperatorNumeric(subId); //卡本身的mccmnc,区别于漫游的网络final ServiceState serviceState = tm.getServiceState();boolean isRoaming =  (serviceState != null)  ? serviceState.getRoaming() : false;//根据卡和网络状态定制举例if(serviceState != null && "46001".equals(simNumeric) && isInService(serviceState)){customizeCarrierText= "CCC";}}return customizeCarrierText;}

(二)Android U 定制在 getCarrierName

Android U上,可在获取SPN后定制内容,

    private void notifySpnDisplayUpdate(CarrierDisplayNameData data) {int subId = mPhone.getSubId();// Update ACTION_SERVICE_PROVIDERS_UPDATED if any value changesif (SubscriptionManager.isValidSubscriptionId(subId)) {mSubscriptionManagerService.setCarrierName(subId, TextUtils.emptyIfNull(getCarrierName(data.shouldShowPlmn(), data.getPlmn(),data.shouldShowSpn(), data.getSpn())));}}@NonNullprivate String getCarrierName(boolean showPlmn, String plmn, boolean showSpn, String spn) {String carrierName = "";if (showPlmn) {carrierName = plmn;if (showSpn) {// Need to show both plmn and spn if both are not same.if (!Objects.equals(spn, plmn)) {String separator = mPhone.getContext().getString(com.android.internal.R.string.kg_text_message_separator).toString();carrierName = new StringBuilder().append(carrierName).append(separator).append(spn).toString();}}} else if (showSpn) {carrierName = spn;}//定制carrierName = customizeCarrierText(carrierName, mPhone.getSubId());return carrierName;}private String customizeCarrierText(String carrierText, int subId) {String customizedCarrierText = carrierText;TelephonyManager tm = (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);tm = tm.createForSubscriptionId(subId);    //优化双卡逻辑if (tm != null && !tm.isWifiCallingAvailable()) { //不更新WFC场景int phoneId = mPhone.getPhoneId();String simNumeric = tm.getSimOperatorNumeric(subId);    //卡MCCMNCfinal ServiceState serviceState = tm.getServiceState();String operatorNumeric = (serviceState != null) ? serviceState.getOperatorNumeric() : null;    //注册网络的MCCMNC,如果是漫游,会跟simNumeric不同String gid1 = tm.getGroupIdLevel1(subId);//Avoid NullPointerExceptionboolean isRoaming = serviceState == null ? false : serviceState.getRoaming();Log.d("customizeCarrierText: operatorNumeric:" + operatorNumeric + ", simNumeric:" + simNumeric);if (TextUtils.isEmpty(simNumeric)) {simNumeric = mPhone.getOperatorNumeric();}if("46000".equals(operatorNumeric) && "310260".equals(simNumeric)) {//定制期望显示customizedCarrierText = "Visible";}Log.d("customizeCarrierText: simNumeric = " + simNumeric + ", operatorNumeric = " + operatorNumeric + ", customizedCarrierText = " + customizedCarrierText);}return customizedCarrierText;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/511242.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始学习Netty - 学习笔记 -Netty入门【协议设计和解析】

2.协议设计和解析 协议 在计算机中,协议是指一组规则和约定,用于在不同的计算机系统之间进行通信和数据交换。计算机协议定义了数据传输的格式、顺序、错误检测和纠正方法,以及参与通信的各个实体的角色和责任。计算机协议可以在各种不同的层…

【C++】STL容器string详解

string详解 一,STL简介1. 版本2. 六大组件 二,string类的使用1. string类的常用构造2. string类容量相关2.1 size和capacity接口2.2 reserve和resize 3. string类对象的访问和遍历,迭代器3.1 运算符重载[]3.2 string迭代器 4. string类对象的…

十九 超级数据查看器 讲解稿 分栏功能

十九 超级数据查看器 讲解稿 分栏功能 点击此处观看视频教程 讲解稿全文: 大家好,这讲介绍一下 ,超级数据查看器的分栏功能。 分栏功能设计的初衷是为了让用户同时同地查询两个表格的数据,方便比较,获得更清晰的查询结果 分栏功…

gin gorm学习笔记

代码仓库 https://gitee.com/zhupeng911/go-advanced.git https://gitee.com/zhupeng911/go-project.git 1. gin介绍 Gin 是使用纯 Golang 语言实现的 HTTP Web框架,Gin接口设计简洁,提供类似Martini的API,性能极高,现在被广泛使用…

BUUCTF:[MRCTF2020]ezmisc

题目地址:https://buuoj.cn/challenges#[MRCTF2020]ezmisc 下载附件打开是一张照片: 放到kali中发现crc校验错误,修改照片宽高: 保存即可发现flag flag为: flag{1ts_vEryyyyyy_ez!}

【代码】Python3|无GUI环境中使用Seaborn作图的学习路线及代码(阴影折线图)

我有个需求是需要画图,让GPT帮我生成了一下学习计划。 学习路线依照GPT的来的,使用的Prompt工具是https://github.com/JushBJJ/Mr.-Ranedeer-AI-Tutor。 文章目录 PrerequisiteMain Curriculum1.1 Seaborn介绍Seaborn基础保存图形为文件练习 1.2 单变量数…

适用于 Windows 的 5 款最佳免费数据恢复软件榜单

每个计算机用户都曾经历过数据丢失的情况。很容易错误地删除重要的文件和文件夹,当发生这种情况时,可能会导致不必要的心痛和压力。值得庆幸的是,可以恢复 Windows PC 上丢失的数据。在本文中,我们将分享您可以使用的五种最佳 Win…

SwiftUI之CoreData详解(一)

coreData 是一种数据持久化的方案,是对SQLite的一种封装。一说到这种桌面化的数据库,我就无比的怀念Foxbase|Foxpro, 多好的数据库产品,被微软扼杀了,相当年教大学生妹子们国家二级数据库时都是手把手教的,呃~~~&#…

Linux-信号3_sigaction、volatile与SIGCHLD

文章目录 前言一、sigaction__sighandler_t sa_handler;__sigset_t sa_mask; 二、volatile关键字三、SIGCHLD方法一方法二 前言 本章内容主要对之前的内容做一些补充。 一、sigaction #include <signal.h> int sigaction(int signum, const struct sigaction *act,struc…

微服务:Feign篇

1.什么是Feign Feign是一种声明式、模板化的HTTP客户端&#xff0c;可用于调用HTTP API实现微服务之间的远程服务调用。它的特点是使用少量的配置定义服务客户端接口&#xff0c;可以实现简单和可重用的RPC调用。 先来看我们以前利用RestTemplate发起远程调用的代码&#xff…

【leetcode热题】分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求&#xff0c;给这些孩子分发糖果&#xff1a; 每个孩子至少分配到 1 个糖果。相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果&#xff0c;计算并返回需要准备的…

使用Visual Studio 2022 创建lib和dll并使用

概述&#xff1a;对于一个经常写javaWeb的人来说,使用Visual Studio似乎没什么必要&#xff0c;但是对于使用ffi的人来说&#xff0c;使用c或c编译器&#xff0c;似乎是必不可少的&#xff0c;下面我将讲述如何用Visual Studio 2022 来创建lib和dll&#xff0c;并使用。 静态库…