rust中简单粗暴地使用global变量的方法

就是用unsafe
在Rust In Action中给出了两段代码

use rand::{random};
   
  static mut ERROR: isize = 0;
   
  struct File;
   
  #[allow(unused_variables)]
fn read(f: &File, save_to: &mut Vec<u8>) -> usize {
      if random() && random() && random() {
         unsafe {
             ERROR = 1;
         }
     }
     0
 }
  
 #[allow(unused_mut)]
 fn main() {
     let mut f = File;
     let mut buffer = vec![];
  
     read(&f, &mut buffer);
     unsafe {
         if ERROR != 0 {
             panic!("An error has occurred!")
         }
     }
 }

第二个例子,停机标志

#![cfg(not(windows))]

extern crate libc;

use std::time;
use std::thread::{sleep};
use libc::{SIGTERM, SIGUSR1};

static mut SHUT_DOWN: bool = false;

fn main() {
    register_signal_handlers();

    let delay = time::Duration::from_secs(1);

    for i in 1_usize.. {
        println!("{}", i);
        unsafe {
            if SHUT_DOWN {
                println!("*");
                return;
            }
        }

        sleep(delay);

        let signal = if i > 2 {
            SIGTERM
        } else {
            SIGUSR1
        };
        unsafe {
            libc::raise(signal);
        }
    }
    unreachable!();
}

fn register_signal_handlers() {
    unsafe {
        libc::signal(SIGTERM, handle_sigterm as usize);
        libc::signal(SIGUSR1, handle_sigusr1 as usize);
    }
}

#[allow(dead_code)]
fn handle_sigterm(_signal: i32) {
    register_signal_handlers();

    println!("SIGTERM");

    unsafe {
        SHUT_DOWN = true;
    }
}

#[allow(dead_code)]
fn handle_sigusr1(_signal: i32) {
    register_signal_handlers();

    println!("SIGUSR1");
}

如果不想用 unsafe代码,可以用 lazy_static! 宏

use lazy_static::lazy_static; 
use regex::Regex; 

lazy_static! {
    static ref RE: Regex = Regex::new(r"hello (\w+)!").unwrap();
}

fn main() {
    let text = "hello bob!\nhello sue!\nhello world!\n";
    for cap in RE.captures_iter(text) {
        println!("your name is: {}", &cap[1]);
    }
}

如果这个全局变量可变, 还需要加上Mutex(单个修改者)或者RwLock(多个修改者)

另外一个方法是 https://github.com/matklad/once_cell 据说正在提议加入std库中

Android Bluetooth API解释

服务(Service)是一系列特征(Characteristic)。例如,您可能拥有名为“心率监测器”的服务,其中包括“心率测量”等特征。
特征(Characteristic 包含一个值(value) 和 0 至多个描述特征值的描述符(descriptor)
描述符(descriptor)可指定人类可读的描述、特征值的可接受范围或特定于特征值的度量单位。

服务和特征的都由UUID标识。

https://github.com/android/connectivity-samples/blob/main/BluetoothLeGatt/Application/src/main/java/com/example/android/bluetoothlegatt/BluetoothLeService.java

要连接到远程外围设备(提供蓝牙服务的设备),请创建 BluetoothGattCallback 并调用 BluetoothDevice#connectGatt 以获取此类的实例。 可以使用蓝牙设备发现或 BLE 扫描过程发现支持 GATT 的设备。

bluetoothGatt = device.connectGatt(this, false, gattCallback);

connectGatt 将连接到由 BLE 设备托管的 GATT 服务器,并返回 BluetoothGatt 实例,然后您可使用该实例执行 GATT 客户端操作。调用方(Android 应用)是 GATT 客户端。BluetoothGattCallback 用于向客户端传递结果(例如连接状态),以及任何进一步的 GATT 客户端操作。

BluetoothGattCallback可以override几个方法:
1)onConnectionStateChange 连接接状态改变 (连接还是断开了)
2) onServicesDiscovered 在远程设备上发现了Gatt服务
3) onCharacteristicRead 特征值已经读取 (报告 一个 特征读操作的结果)
4) onCharacteristicChanged 特征值 改变了
5) onCharacteristicWrite 特征已写 (指出 特征写操作的结果), 是对 writeCharacteristic(BluetoothGattCharacteristic, byte[], int)操作 的回应。 这时候,可以 characteristic.getValue()来获取远程设备报告 他写入的值,是否与你 调用 writeCharacteristic时写入的值,是否一致
6) onMtuChanged MTU改变了
7) onDescriptorRead 报告 描述符 读操作的结果
8) onDescriptorWrite 指出 描述符 写操作的结果, 是对 writeDescriptor的响应. 写描述符,主要是对特征的功能进行配置,比如特征值改变后,要不要主动通知 客户端(中心设备)
9) onReadRemoteRssi 报告远程设备的信号强度, 是对 readRemoteRssi 的响应
10)onServiceChanged 需要重新同步远程设备

