首页 » 技术分享 » 语音信箱流程分析 voice mail number

语音信箱流程分析 voice mail number

 
文章目录

该文档主要是基于Android JB 版本,和可能和KK版本有一定的差异.如果在KK上面有不一样的地方,也可以参考JB上面的思路来分析相关的问题 

以下分析是基于mtk的源码,不是Android 原始的code.我会把mtk 相关的code 提取出来,以便和Android 原生的code 对比.

 

如何配置语音信箱

具体可以参考: FAQ04505

 

有些SIM卡在出厂时并没有预置VoiceMail number,但运营商又要求能够根据PLMN去自适应的从手机中读取到预设的VM number。在此介绍以xml的方式预置VM number的方法,以及如何允许用户去修改并能够记住用户的选择。VM number使用的优先级为: SIM卡读取>用户设置>xml预置。在用户修改voice mail number时,优先存储到SIM卡。SIM卡存储失败,则以IMSI为单位存储到手机中

 

以XML的方式预置VM number,文件名为:voicemail-conf.xml

文件的内容格式为

<?xml version='1.0' encoding='utf-8'?>

<voicemail>

<voicemail numeric="46000" carrier="CMCC" vmnumber="10086" vmtag="CMCC voicemail" />

</voicemail>

如果遇到虚拟运营商,另外分析.

 

关于文件的位置

文件在手机中的位置:system/etc

文件在工程中的位置: mediatek\frameworks\base\telephony\etc\voicemail-conf.xml

 

还需要在build\target\product\common.mk中,添加语句PRODUCT_COPY_FILES += mediate/frameworks/base/telephony/etc/voicemail-conf.xml:system/etc/voicemail-conf.xml

使SIM卡中的VM number优先于预置号码的方法

 将SIMRecords.java (

JB3之前版本:frameworks\base\telephony\java\com\android\internal\telephony\gsm

JB3,JB5:frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)

的函数private setVoiceMailByCountry(String spn)中的语句if (mVmConfig.containsCarrier(spn))修改为

if (TextUtils.isEmpty(voiceMailTag) && TextUtils.isEmpty(voiceMailNum) && mVmConfig.containsCarrier(spn))

在使用了voicemail-conf.xml来预置VM number后,使终端用户可以修改VM number的方法

1) 在SIMRecords.java中添加语句import android.text.TextUtils;

 

2) 在SIMRecords.java中添加一个成员变量boolean isSetByCountry = false;

 

3) 将SIMRecords.java中的函数private setVoiceMailByCountry(String spn)修改为

private void setVoiceMailByCountry(String spn) {

if (TextUtils.isEmpty(voiceMailTag) && TextUtils.isEmpty(voiceMailNum) && mVmConfig.containsCarrier(spn))

{

// isVoiceMailFixed = true; //注释掉此语句以让用户能够修改

isSetByCountry = true; //让GsmPhone知道这是从xml中读取的

voiceMailNum = mVmConfig.getVoiceMailNumber(spn);

voiceMailTag = mVmConfig.getVoiceMailTag(spn);

}

 

4) 在GSMPhone.java (

   JB3之前版本:frameworks\base\telephony\java\com\android\internal\telephony\gsm

JB3,JB5:frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)中的函数handleMessage中的语句case EVENT_SIM_RECORDS_LOADED:中将语句 if (imsi != null && imsiFromSIM != null && !imsiFromSIM.equals(imsi))

{

}

修改为

SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());

// 当相应卡槽更换SIM卡后,是否清除用户对之前SIM卡的VM number设置

boolean clear_if_change_sim = sp.getBoolean(“clear_if_change”, false);

if (clear_if_change_sim && imsi != null && imsiFromSIM != null && !imsiFromSIM.equals(imsi)){

//storeVoiceMailNumber(null);

Log.d(LOG_TAT, “reset vm number because sim changed”);

SharedPreferences.Editor editor = sp.edit();

editor.remove(getVmSimImsi());

editor.apply();

setVmSimImsi(null);

}

 

5) 将GSMPhone.java中的函数private void storeVoiceMailNumber(String number)中的语句editor.putString(VM_NUMBER+mySimId, number);

修改为editor.putString(getSubscriberId(), number);

//不再使用卡槽作为保存VM number的单位,而使用IMSI

 

6) 将GSMPhone.java中的函数public String getVoiceMailNumber()中的语句

if (TextUtils.isEmpty(number))

修改为

if (TextUtils.isEmpty(number) || ((SIMRecords)mIccRecords.get())).isSetByCountry)

//如果SIM卡中无VM number或是通过voicemail-conf.xml来设置的,则应该读取一下Preference,看是否用户对此SIM卡设置过VM number。

并且将语句number = sp.getString(VM_NUMBER+mySimId, null);修改为

