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

news2024/10/22 8:00:27

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

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}
         */
        @Override
        public 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/SPN
            if (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 List
                refreshCachedActiveSubscriptionInfoList();

                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 changes
        if (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();
    }

    @NonNull
    private 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)
    @VisibleForTesting
    public 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
     */
    @UnsupportedAppUsage
    public 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 changes

            if (SubscriptionManager.isValidSubscriptionId(subId)) {
                mSubscriptionManagerService.setCarrierName(subId, TextUtils.emptyIfNull(
                        getCarrierName(data.shouldShowPlmn(), data.getPlmn(),
                                data.shouldShowSpn(), data.getSpn())));
            }
    }


    @NonNull
    private 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);    //卡MCCMNC
            final ServiceState serviceState = tm.getServiceState();
            String operatorNumeric = (serviceState != null) ? serviceState.getOperatorNumeric() : null;    //注册网络的MCCMNC,如果是漫游,会跟simNumeric不同
            String gid1 = tm.getGroupIdLevel1(subId);
            //Avoid NullPointerException
            boolean 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.coloradmin.cn/o/1498759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java on VS Code 2月更新|创建 Maven 模块支持,项目管理体验优化!

作者:Nick Zhu - Senior Program Manager, Developer Division At Microsoft 排版:Alan Wang 大家好,欢迎来到2024年2月的 Visual Studio Code Java 更新!在本篇博客中,我们将分享项目管理体验的改进以及 Maven 多模块…

【MySQL | 第三篇】MySQL索引及两种索引分类方法总结