顺序:
onConnectionStateChange 已连接 请求修改MTU
onMtuChanged MTU已修改 请求发现远程设备上的服务
onServicesDiscovered 服务已经发现 (1)枚举服务和其中的特征。 或者 直接获取特定UUID的特征
(2)对特征进行相关设置,比如是否需要通知,写类型(WRITE_TYPE_NO_RESPONSE是否需要写响应)
(3) 读写特征
onCharacteristicRead 读数据已经获得 如果上一步,发出 读操作,当特征值读到时,会出发此回调。

操作:
读—-
如果客户端(中心设备) 要读服务端(外围设备)的 特征的值,直接调用
readCharacteristic, 然后在 onCharacteristicRead 获取读的的值
写—–
如果客户端(中心设备) 要往 服务端的某个特征写,并且想知道回应值,
先打开通知 setCharacteristicNotification, 设置写类型 setWriteType, 设置值 setValue, 最后 writeCharacteristic
写完之后,在 onCharacteristicChanged 获取响应数据


服务端,外围设备开启 GattServer
openGattServer(Context context, BluetoothGattServerCallback callback)

onConnectionStateChange
onCharacteristicReadRequest
onCharacteristicWriteRequest
onDescriptorReadRequest
onDescriptorWriteRequest
onMtuChanged
onNotificationSent
onServiceAdded

浅谈SIM卡虚拟化及云服务的发展趋势

作者: 龚智辉 (优克云联CTO)

来源: 黑马汇 https://www.sohu.com/a/368011397_574724

记得1997年我刚参加工作得到第一部摩托罗拉手机的时候,第一件事情就是到移动营业厅去开一张SIM卡,有了这张卡才可以打电话、发短信。一张SIM卡可以使用好多好多年,感觉只要不丢就可以用到天荒地老;换手机的时候只需要把SIM卡拔出来放入新手机,就可以继续使用原来的电话号码。还能通过SIM卡存储或转存通信录,最初的时候为了方便查找联系人,中文姓名前还会加上首字母,尽管已过去了20多年,一些老朋友在我的手机通信录里至今还是以这样的形式出现。

我曾经在一个运营信息交换电子商务平台的公司工作,为券商提供证券交易的综合服务平台,其中有个功能就是支持手机炒股,我们使用了STK技术。手机只要换上STK SIM卡,就可以使用手机菜单进行股票的查询、买、卖、撤单等各种操作。现在看来就是最原始的移动互联网应用之一。

今天,手机SIM卡作为开启每个人移动通信服务以及连接互联网大门的钥匙,其重要性、安全性、便利性一直备受关注。

SIM卡从1991年在芬兰移动网络运营商率先商用以来,到现在已经有将近三十年的历史。对于普通用户来说,最直观的感觉就是SIM卡越做越小——苹果公司一直在积极地推动SIM卡的小型化,2010年在iPhone 4上首次使用微型SIM卡,2012年在iPhone 5上开始使用纳米SIM卡。

其实,无论是全尺寸(Full-size,1FF,85.6×53.98)、迷你卡(Mini SIM,2FF, 25×15)、微型卡(Micro-SIM,3FF, 15×12)还是纳米卡(Nano-SIM,4FF, 12.3×8.8),SIM卡的物理结构都基本相同,只是封装方式上的区别。

随着苹果公司在iPad Air 2上使用Apple SIM,软卡和eSIM这些名称也陆续进入大众的视野,然而这些 SIM卡已经是只闻其名不见其形了。

未来,SIM卡将以怎样的形态出现在我们的生活中呢?本文就试着从SIM的起源开始,简单探讨一下SIM技术的演进发展历程和SIM卡虚拟化及云服务的未来。

实体SIM卡

SIM卡的名称来源于英文Subscriber Identity Module或Subscriber Identification Module的缩写,是为了安全地存储国际移动用户身份(IMSI,International Mobile Subscriber Identity)、用户号码及其关联密钥以及其他移动网络连接信息的一种集成电路,用于在移动通信设备(包括:移动电话、平板电脑、移动电脑等)连接移动通信网络时识别用户身份及鉴权。