Log.d(LOG_TAG, vm num from simRecords, num= “+number+” is from factory= “+ ((SIMRecords)mIccRecords).isSetbyCountry);

String temp = sp.getString(getSubscriberId(), null);

if (temp != null)

{

Log.d(LOG_TAG, “replace vm num with user defined, num= “+temp);

number = temp;

}

 

 

 

流程分析

在看这一节的时候,请先看一下如何配置语音信箱 这一节,因为下面的分析是建立在上面这一节的基础之上的.

 

voicemail-conf.xml 加载和解析

KK:frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/VoiceMailConstants.java

JB: frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/VoiceMailConstants.java

配置在xml 文件里面的语音信箱的解析是VoiceMailConstants 来负责的.在创建VoiceMailConstants 这个类的时候,就直接调用了loadVoiceMail(); 来解析

VoiceMailConstants() {

CarrierVmMap = new HashMap<String, String[]>();

loadVoiceMail();

    }

上面红色方框里面的data 变量对应的就是类似下面的配置.

<voicemail numeric="46000" carrier="" vmnumber="10086" vmtag="Voice Mail" />

 

在不考虑虚拟运营商的情况下:

numeric 就是mcc + mnc 

vmnumber 就是要求预制的语音信箱号码

vmtag 就是语音信箱的名称

carrier 就是一个备注的名称,用于给开放人员方便区分不同sim卡的,可以随便写

所有的配置的信息都保存在CarrierVmMap 这个映射对应里面.

如果voicemail-conf.xml 里面配置的参数里面有numeric(mcc+mnc) 相同的属性值的话,后面的会覆盖前面的.原因很简单,这和java 里面的Map 对象有关系.

 

需要调用的时候根据获取到的sim 卡的spn 来在CarrierVmMap 这个HashMap 里面得到对应的值.

 

 

真正的发起加载和解析这个xml 文件的发起者还是SIMRecords.java.

SIMRecords.java路径:

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/SIMRecords.java

 

语音信箱号码的读取

在sim 卡识别完成之后,系统会去根据mcc +mnc 来获取这张卡的spn,再根据spn 设置这张卡的语音信箱号码.

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java

r.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); 这行代码会监听到系统识别sim 卡相关的信息完成了.然后发送EVENT_SIM_RECORDS_LOADED 消息来更新sim 卡的语音信箱. 当相应卡槽更换SIM卡后,会更新之前SIM卡的VM number设置

 

 

一般发起读取sim 卡语音信箱的是phone 进程.

 

Phone 进程会调用framework 里面的PhoneProxy.java 的getVoiceMailNumber方法来调用GSMPhone.java 的getVoiceMailNumber 方法

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

 

public String getVoiceMailNumber() {

return mActivePhone.getVoiceMailNumber();

 }

 

在调用GSMPhone.java 的getVoiceMailNumber 方法的时候,会进行判断.

如果SIM卡中无VM number或是通过voicemail-conf.xml来设置的,则应该读取一下Preference,看是否用户对此SIM卡设置过VM number。

 

其中标记1的地方是获取运营商本身写到sim卡里面的语言信箱号码.

标记2 的地方是判断运营商是否在sim 卡设置VM mumber 和用户是否自己重新设置过VMnumber.

isSetByCountry 这个布尔值变量就是后来我们自己增加的,用来区分用户自己是否改过这个VM number

标记3中的是根据spn 来获取对应的VM number的,mtk 原始的设计不是这样的,而是使用卡槽和sim卡id 来区别的,我这里由于项目的需要,涉及到虚拟运营商,所以使用的spn来区别.而且我这边在设置的时候也把这个key 改成了使用spn.

 

关于getMvnoVoiceMailNumberKey()的分析在虚拟运营商的语音信箱的分析 这一节

下面的函数是mtk 原始的设计

 

 

语音信箱号码的修改和保存的流程

如何我们使用voicemail-conf.xml 来配置了VM number 之后,是否是否可以手动修改呢,结果是肯定可以的,但是要注意手动修改的VM numbe和xml 预制的VM number的优先级情况.

 

前面已经提到几种VM number 的优先级情况:

VM number使用的优先级为: SIM卡读取>用户设置>xml预置。在用户修改voice mail number时,优先存储到SIM卡。若SIM卡存储失败,则以IMSI为单位存储到手机中

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java

 

Mtk 原始的设计在保存VM number 的时候是使用的卡槽来区分.我这边这里修改之后是使用的

关于getMvnoVoiceMailNumberKey()的分析在虚拟运营商的语音信箱的分析 这一节

 

App层和framework 层是怎么交互的?

相关的文件

 

JB:

packages/apps/Phone/src/com/mediatek/settings/VoiceMailSetting.java

 

KK:

packages/services/Telephony/src/com/mediatek/settings/VoiceMailSetting.java

packages/apps/Dialer/src/com/android/dialer/dialpad/DialpadFragment.java

 

