作者归档:softsim

Android 11 上的软件包可见性过滤

如果应用以 Android 11(API 级别 30)或更高版本为目标平台,并查询与设备上已安装的其他应用相关的信息,则系统在默认情况下会过滤此信息。从您的应用的角度来看,有限的软件包可见性会减少设备上显示的已安装应用数。

此过滤行为有助于最大限度减少显示您的应用在实现其用例时不需要的潜在敏感信息,但您的应用仍然可以访问这些信息。此外,过滤后的软件包可见性可帮助 Google Play 等应用商店评估应用为用户提供的隐私权和安全性。例如,Google Play 会将已安装应用的列表视为个人和敏感用户数据。

有限的应用可见性会影响提供其他应用相关信息的方法的返回结果,例如 queryIntentActivities()、getPackageInfo() 和 getInstalledApplications()。

有限的可见性还会影响与其他应用的显式交互,例如启动另一个应用的服务

某些软件包仍然自动可见。您的应用始终可以在查询其他已安装的应用时看到这些软件包。
=====================================
系统会自动让部分应用对您的应用可见,以便您的应用可与其交互,而无需声明 queries 元素。此行为有助于支持基本功能和常见用例。

即使您的应用以 Android 11(API 级别 30)或更高版本为目标平台,以下类型的应用也始终对您的应用可见:

您自己的应用。
实现 Android 核心功能的某些系统软件包,例如媒体提供程序。
安装了您应用的应用。
使用 startActivityForResult() 方法在您的应用中启动 activity 的任何应用,正如如何获取 activity 的结果这一指南中所述。
启动或绑定到您应用中的某项服务的任何应用。
访问您应用中的 Content Provider 的任何应用。
具有 Content Provider 的任何应用,其中您的应用已被授予 URI 权限来访问该 Content Provider。
接收您应用的输入的任何应用。这种情况仅适用于您的应用作为输入法应用提供输入。

此外,您可以使用隐式或显式 intent 来启动另一应用的 activity,无论这个应用是否对您的应用可见。

实现 Android 核心功能的某些系统软件包会自动对您的应用可见,即使您的应用以 Android 11 或更高版本为目标平台也是如此。这组特定的软件包取决于运行您应用的设备。

如需查看特定设备的完整软件包列表,请在开发机器上的终端中运行以下命令:

adb shell dumpsys package queries

在命令输出中,找到 forceQueryable 部分

=====================================

如需查看其他软件包,请使用 query 元素声明您的应用需要提高软件包可见性。

在极少数情况下,如果遇到 元素无法提供适当的软件包可见性,您还可以使用 QUERY_ALL_PACKAGES 权限。如果您在 Google Play 上发布应用,那么应用是否此权限需要根据即将生效的政策进行批准。

QMI UIM APDU Security Restrictions

qmi_uim.c ADPU rejected due to security restrictions: logical_channel doesnt belong to client

将 NV 67312 (QMI UIM APDU Security Restrictions)设置为0, 可以移除APDU 限制.

此NV项目不能用QXDM工具修改,必须写入 HW MBN 才能生效

所以需要编译modem源代码
modem_proc/mcfg/mcfg_gen/generic/common/Default/mcfg_hw_gen_Default.xml

 <NvEfsItemData name="apdu_security_restrictions" id="67312" description="APDU Security Restrictions" comment="" category="UIM" mcfgAttributes="0x09" mcfgVariant="2" fullpathname="/nv/item_files/modem/qmi/uim/apdu_security_restrictions"> <Member name="apdu_security_restrictions" description="" comment="" sizeOf="1" type="uint8">0 </Member>
 </NvEfsItemData>

重新编译生成 Non-Hlos.bin

用QPST EFS Explorer 也无法 往 /nv/item_files/modem/qmi/uim 目录写入 apdu_security_restrictions 文件,写入别的名称文件倒是可以

可插拔esim

eSIM.me的eUICC卡

可以将带有 SIM 卡槽的Android设备变成兼容 eSIM 的设备的产品。将eSIM.me提供的eUICC卡插入安卓设备,通过Google Play商店安装eSIM.me app,作为控制eUICC的LPA

如果你有一个双 SIM 卡终端,有两个卡槽和两张 eSIM.me 卡,你可以让两个插槽都兼容 eSIM。

eSIM.me app(LPA )通过OMAPI来写卡

在下面的日志中,您可以看到如何使用 ISD-R AID (a0000005591010ffffffff8900000100) 打开逻辑通道