SIM卡标准最初由欧洲电信标准协会在规范中制定,编号为TS 11.11。本规范描述了SIM卡的物理和逻辑行为;随着UMTS的发展,规范工作被部分转移到3GPP。3GPP现在负责诸如SIM(TS 51.011 [4])和USIM(TS 31.102 [5])和ETSI等应用程序的进一步开发,以便物理卡UICC进一步发展。

SIM卡的物理接口使用智能卡标准接口ISO/IEC 7816,并一直沿用至今。核心是一个集成电路芯片,使用不同大小的封装方式,形态基本保持相同(见图 2)。

GSM时代,SIM卡是软硬件一体的。随着第三代移动通信UMTS的出现,SIM卡的软硬件则逐步分离。目前,SIM的实际意义就是应用软件,而硬件部分是UICC,也就是芯片的物理本体,集成电路芯片。软件部分对于UMTS来说就是引入的USIM(通用订户身份模块);SIM卡中的软件包括底层操作系统和上层各种软件,包括操作系统、Java虚拟机、Toolkit、OTA等。3GPP的TS11.14规范了SIM应用工具包(STK)技术,允许在SIM卡中加载应用。这样在SIM卡上进行开发和配置就更加简单方便。本文引言中提到的2000年左右开发的手机炒股,还有当时招商银行与中国移动合作开发的手机银行,以及影响了一代人、救活了一个伟大互联网公司的手机QQ,都是使用了STK这个技术。

随着物联网的发展,越来越多的非通信设备也需要接入移动网络,因此对嵌入式SIM卡(eSIM)的需求不断增加,GSMA(全球移动通信联盟)推出了RSP(远程SIM卡配置)规范,各种设备都可以预先嵌入式安装eSIM卡,当用户启动设备时,再按需开通和下载不同运营商的SIM卡用户参数和相关密钥,实现在线开通SIM卡。苹果公司在iPad、iPhone以及Apple Watch所使用的AppleSIM,就是使用eSIM技术。

虚拟SIM卡

SIM卡技术的引入,将通信设备的认证鉴权集中到了一个小小的SIM卡。不仅方便了移动设备的独立发展,也方便了SIM技术自身的不断发展。但是,随着人们在现实生活中对移动通信技术的越来越广泛应用,特别是移动互联网技术的发展,最终用户在各个方面的使用越来越多,SIM卡在使用过程中的一些限制也越来越突出:

l 机卡绑定,即移动通信设备与指定的SIM卡绑定,如需更换运营商,则需要连同手机一起更换。

l 通常情况下,一张SIM卡只能对应一个运营商的网络,用户如需转网,原有SIM卡无法重用,必须重新开卡。

l 跨国旅行时如需使用当地SIM卡,用户必须将原来的SIM卡移除,无法同时使用。即使存在多卡多待的手机,可以容纳的SIM卡数量也很有限,绝大多数手机仅支持双卡。

为了解决这些问题,特别是为了解决漫游地上网问题,各种突破SIM卡限制的技术应运而生。其中最常使用的技术就是SIM卡虚拟化技术;其可行性来源于以下几种技术基础:

1)卡规范上通过USIM技术,将软件部分和硬件本体分离,为SIM卡虚拟化提供了最基本的条件。

2)SAP协议:SIM卡访问协议(SIM Access Profile),支持远程对SIM卡进行操作。SAP协议最早应用在车载蓝牙电话模块。车载模块可以通过蓝牙使用用户手机中的SIM使用电话功能。这样可以实现一个通信设备使用不在设备内的SIM卡。

3)虚拟机技术:SIM的软件部分原本就是操作系统和其中的软件及相关模块,使用虚拟机技术,非常容易在虚拟机管理系统中虚拟出一个SIM卡的软件系统来。

4)RSP规范:RSP是eSIM的供卡规范,支持M2M方式和Consumer方式。其中Consumer方式,是通过一般网络连接,通过安全通信协议,将SIM订户信息及相关安全认证信息下载到eSIM中,实现SIM卡的下载。

下面简单介绍一下几种常见的SIM卡虚拟化技术:

1. 接口延伸法

将SIM卡的接口通过物理电路延伸到另外一个模块。在新的模块中,承接原有的SIM卡,同时支持插入其他SIM卡,或者通过软件模块,获得从其他地方下载的SIM卡。扩展出来的模块可以自主开发,进一步扩展其他功能。

2. 贴片扩展法

在SIM卡上,重新贴上一个很薄的芯片,实现原来SIM卡的二次扩展。由于通过另外的电路嵌入到已有的SIM卡系统中,所以,可以在贴片上额外增加其他功能,比如电子钱包。

3. 近距离无线延展法