文章目录 3.MySQL索引及两种索引分类方法3.1索引的概念3.1.1相关定义3.1.2查询例子 3.2索引的底层3.2.1二叉树(1)满二叉树(2)完全二叉树(3)二叉查找树(4)二叉平衡树(AVL&…

uniapp——nextTick(vue3)数据更新完之后加载

说明 将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它。 代码 <view class"tabBox"><scroll-view scroll-x"true" :scroll-with-animation"true"><view class"box"><…

利用“定时执行专家”循环执行BAT、VBS、Python脚本——含参数指定功能

目录 一、软件概述 二、VBS脚本执行设置 三、触发器设置 四、功能亮点 五、总结 在自动化办公和日常计算机任务管理中&#xff0c;定时执行脚本是一项非常重要的功能。今天&#xff0c;我将为大家带来一款名为“定时执行专家”的软件的评测&#xff0c;特别是其定时执行VB…

leetCode刷题 5.最长回文子串

目录 1. 思路 2. 解题方法 3. 复杂度 4. Code 题目&#xff1a; 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#x…

3.7 day2 Free RTOS

使用ADC采样光敏电阻数值&#xff0c;如何根据这个数值调节LED灯亮度。2.总结DMA空闲中断接收数据的使用方法 while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */adc_value HAL_ADC_GetValue(&hadc);TIM3->CCR3 adc_value * 999 / 4095;printf("%d …

Docker网络+原理+link+自定义网络

目录 一、理解Docker网络 1.1 运行tomcat容器 1.2 查看容器内部网络地址 1.3 测试连通性 二、原理 2.1 查看网卡信息 2.2 再启动一个容器测试网卡 2.3 测试tomcat01 和tomcat02是否可以ping通 2.4 只要删除容器,对应网桥一对就没了 2.5 结论 三、--link 3.…

探索考古文字场景,基于YOLOv7【tiny/l/x】不同系列参数模型开发构建文本考古场景下的甲骨文字符图像检测识别系统

甲骨文是一种非常历史悠久的古老文字&#xff0c;在前面我们基本上很少有涉及这块的内容&#xff0c;最近正好在做文字相关的项目开发研究&#xff0c;就想着基于甲骨文的场景来开发对应的检测识别系统&#xff0c;首先看下实例效果&#xff1a; YOLOv7是 YOLO 系列最新推出的Y…

dubbo3适配springboot2.7.3

版本详细 <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.0.3</version> </dependency><parent><groupId>org.springframework.boot</groupId><artifactId&…

css 用flex做成田字型

哈喽&#xff0c;各位小伙伴&#xff01;今天给大家来css控制div完成田字型样式&#xff0c;来&#xff0c;看看下面的效果图&#xff1a; 一看就知道你们想要代码了&#xff0c;不急。代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head>&…

c++ 11 新特性 不同数据类型之间转换函数之reinterpret_cast

一.不同数据类型之间转换函数reinterpret_cast介绍 reinterpret_cast是C中的一种类型转换操作符&#xff0c;用于执行低级别的位模式转换。具体来说&#xff0c;reinterpret_cast可以实现以下功能&#xff1a; 指针和整数之间的转换&#xff1a;这种转换通常用于在指针中存储额…

【考研数学】武忠祥各阶段用书搭配+学习包

25考研数学全流程规划&#xff01;别等到二战了才知道这样学 本人属于基础很差相当于是零基础的考研党&#xff0c;经过一年备考成功上岸 中间花费了很多时间在考研数学备考信息检索上&#xff0c;写下这篇希望能帮助基础不好的学弟学妹们多节约一些时间复习&#xff01; 25…

容器+虚拟机双引擎,ZStack Edge云原生超融合打通业务最后一公里

企业数字化转型的焦点正在发生变化&#xff0c;云基础设施由资源到应用&#xff0c;数据中心从核心到边缘。面向云原生趋势&#xff0c;围绕应用升级&#xff0c;新一代超融合产品——云原生超融合应运而生。 云原生与边缘计算趋势催生云原生超融合 当前&#xff0c;企业客户…

【CSP试题回顾】201409-3-字符串匹配

CSP-201409-3-字符串匹配 关键点&#xff1a;<string>库函数的使用 length() 或 size(): 返回字符串的长度。 empty(): 检查字符串是否为空。 append() 或 : 向字符串的末尾添加字符或另一个字符串。 insert()在字符串的指定位置插入另一个字符串或字符。 std::str…

Docker-容器网络互联

目录 1 前言 2 常用指令 3 实现容器互联 3.1 自定义网络 3.2 让容器连接创建的网络 3.2.1 容器创建后连接网络 3.2.2 容器创建时连接网络 3.3 尝试使用容器名访问(测试) 1 前言 在默认情况下&#xff0c;docker中的容器都是连接到一个虚拟的网桥上的&#xff0c;这为独…

web服务之虚拟主机功能

华子目录 概述基于IP地址的虚拟原理实验 基于不同端口号的虚拟主机原理实验 基于域名的虚拟主机原理域名解析实验 概述 如果每台运行 Linux 系统的服务器上只能运行一个网站&#xff0c;那么人气低、流量小的草根站长就要被迫承担着高昂的服务器租赁费用了&#xff0c;这显然也…

使用Python制作自己的wheel文件

平时自己利用Python制作一个个小工具后想分享给别人&#xff0c;但又嫌分享一堆项目代码很麻烦&#xff0c;那么你可以考虑将自己的项目打包成一个wheel文件&#xff0c;别人拿到文件后只需pip install安装即可使用&#xff0c;非常方便。 在上一篇博文中&#xff0c;利用nvid…

01、python_爬虫的相关概念

一、什么是爬虫&#xff1f; 爬虫是网络爬虫的简称&#xff0c;指的是一种自动化程序&#xff0c;用于在互联网上抓取信息。爬虫的核心工作包括爬取网页、解析数据和存储数据。 通俗来说就是&#xff1a;通过一个程序&#xff0c;根据url(http://taobao.com)进行爬取网页&…

第九届多媒体系统和信号处理国际会议(ICMSSP 2024)即将召开!

2024年第九届多媒体系统和信号处理国际会议&#xff08;ICMSSP 2024&#xff09;将在5月24-26日在泰国曼谷举行。ICMSSP 2024旨在展示多媒体系统和信号处理等相关主题的最新研究和成果&#xff0c;为不同领域的专家代表提供了面对面交流新想法以及应用经验的机会&#xff0c;建…

从零搭建React18.2+ReactRoute6.22+TS5+RTK2.2搭配antd5+antd-style书写All in Js完整体验项目规范

1. 使用CRA创建项目 全局设置npm淘宝镜像源 npm config set registry https://registry.npmmirror.com -g使用最新版create-react-app初始化项目结构 npx create-react-app custom-template --template typescript初始化项目之后在package.json文件中配置使用node>18.0.0…