I/esim.me ( 5121): AndroidOmapiApi. Check isReaderAvailable : SIM1
I/SecureElement-Terminal-SIM1( 2133): ATR : 3b9f96803fc7828031e073fe211b57aa8660f0010004fb
I/SecureElementService( 2133): openLogicalChannel() AID = a0000005591010ffffffff8900000100, P2 = 0
W/SecureElement-Terminal-SIM1( 2133): Enable access control on logical channel for esim.me
I/SecureElement-AccessControlEnforcer( 2133): checkCommand() : Access = ALLOWED APDU Access = ALLOWED Reason = Unspecified
I/SecureElement-Terminal-SIM1( 2133): Sent : 81cadf2000
I/SecureElement-Terminal-SIM1( 2133): Received : df20082618f36467f9a02d9000
I/SecureElement-AraController( 2133): Refresh tag unchanged. Using access rules from cache.
I/SecureElement-AccessControlEnforcer( 2133): getAccessRule() appCert = 8d48ecfaf44c5752145ee28b3eb7429cc6627e98
I/SecureElement-AccessControlEnforcer( 2133): getAccessRule() appCert = a2afbbf5681bb26e40c8d69da6c72dd3a62cda7dbd74c5c2f26e7ff1fa819905
I/SecureElement-AccessRuleCache( 2133): findAccessRule() Case C REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c1148d48ecfaf44c5752145ee28b3eb7429cc6627e98 , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2133):  [mPackageName=esim.me, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElementService( 2133): openLogicalChannel() Success. Channel: 1

OMAPI 应该响应来自未经授权的应用程序的访问请求,但访问规则设置为只接受来自(包括)eSIM.me 应用程序的访问请求。
从下面的日志中可以看到eSIM.me的ARA-M中设置的访问规则的详细信息。
上述日志中eSIM.me应用程序签名的哈希值(SHA1:8d48ecfaf44c5752145ee28b3eb7429cc6627e98)在第二条规则中设置为完全权限(AID_REF_DO:4f00)

I/SecureElement-Terminal-SIM1( 2108): Sent : 81caff4000
I/SecureElement-Terminal-SIM1( 2108): Received : ff4081b4e222e1184f00c114a4ec39717cecd7f1e84e913b22b0555fe7dfdd8ae306d00101d10101e222e1184f00c1148d48ecfaf44c5752145ee28b3eb7429cc6627e98e306d00101d10101e222e1184f00c11446bcaf2247253f695f19d60eece158099fe52c62e306d00101d10101e222e1184f00c114fa4abb42827a0fbceb891ddc0d34f658c21ff88ce306d00101d10101e222e1184f00c11435cc639bb5826fde0d0ef088b0f20cb69f0c49ade306d00101d101019000
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c114a4ec39717cecd7f1e84e913b22b0555fe7dfdd8a , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c1148d48ecfaf44c5752145ee28b3eb7429cc6627e98 , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c11446bcaf2247253f695f19d60eece158099fe52c62 , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c114fa4abb42827a0fbceb891ddc0d34f658c21ff88c , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]
I/SecureElement-AccessRuleCache( 2108): Add Access Rule: REF_DO: AID_REF_DO: 4f00 Hash_REF_DO: c11435cc639bb5826fde0d0ef088b0f20cb69f0c49ad , com.android.se.security.ChannelAccess
I/SecureElement-AccessRuleCache( 2108):  [mPackageName=, mAccess=ALLOWED, mApduAccess=ALLOWED, mUseApduFilter=false, mApduFilter=null, mCallingPid=0, mReason=, mNFCEventAllowed=ALLOWED, mPrivilegeAccess=UNDEFINED]

默认文件系统
即使在下载eSIM之前使用eUICC,也需要向终端端显示所需的最低文件系统。GSMA SGP.22规定应提供一个MF和极少量的EF,
但eSIM.me卡的默认文件系统还包括DIR、ICCID、IMSI、MSISDN等。它似乎是一个默认文件系统,
或者它被实现为一个默认配置文件。如果您请求一个处于空状态的配置文件列表,则会返回一个配置文件,
如下面的日志所示。