通过蓝牙本地连接,利用SAP协议,或其他特有协议,通信设备可以非常方便地使用扩展设备所提供的SIM卡。SAP协议(SIM Access Profile)由国际蓝牙组织(Bluetooth.org)2000年开始制定,2005年出1.0正式版本,2008年出1.1正式版本,这个协议是移动设备与SIM卡分离的关键。因为,既然可以本地无线,也就可以通过移动网和互联网无限延伸了。而且,这个通过蓝牙供卡的设备,还可以通过其他通信手段从网络上获得其他的SIM卡。

4. 网络远程延展法

通过互联网,连接部署在远程的SIM卡,非常方便地使用位于全球网络可以触达的任何地方的SIM卡。这个技术来源于远程SIM技术,在2000年代中期,大量应用在移动通信公司的漫游SIM的远程拨测设备(RTU)(参考2007年11月期《广东科技》的“SIM卡池及异地调度技术的实现与应用”)。但这种技术在普通用户的移动设备上应用则是2011年以后,优克联、斯凯荣、iQsim、Aicent都利用这个技术来解决用户移动漫游问题,将SIM卡异地调度技术应用到普通的终端设备上,通过远程连接来使用云端卡池中的SIM卡。

5. 虚拟机扩展法

前面几种方法都是将SIM卡在物理空间上的延伸,使用远程或另外的SIM卡来替换通信设备中的SIM卡。但实际的使用过程中从实用性考虑,还是需要在本地有一个SIM卡比较好。

而本方法在通信终端中不需要SIM卡的物理本体,直接将SIM软件使用虚拟机进行仿真模拟,具备SIM所有的接口功能,通过软件接口(比如SAP协议)提供物理卡的所有功能。只有在需要使用到只有物理卡中才有的信息(比如密钥)时,才调用远程SIM卡。这是一种更加高效实用的方法。

我们知道,SIM卡识别身份最重要的就是SIM卡硬件中的密钥。尽管密钥是一串数字,但SIM卡在制作的时候,将这个密钥和软件捆绑在一起,确保更高的安全级别。其实,软件运行在SIM卡上和运行在任何计算机模拟的计算空间都是一样的,关键还是在密钥。

按照密钥所存放位置的不同,又衍生出来以下三种技术:

1) eSIM技术:GSMA推荐技术,基本维持了物理实体SIM卡所具备的安全级别,通过RSP协议,将所有SIM信息安全地从远端下载存储到eSIM这个物理设备中。也就是将以前在SIM卡工厂写卡的一些动作,推迟到用户按需订购后再写卡。

2) 软卡技术:这个技术就是将SIM卡中所有的配置以及密钥,都以软件的形式在设备的操作系统中模拟出一个虚拟的SIM卡。

3) 远程虚拟卡技术:这个技术是将卡在用户设备端使用虚拟机的方式进行模式,当需要使用SIM卡的时候,需要使用SAP或其他相似技术,远程访问部署在远端的物理SIM来完成鉴权。这个技术与软卡的不同点在验证密钥还在物理卡中。

基于虚拟SIM卡技术衍生的漫游解决方案

大部分技术的发展都是为了解决实际应用场景中的问题而产生的,前面所描述的SIM技术的不断突破,很多都是受用户漫游问题所驱动的。

为了解决漫游地高额话费以及漫游地通信不方便的问题,移动电话用户一直在期望有更好的办法解决移动漫游问题,方便出行时连接互联网。特别是2005年以来移动互联网的发展,更加促进了异地使用非漫游的本地卡上网的需要。

实际生活中,用户解决漫游问题常常使用以下几种方法:

l 购买目的地SIM卡使用:到达目的地后购买并更换SIM卡使用,或者插入到双卡手机,总之需要占用一个卡槽;

l 插卡MiFi(随身无线路由器):使用可以插卡的上网设备(俗称插卡猫),购买目的地的SIM卡,并插入到设备中使用。设备提供Wifi热点信号,供移动设备连接使用。设备连接的网络和具备的连接能力,依赖于所购买的SIM卡。

l 免插卡MiFi:无需插卡,只要购买所需的流量包,就可以使用上网功能。流量包可能是全球多个国家通用的,使用方便,且价格上普遍比插卡MiFi更便宜。

l eSIM技术:通过其他网络(如WiFi)在线购买目的地运营商提供的eSIM卡进行上网。但目前该技术并不受大多数运营商欢迎,因此在市场上普遍供应不足。从苹果公司的Apple SIM推进情况就可以得到印证。Apple Watch开通上网卡在中国一波三折,更别说其他品牌的移动电话了。所以说,eSIM技术的推广运营商的供应是关键。