从拨号盘按1到拨出电话:

我们所看到拨号盘页面也就是DialpadFragment.java 文件,该文件实现了View.OnLongClickListener 接口,所以它本身就可以实现onLongClick 方法来处理长按时间

 

 

 

上面的getVoicemailIntent() 方法返回的intent 里面并没有具体的voicemail number ,那么应用到底是在那里处理才会拿到这个号码呢?

 

虚拟运营商的语音信箱的分析(配置,读取)

这里主要是介绍和MNO 不一样的地方,因为MVNO 和MNO 在这一块大部分是一样的.

什么是mvno?

MVNO(Mobile Virtaul Network Operator)虚拟网络运营商,没有自己的实体网络,通过租用MNO(Mobile Network Operator)的网络来提供网络服务。

 

MVNO 的区分方式

目前MTK支持区分MVNO的方式有四种(KK以前没有EF_GID1方式),每种区分方式对应一个xml的配置表:

1. EF_SPN方式,对应MVNO配置到Virtual-spn-conf-by-efspn.xml中

2. EF_IMSI方式,对应MVNO配置到Virtual-spn-conf-by-imsi.xml中

3. EF_PNN方式,对应MVNO配置到Virtual-spn-conf-by-efpnn.xml中

4. EF_GID1方式,对应MVNO配置到Virtual-spn-conf-by-efgid1.xml中

 

MVNO VM number 如何配置

Mvno 和 MNO 的mcc mnc 都是一样的.我们前面在voicemail-conf.xml 加载和解析这一节中提到如果sim 卡的mcc+mnc 是一样的,那么他们的VM number 就是一样的.但是MVNO 和他所租用的运营商不是同一个运营商,所以MVNO的语音信箱和spn 经常会不一样.所以如果涉及到MVNO的时候,需要在 如何配置语音信箱这一节的基础上面再做如下修改.

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java


editor.putString(VM_NUMBER + mySimId, number); 改成

editor.putString(getMvnoVoiceMailNumberKey(), number);

 

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/SIMRecords.java

setVoiceMailByCountry 的参数原本是operator也就是mcc+mnc,处于MVNO的考虑,我们这里把他改成getMvnoVoiceMailNumberKey() 方法的返回值.

 

getMvnoVoiceMailNumberKey() 方法具体的实现代码如下(SIMRecords GSMPhone 是一样的.


目前Android JB 版本支持的MVNO 就是通过上面涉及到的3个方式来分区的,也就是spn,pnn,imsi.具体可以参考MVNO 的区别方式  这一节.

 

 

漫游状态下和非漫游状态的下配置不同的语音信箱号码

同一个mcc+mnc 可以对应有多个运营商,在这个时候,他们的可以都有自己VM number.

那么如果同一张sim 卡在使用不同的网络的时候,如果要求它的VM number 跟着所使用的网络的运营商来变也是可以的.其中一种就是漫游和非漫游情况下的配置不同语音信箱的配置.

 

原理分析:在用户需要获取VM number 的时候,我们去判断当前sim 卡是否处于漫游状态,如果,如果处于漫游状态,我们就去改变的.具体修改方法如下:

 

方法一(少部分sim 有要求)

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java

 

 

增加isNetworkRoaming 方法

 

上面这种方法是将需要配置漫游状态下使用不同VM number 的sim 卡的mcc + mnc 和对应的漫游状态下的VM number 配置2个数组里面,在有这种要求的sim 的sim 卡数量少的时候,比较适合.如果遇到数量很多的情况下,可以按照方法2的思路来改.

 

方法2

在voicemail-conf.xml 中的配置可以按照下面的格式添加roamingvm 这一属性, roamingvm 的属性值就是对应的sim 卡(23433 ) 在漫游状态下的VM number , vmnumber 就是非漫游状态下的VM number .

<voicemail numeric="23433" carrier="Orange" vmnumber="+447973100123" vmtag="Voicemail" roamingvm="+447973100123"/>

 

同时参考voicemail-conf.xml 这一节来处理roamingvm 这一项.然后在GSMPhone.java 的getVoiceMailNumber 方法按照下面的思路来修改:如果当前sim 是漫游状态,就使用roamingvm 这一属性值来作为其VM number ,这个时候就需要在VoiceMailConstants.java

 中增加一个方法来专门获取roamingvm 这一属性值.

KK:frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/VoiceMailConstants.java

JB: frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/VoiceMailConstants.java

增加方法和变量

static final int NUMBER_Roaming = 5;

         String getVoiceMailNumberRoaming(String carrier) {

String[] data = CarrierVmMap.get(carrier);

return data[NUMBER];

}

 

同时在private void loadVoiceMail() 做如下红色部分修改

 

 

转载自原文链接, 如需删除请联系管理员。

原文链接:语音信箱流程分析 voice mail number,转载请注明来源!

0