I/esim.me ( 5121): AndroidOmapiApi. execute APDU command :81E2910003BF2D00
I/SecureElement-AccessControlEnforcer( 2133): checkCommand() : Access = ALLOWED APDU Access = ALLOWED Reason = Unspecified
I/SecureElement-Terminal-SIM1( 2133): Sent : 81e2910003bf2d00
I/SecureElement-Terminal-SIM1( 2133): Received : 613e
I/SecureElement-Terminal-SIM1( 2133): Sent : 81c000003e
I/SecureElement-Terminal-SIM1( 2133): Received : bf2d3ba039e3375a0aXXXXXXXXXXXXXXXXXXXX4f10a0000005591010ffffffff89000012009f70010191076553494d2e6d6592076553494d2e6d659501019000
I/esim.me ( 5121): AndroidOmapiApi. received APDU response :BF2D3BA039E3375A0AXXXXXXXXXXXXXXXXXXXX4F10A0000005591010FFFFFFFF89000012009F70010191076553494D2E6D6592076553494D2E6D659501019000
I/esim.me ( 5121): Profile list object: profileInfoListOk: {
I/esim.me ( 5121):           {
I/esim.me ( 5121):                   iccid: XXXXXXXXXXXXXXXXXXXX,
I/esim.me ( 5121):                   isdpAid: A0000005591010FFFFFFFF8900001200,
I/esim.me ( 5121):                   profileState: 1,
I/esim.me ( 5121):                   serviceProviderName: eSIM.me,
I/esim.me ( 5121):                   profileName: eSIM.me,
I/esim.me ( 5121):                   profileClass: 1
I/esim.me ( 5121):           }
I/esim.me ( 5121):   }

MCC / MNC 是 262/24

eUICC卡信息
作为参考,我还将在此处粘贴阅读 EuicInfo2 的日志。

I/esim.me ( 5121): AndroidOmapiApi. execute APDU command :81E2910003BF2200
I/SecureElement-AccessControlEnforcer( 2133): checkCommand() : Access = ALLOWED APDU Access = ALLOWED Reason = Unspecified
I/SecureElement-Terminal-SIM1( 2133): Sent : 81e2910003bf2200
I/SecureElement-Terminal-SIM1( 2133): Received : 617b
I/SecureElement-Terminal-SIM1( 2133): Sent : 81c000007b
I/SecureElement-Terminal-SIM1( 2133): Received : bf2278810302010282030202008303040200840d81010082040007701e830239b68503017f3a8603090200870302030088020490a916041481370f5125d0b1d408d4c3b232e6d25e795bebfbaa16041481370f5125d0b1d408d4c3b232e6d25e795bebfb8b010004030100000c0d45442d5a492d55502d303832329000
I/esim.me ( 5121): AndroidOmapiApi. received APDU response :BF2278810302010282030202008303040200840D81010082040007701E830239B68503017F3A8603090200870302030088020490A916041481370F5125D0B1D408D4C3B232E6D25E795BEBFBAA16041481370F5125D0B1D408D4C3B232E6D25E795BEBFB8B010004030100000C0D45442D5A492D55502D303832329000
I/esim.me ( 5121):  - EUICC info 2 is: {
I/esim.me ( 5121):   profileVersion: 020102,
I/esim.me ( 5121):   svn: 020200,
I/esim.me ( 5121):   euiccFirmwareVer: 040200,
I/esim.me ( 5121):   extCardResource: 81010082040007701E830239B6,
I/esim.me ( 5121):   uiccCapability: 011111110011101,
I/esim.me ( 5121):   javacardVersion: 090200,
I/esim.me ( 5121):   globalplatformVersion: 020300,
I/esim.me ( 5121):   rspCapability: 1001,
I/esim.me ( 5121):   euiccCiPKIdListForVerification: {
I/esim.me ( 5121):           81370F5125D0B1D408D4C3B232E6D25E795BEBFB
I/esim.me ( 5121):   },
I/esim.me ( 5121):   euiccCiPKIdListForSigning: {
I/esim.me ( 5121):           81370F5125D0B1D408D4C3B232E6D25E795BEBFB
I/esim.me ( 5121):   },
I/esim.me ( 5121):   euiccCategory: 0,
I/esim.me ( 5121):   ppVersion: 010000,
I/esim.me ( 5121):   sasAcreditationNumber: ED-ZI-UP-0822
I/esim.me ( 5121): }

SAS-UP的认证号是ED-ZI-UP-0822,貌似是中国一家叫“东信和平”的智能卡厂商生产的卡。

https://www.gsma.com/security/wp-content/uploads/2021/02/GSMA-SAS_UP-Certificate-Eastcompeace-Zhuhai-China-0822.pdf

原文: https://cheerio-the-bear.hatenablog.com/entry/2022/03/04/172109

SAS for UICC Production (SAS-UP)

SAS for Subscription Management (SAS-SM)

Security Accreditation Scheme (SAS)

The SAS-SM is defined only for activities within eSIM Remote Provisioning and Management:

eSIM life-cycle and processes in the scope of SM-SR
Profile life-cycle and processes in the scope of SM-DP and SM-DP+
SM-DS processes

rust global jvm