l 软卡技术:这也是目前市面上比较常用的技术,如华为天际通、红茶移动都是使用这一技术。

l 手机内嵌全球上网技术:使用云端卡池,用户根据自身需要选择购买对应流量套餐并激活使用。优克联在2018年巴塞罗那移动大会上推出世界手机S1就是使用了这个技术,通过GlocalMe Inside方案将远程虚拟卡技术整合到手机的基带中。目前小米部分型号手机支持该功能,估计未来还会有更多品牌和型号的手机陆续支持。

从以上分析可以看出:

l eSIM技术是官方推出的技术,但运营商不热心,叫好不叫座也没有办法。

l 软卡技术,由于存在安全风险,GSMA不建议使用,运营商及相关厂商投鼠忌器,不敢大规模推广。

l 免插卡MiFi是相对性能优异且安全的技术,但需要携带额外的设备,造成了一定负担。一些免插卡MiFi兼顾充电宝功能,用户接受度更高。

l 直接将免插卡MiFi的技术植入手机,是更符合用户期望的方式,不影响用户原有SIM卡的使用,性能和安全性也最有保障。

SIM卡云服务解决方案

移动通信的特点使得不同运营商提供的服务必然是存在差异的,以中国为例,三大运营商的覆盖就各有特点,例如:中国移动基站数量最多,大部分区域的信号覆盖就最好;但是中国移动的用户数量也最多,在高峰期网络拥塞的情况也最严重,用户即使处于良好的覆盖条件下也难以获得满意的网速,联通和电信在这时候却有更好的体验;另外,价格也是用户考虑的一个重要指标,特别是在出国漫游使用时。因此,如果用户只能使用手机中原有的一张SIM卡,那么就很可能遭遇到:有时候覆盖信号差;有时候网络拥塞上网速度慢;有时候需要支付高额的漫游费用;有时候套餐流量用尽只能到处蹭免费Wi-Fi……

应对这些问题,最根本的解决方案是:允许用户更方便的更换SIM卡,从而令用户可以根据自己的需求,随时随地自由选择提供服务的运营商,以及所需的网络服务套餐。

而实现这一解决方案,上述软卡、eSIM、远程SIM卡等技术都是可行的,但是考虑到提供全球范围上百个国家的网络服务,SIM卡供应的问题也必须通盘考虑。

针对SIM卡的供应来源,我们针对上述虚拟SIM卡技术展开逐一分析。

l 软卡:由于密钥可以随着软件拷贝,在安全上存在着使用方面的风险,完全依赖于对卡的使用方的信任。

l eSIM卡:可以解决软卡的安全上的担忧,但是,由于运营商在商业上希望对用户锁定来进行自我保护,不想把用户选择的权利下放到手机,目前eSIM的供应量有限,因此,目前eSIM的应用主要集中在物联网领域。

l 远程SIM卡:由于使用普通的物理卡,SIM卡来源不存在特殊性,因此可以在全球范围内获得充足的供卡。

从安全性和SIM卡供应两方面来看,远程SIM卡技术是比较好的,另外,对便利性、实施成本、流量成本等其他方面一并考虑后,下表是对这三种技术的对比分析。

从以上分析来看,目前远程SIM卡还是最合适的供卡方式,其次是软卡,最后是eSIM卡。随着物联网的普及,在物联网领域eSIM会有比较好的期望,但还是需要观望。

显然,普通用户并不会关心使用具体什么技术,用户所需要的只是一个自己满意的漫游及本地上网解决方案——无论何时何地,购买了设备及服务套餐就可以上网。而作为对应的服务提供商,则要求解决方案具备以下特点:

l 具有连接全球移动网络所覆盖的所有区域的SIM卡卡池(覆盖能力)

l 具有动态感知移动终端所在区域的网络并动态换卡的能力(联网能力)

l 具有为用户提供连接服务的按需服务的能力(可自由选择)

l 具有根据用户使用需求动态扩充性能的能力(容量扩充能力)

l 具有灵活的计费能力,支撑用户的购买需求和透明消费的需求(可计费)

l 具有优质的网络连接服务质量(最佳连接)

这些特点正好反映了云计算的弹性服务、资源池化、按需服务、服务可计费、泛在接入的五大特点,因此我们将满足以上特点的解决方案称为SIM卡云服务解决方案,简称云卡。通过将各种SIM卡资源池化,然后将连接资源的能力虚拟化之后,实现用户根据需要弹性购买。随着5G技术的发展,连接能力的虚拟化会更加多维度,给用户更多的选择空间。云卡正好借鉴现代云计算技术来实现SIM卡所提供的连接服务的云化。