JNI 接口指针(JNIEnv)仅在当前线程中有效。 如果另一个线程需要访问 Java VM,它必须首先调用 AttachCurrentThread() 将自己附加到 VM 并获取 JNI 接口指针(JNIEnv)。 一旦连接到 VM, native线程就像在native方法中运行的普通 Java 线程一样工作。 native线程一直连接到 VM,直到它调用 DetachCurrentThread() 来分离自己。

https://github.com/sfackler/rust-jni-sys/blob/master/src/lib.rs
这里定义了

pub type JavaVM = *const JNIInvokeInterface_;


#[repr(C)]
#[derive(Copy)]
pub struct JNIInvokeInterface_ {
    pub reserved0: *mut c_void,
    pub reserved1: *mut c_void,
    pub reserved2: *mut c_void,
    pub DestroyJavaVM: Option<unsafe extern  "system" fn(vm:  *mut  JavaVM)  -> jint>,
    pub AttachCurrentThread: Option<
        unsafe extern "system" fn(vm: *mut JavaVM,
                                  penv: *mut *mut c_void,
                                  args: *mut c_void)
                                  -> jint,
    >,
    pub DetachCurrentThread: Option<unsafe extern "system"  fn(vm:  *mut  JavaVM)  -> jint>,
    pub GetEnv: Option<
        unsafe extern "system" fn(vm: *mut JavaVM,
                                  penv: *mut *mut c_void,
                                  version: jint)
                                  -> jint,
    >,
    pub AttachCurrentThreadAsDaemon: Option<
        unsafe extern "system" fn(vm: *mut JavaVM,
                                  penv: *mut *mut c_void,
                                  args: *mut c_void)
                                  -> jint,
    >,
}

impl Clone for JNIInvokeInterface_ {
    fn clone(&self) -> Self {
        *self
    }
}

这就是一个JNI C接口在rust中的表示。

可以Copy, 可以Clone

在 https://github.com/jni-rs/jni-rs/blob/master/src/wrapper/java_vm/vm.rs 中

#[repr(transparent)]
pub struct JavaVM(*mut sys::JavaVM);
...
impl JavaVM {
...
    pub unsafe fn from_raw(ptr: *mut sys::JavaVM) -> Result<self> {
        non_null!(ptr, "from_raw ptr argument");
        Ok(JavaVM(ptr))
    }

    pub fn get_java_vm_pointer(&self) -> *mut sys::JavaVM {
        self.0
    }

可以看到,直接从sys::JavaVM生成的

而JNI_OnLoad在rust中的签名是

pub unsafe extern "system" fn JNI_OnLoad(vm: *const JavaVM, _reserved: *const c_void) -> jint 
pub unsafe extern fn JNI_OnLoad(vm: *mut JavaVM, reserved: *mut c_void) -> jint

或者

                            
#[allow(non_snake_case)]
#[no_mangle]
pub extern "system" fn JNI_OnLoad(vm: JavaVM, _: *mut c_void) -> jint {

    let env = vm.get_env().expect("Cannot get reference to the JNIEnv");

    JNI_VERSION_1_8
}

JNIEnv是一个用来在Java和Native代码之间通信的结构体的指针。
这个通信的ABI几乎由每个JVM(和Android)实现。
这个结构体有多个版本,所以为什么要在JNIEnv里有GetVersion这个玩意。

rust thread spawn 静态生命周期

通过 thread::spawn 产生的线程理论上可以比其父线程寿命更长。 如果子线程可以引用来自父线程的数据,那么当父线程停止时,这些引用将dangle(悬空, 也就是be invalid 无效)。 这就是为什么 thread::spawn 的闭包有个“static bound”。你在主函数中用join来等待线程的结束, 因此 主函数 肯定比 子进程活的时间长, 但是 编译器没办法理解这一点。

use std::thread;
use std::time::Duration;


fn do_task(host: &str) {
    println!("{}", host)
}

fn start(hostz: &'static str) {
//fn start(hostz: &str) {
    thread::spawn(move || do_task(hostz));
}

fn main() {
    let hostx = "tw.com";

    start(hostx);

    thread::sleep(Duration::from_millis(2000));
}

如果hostz不标记为static生命周期就会报错
hostz有一个匿名的生命周期 `_ 有被 move到 lamda函数里, thread::spawn又要求static生命周期

解释:
闭包 捕获了一个 str, 所以它的上下文(环境)生命周期 不会比这个str更长, 但是 spawn又要求 闭包有static 生命周期

另外一种解决方法是:

fn start(hostz: &str) {
    let hostff = hostz.to_owned();
    thread::spawn(move || do_task(&hostff));
}

在spawn之前, 复制一份str

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;
        }
    }