云卡云服务系统包括以下基本要素:

1) 云卡终端:

l 同时支持各种虚拟卡技术,包括eSIM卡、远程物理卡、软卡,并具备良好的扩展能力,支持未来各种各样的SIM卡;

l 具备根据网络环境动态换卡的能力;

l 具有高度安全的SIM卡保护机制,确保云卡在网络连接安全性、计费准确性;

l 具有良好的质量监控与保障机制;

2) 云卡平台

l 具有容纳全球各种各样SIM卡的管理能力,为云卡终端按需分配SIM卡资源;

l 对云卡终端连接情况进行监控与分析,保证连接质量,可按用户等级提供服务;

l 服务可量化,可计费。

l 不限于原始运营商提供的服务能力,为用户提供更方便、灵活、个性化、场景化的贴心移动连接服务。

云卡云服务的现状与展望

从前面分析来看,SIM卡云服务就是移动用户最终所希望使用的技术。实际上,业界在该领域已经有很多厂商在辛勤耕耘,并正在为广大用户提供服务。

l 华为天际通:华为的多款手机已支持使用软SIM技术的天际通功能,从其官网介绍来看,已支持全球80+国家的精品网络覆盖。然而,可惜的是中国却不在覆盖范围内。从这点来看说,还不是的令人完全满意的云卡平台。

(来源:https://consumer.huawei.com/cn/mobileservices/skytone/)

l 环球漫游:16年出入境通信服务,全球出行移动连接服务提供商,网络覆盖超过210个国家,通过随身WiFi提供服务,并为全球商户提供广告投放服务。

(来源:https://www.vipwifi.com/)

l 优克联:覆盖全球140+国家地区。提供漫游超人租赁服务(免插卡MiFi)和GlocalMe终端产品(免插卡MiFi和自带流量世界手机);并提供GlocalMe Inside手机嵌入方案,通过云服务平台为合作伙伴提供PaaS和SaaS云卡方案。

(来源:https://www.ucloudlink.com/html/service-local-mobile-data/)

l 漫游宝(Skyroam):提供无需插卡的随身WiFi,覆盖全球130+国家,并提供VPN服务,覆盖16个国家。

(来源:https://www.skyroam.com/wifi-pricing)

l 小米全球上网:通过第三方提供全球上网服务,有的手机通过红茶移动提供,有的通过优克联的GlocalMe Inside技术提供,使用方式与华为天际通相同,网络覆盖与技术提供商有关。

l 红茶移动:使用eSIM技术,提供CaaS服务平台,采用eSIM技术,为主要手机品牌提供SIM卡服务,无自主终端,网络覆盖100+国家和地区。

(来源:https://www.redteamobile.com/)

目前看来,华为、小米等厂商主要业务在手机,上网也主要为手机服务,云卡服务平台不是其主要业务方向。环球漫游、漫游宝通过自身MiFi产品提供上网服务,以自主运营、直销服务的方式对外,无法形成服务全球的云卡服务平台。红茶移动基于eSIM技术,通过CaaS提供基础卡服务,在eSIM还没有供应量和承接终端的情况下,多数情况下采用软卡技术提供,服务比较专注,对最终用户的体验和触达还不够。尽管通过手机厂商,网上公布有2.5亿以上的装机,但实际使用人数不多。优克联提供的云卡平台,广泛支持远程物理卡、eSIM卡和软卡,自营的漫游超人Mifi租赁业务在国内占有率最大,以 PaaS服务和SaaS服务方式,面向最终用户和合作伙伴,构建全生态链的云卡服务,是值得期待的为移动用户提供贴心管家式云卡服务平台的公司。

云卡云服务平台的未来会怎么样?互联网的游戏规则就是用脚投票,最终用户说了算,谁能将用户服务好,谁就是赢到最后的玩家。但无论如何,云卡云服务平台就是最终用户最喜欢的平台,有用户需求在,云卡云服务平台一起会发展起来的。(署名:龚智辉)

android studio中配置rust开发native lib

0. 首先配置好rust开发环境:
rust编译android命令行程序

1. 进到项目的 app/src/main 目录, 创建一个rust目录
然后进到 app/src/main/rust, 执行

cargo new --lib softsim

2.在项目根目录下的 build.gradle 里添加
id “org.mozilla.rust-android-gradle.rust-android” version “0.9.2”
例如

plugins {
    id 'com.android.application' version '7.1.2' apply false
    id 'com.android.library' version '7.1.2' apply false
    id "org.mozilla.rust-android-gradle.rust-android" version "0.9.2"
}

3. 在 app/build.gradle 里添加

android { 
... 
}

cargo {
    module  = "./src/main/rust/softsim"       // Or whatever directory contains your Cargo.toml
    libname = "softsim"                       // Or whatever matches Cargo.toml's [package] name.
    targets = ["arm", "arm64"]                // See bellow for a longer list of options
    profile = 'release'                      // default debug
    pythonCommand = "python3"
}

dependencies {
   .....
}

tasks.whenTaskAdded { task ->
    if ((task.name == 'javaPreCompileDebug' || task.name == 'javaPreCompileRelease')) {
        task.dependsOn 'cargoBuild'
    }
}

4. settings.gradle

pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
    }
}

android设置默认的sim卡

Android 4.4

ConnectivityManager mConnService;
TelephonyManager.setDefaultDataPhoneId(context, phoneId);
mConnService.setMobileDataEnabledByPhoneId(phoneId, true);

Android 7之后

private static void setDefaultDataSubId(Context context, int subId) {
       SubscriptionManager.from(context).setDefaultDataSubId(subId);
}


SubscriptionManager.from(context).setDefaultSmsSubId(subId);



SubscriptionManager.getDefaultDataSubscriptionId()



mSubscriptionManager.addOnSubscriptionsChangedListener(this.mOnSubscriptionsChangeListener);



https://android.googlesource.com/platform/frameworks/base/+/master/telephony/java/android/telephony/SubscriptionManager.java

/**
     * Set the subscription which will be used by default for data, with the subscription which
     * the supplied subscription ID corresponds to; or throw a RuntimeException if the supplied
     * subscription ID is not usable (check with {@link #isUsableSubscriptionId(int)}).
     *
     * @param subscriptionId the supplied subscription ID
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
    public void setDefaultDataSubId(int subscriptionId) {
        if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
        try {
            ISub iSub = TelephonyManager.getSubscriptionService();
            if (iSub != null) {
                iSub.setDefaultDataSubId(subscriptionId);
            }
        } catch (RemoteException ex) {
            // ignore it
        }
    }

小米
miui.telephony.SubscriptionManager
SubscriptionManager.getDefault().setDefaultDataSlotId(slot_id);

摩托
com.motorola.msimsettings.utils.SmartSimUtils

    public static int setSimForDataWithStatusToast(Context context, int arg3) {
        int result = SmartSimUtils.setSimForData(context, arg3);
        Toast.makeText(context, SmartSimUtils.mapDDSSwitchStatusToMessageResId(result), 1).show();  // 切换可能需要1分钟
        SmartSimUtils.notifyDdsAssistantSlotChanged(context);
        return result;
    }


    public int setDefaultDataSubIdWithNwAutoSwitch(int arg1) {
        return this.mManager.setDefaultDataSubIdWithNwAutoSwitch(arg1);
    }


    public MotoExtTelephonyManagerAdapter(Context arg2) {
        this.mManager = new MotoExtTelephonyManager(arg2);
    }

com.motorola.android.telephony.MotoExtTelephonyManager

    private IMotoExtTelephony getIMotoExtTelephony() {
        return com.motorola.android.internal.telephony.IMotoExtTelephony.Stub.asInterface(ServiceManager.getService("motoexttelephony"));
    }

    public int setDefaultDataSubIdWithNwAutoSwitch(int ddsSubId) {
        try {
            IMotoExtTelephony motoExtTelephony = this.getIMotoExtTelephony();
            if(motoExtTelephony == null) {
                this.loge("moto ext telephony is null");
                return -1;
            }

            return motoExtTelephony.setDefaultDataSubIdWithNwAutoSwitch(ddsSubId);
        }
        catch(RemoteException | NullPointerException ex) {
            return -1;
        }
    }




在JNI中避免使用全局静态变量

Java 可以加载一个类的多个实例(因为例如有多个类加载器classloader),并且静态变量将在所有实例之间共享。 不幸的是,清理调用也是如此,如果您的类的一个实例被销毁而另一个仍在使用中,则会导致不一致。

在这些情形,使用 native class constructor / finalizer 并分配/取消 内存资源 会更明智。

你不能假设 Java会以单线程模式调用natvie函数。


大多数代码只需要在一个线程里运行,这样,就不需要锁定机制。
但是,不能因为只有一个线程, 就直接用 static mut, 并将 对它的访问 wrap到unsafe代码块里。
这样可能会出现严重的内存问题。
举个例子, 从全局变量中不安全地借用可能会同时给我们多个可变引用。 然后我们可以使用其中一个来迭代一个向量,
但另一个却从同一个向量中删除值。 然后迭代器可能会超出有效的内存边界,这种潜在崩溃, 是安全的 Rust 可以防止的。

Rust标准库, 提供了一种“全局”存储值的方法,可在单个线程中安全访问。这就是 thread local.
在多线程的情况下,每个线程都会获得变量的独立副本。

thread local优缺点

坏处就是, 该静态对象,对你的程序产生的其他线程,并不可见。
好处就是,与真正的全局状态不同,它是完全安全的,无痛使用— 真正的全局状态,在任何语言中,都是一个巨大的痛苦。

使用thread local不是最简单的方案,但是它允许执行任意的初始化代码, 这些初始化代码
只有在第1次 访问该变量的值时,才会执行。


use lazy_static::lazy_static;
use std::collections::HashMap;

lazy_static! {
    static ref PRIVILEGES: HashMap<&'static str, Vec<&'static str>> = {
        let mut map = HashMap::new();
        map.insert("James", vec!["user", "admin"]);
        map.insert("Jim", vec!["user"]);
        map
    };
}

fn show_access(name: &str) {
    let access = PRIVILEGES.get(name);
    println!("{}: {:?}", name, access);
}

fn main() {
    let access = PRIVILEGES.get("James");
    println!("James: {:?}", access);

    show_access("Jim");
}

lazy_static! {
    static ref USER_TOKEN_HASHMAP: Mutex> = Mutex::new(HashMap::new());
}

fn func() {
    let mut _map = USER_TOKEN_HASHMAP.lock().unwrap();
    let user_email = String::from("aaa");
    let user_password = String::from("bbb");
    _map.insert(user_email, user_password);
}

必须用Mutex

参考资料
https://www.sitepoint.com/rust-global-variables/

BluetoothAdapter setName

1. 蓝牙必须启用,setName()才能工作. 如果蓝牙当前状态是关闭的,调用setName是无效的。
2. 启用蓝牙需要时间。也就是说,你不能在调用 enable()之后马上再调用setName()
3. 名字需要一定时间生效。 也就是说,你不能在setName()之后,马上调用getName()就能得到新设置的名字

为什么rust编译出的可执行文件体积那么大

因为Rust用静态链接(static linking)来编译可执行文件。

也就是说,所有依赖的库,都会编译进去。当然也包含了Rus runtime

可以添加

-C prefer-dynamic

命令行参数, 来让rust编译器是用动态链接。

如果用cargo,这个选项也要传给rustc

cargo rustc --debug  -- -C prefer-dynamic
或者
cargo rustc --release -- -C prefer-dynamic

进一步,在Cargo.toml中配置,也可以减少一些体积

[profile.release]
opt-level = 'z'     # Optimize for size.
lto = true          # Enable Link Time Optimization
codegen-units = 1   # Reduce number of codegen units to increase optimizations.
panic = 'abort'     # Abort on panic
strip = true        # Strip symbols from binary*

https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html
https://stackoverflow.com/questions/29008127/why-are-rust-executables-so-huge

F2 Status

返回当前目录或者当前应用的相关信息

按照应用规范,这个命令可能用来指出 在UICC的应用的初始化过程已经成功执行,或者终止过程即将执行。

注意:这个之事,也可能用来同步终端和UICC上的应用。

P1 = 0 没有额外的指示,仅仅想知道卡还在不在

P1 = 1 当前应用 已经在终端被初始化(也就是准备好让这张卡入网)

P1= 2 终端准备 关闭uicc上的应用

p2=0 Response parameters and data are identical to the response parameters and data of the SELECT command

p2 =1 The DF name TLV-object of the currently selected application is returned

p2 =c 不要返回额外的数据,我就是看看卡还在不在

6FAD

此EF包含有关操作模式(按照usim类型而不同的模式)的信息。

正常模式(用户登录3GPP网络)

型号批准(在型号批准过程中,允许ME的特定的使用)

基站小区测试(在某个小区在商用之前,进行测试)

制造商特定的用途(允许ME制造商执行专有的自动测试,例如维护阶段)

此文件的内容至少为4个字节,其中前4个字节的含义

1字节 UE operation mode
00 = 正常模式
80 = 类型批准操作
01 = 正常模式+特定设施
81 = 型号批准操作 + 特定
02 = 维护(离线)
04 = 小区测试操作

2-3字节 Additional information
UE操作模式的额外信息
4字节 length of MNC in the IMSI

如果 service n°130 is “available”, 那么这个字节为0
否则,应该为 2 或者 3 表示 MNC的长度为2 或者 3

130号服务是 Support for SUPI of type NSI or GLI or GCI

如130号服务可用,那么 EF_IMSI (6F07)就不应该存在