深度解析:Android在Mms设置页面更改短信中心号码流程
相关控件初始化方法:showSmscPref
private void showSmscPref() {
int count = MSimTelephonyManager.getDefault().getPhoneCount();
boolean airplaneModeOn = Settings.System.getInt(getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0) != 0;
for (int i = 0; i < count; i++) {
final Preference pref = new Preference(this);
pref.setKey(String.valueOf(i));
String title = (count <= 1) ?
getString(R.string.pref_one_smcs)
: getString(R.string.pref_more_smcs, i + 1);
pref.setTitle(title);
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
MyEditDialogFragment dialog = MyEditDialogFragment.newInstance(
MessagingPreferenceActivity.this,
preference.getTitle(),
preference.getSummary(),
Integer.valueOf(preference.getKey()));
dialog.show(getFragmentManager(), "dialog");
return true;
}
});
mSmscPrefCate.addPreference(pref);
mSmscPrefList.add(pref);
updateSMSCPref(i, airplaneModeOn);
}
registerReceiver();
}
这里使用了一个内部类MyEditDialogFragment,该类继承了DialogFragment。
public static class MyEditDialogFragment extends DialogFragment {
private MessagingPreferenceActivity mActivity;
public static MyEditDialogFragment newInstance(MessagingPreferenceActivity activity,
CharSequence title, CharSequence smsc, int sub) {
MyEditDialogFragment dialog = new MyEditDialogFragment();
dialog.mActivity = activity;
Bundle args = new Bundle();
args.putCharSequence(TITLE, title);
args.putCharSequence(SMSC, smsc);
args.putInt(SUB, sub);
dialog.setArguments(args);
return dialog;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final int sub = getArguments().getInt(SUB);
if (null == mActivity) {
mActivity = (MessagingPreferenceActivity) getActivity();
dismiss();
}
final EditText edit = new EditText(mActivity);
edit.setPadding(15, 15, 15, 15);
edit.setText(getArguments().getCharSequence(SMSC));
Dialog alert = new AlertDialog.Builder(mActivity)
.setTitle(getArguments().getCharSequence(TITLE))
.setView(edit)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
MyAlertDialogFragment newFragment = MyAlertDialogFragment.newInstance(
mActivity, sub, edit.getText().toString());
newFragment.show(getFragmentManager(), "dialog");
dismiss();
}
})
.setNegativeButton(android.R.string.cancel, null)
.setCancelable(true)
.create();
alert.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
return alert;
}
}
上述代码调用了还有一个内部类:《TAG 1-2》
public static class MyAlertDialogFragment extends DialogFragment {
private MessagingPreferenceActivity mActivity;
public static MyAlertDialogFragment newInstance(MessagingPreferenceActivity activity,
int sub, String smsc) {
MyAlertDialogFragment dialog = new MyAlertDialogFragment();
dialog.mActivity = activity;
Bundle args = new Bundle();
args.putInt(SUB, sub);
args.putString(SMSC, smsc);
dialog.setArguments(args);
return dialog;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final int sub = getArguments().getInt(SUB);
final String displayedSMSC = getArguments().getString(SMSC);
// When framework re-instantiate this fragment by public empty
// constructor and call onCreateDialog(Bundle savedInstanceState) ,
// we should make sure mActivity not null.
if (null == mActivity) {
mActivity = (MessagingPreferenceActivity) getActivity();
}
return new AlertDialog.Builder(mActivity)
.setIcon(android.R.drawable.ic_dialog_alert).setMessage(
R.string.set_smsc_confirm_message)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.phonefeature",
"com.android.phonefeature.smsc.SmscService"));
intent.setAction(COMMAND_SET_SMSC);
intent.putExtra(SUB, sub);
intent.putExtra(SMSC, displayedSMSC);
mActivity.startService(intent);
}
})
.setNegativeButton(android.R.string.cancel, null)
.setCancelable(true)
.create();
}
}
当用户点击确认(OK)后,会启动一个SmscService服务,而且把改动后的smsc number封装到intent中去,在SmscService服务中的onStartCommand将Intent中的数加入到消息队列中进行处理。
public int onStartCommand(Intent intent, int flags, int startId) {
int sub = intent.getIntExtra(SUB, 0);
int count = MSimTelephonyManager.getDefault().getPhoneCount();
Phone phone = count > 1 ? MSimPhoneFactory.getPhone(sub)
: PhoneFactory.getDefaultPhone();
if (phone == null) return START_STICKY;
boolean enable = phone.getIccCard().hasIccCard();
Intent i = new Intent();
i.setAction(NOTIFY_PHONE_STATE);
i.putExtra(SUB, sub);
i.putExtra(ENABLE, enable);
sendBroadcast(i);
String action = intent.getAction();
Message msg;
if (COMMAND_GET_SMSC.equals(action)) {
msg = mHandler.obtainMessage(MSG_GET_SMSC);
msg.arg1 = sub;
phone.getSmscAddress(msg);
} else if (COMMAND_SET_SMSC.equals(action)) {
msg = mHandler.obtainMessage(MSG_SET_SMSC);
msg.arg1 = sub;
String displayedSMSC = intent.getStringExtra(SMSC);
Bundle bundle = new Bundle();
bundle.putString(SMSC, displayedSMSC);
msg.setData(bundle);
String actualSMSC = adjustSMSC(displayedSMSC);
phone.setSmscAddress(actualSMSC, msg);
}
return START_STICKY;
}
上述代码中的phone。通过验证分析,得到phone为GSMPhone的实例,验证的代码为PhoneFactory类中的makeDefaultPhone方法进行验证得到(我这里验证的结果为GSM:
public static void makeDefaultPhone(Context context) {<TAG 1-1>
synchronized(Phone.class) {
if (!sMadeDefaults) {
sLooper = Looper.myLooper();
sContext = context;
if (sLooper == null) {
throw new RuntimeException(
"PhoneFactory.makeDefaultPhone must be called from Looper thread");
}
int retryCount = 0;
for(;;) {
boolean hasException = false;
retryCount ++;
try {
// use UNIX domain socket to
// prevent subsequent initialization
new LocalServerSocket("com.android.internal.telephony");
} catch (java.io.IOException ex) {
hasException = true;
}
if ( !hasException ) {
break;
} else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
throw new RuntimeException("PhoneFactory probably already running");
} else {
try {
Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
} catch (InterruptedException er) {
}
}
}
sPhoneNotifier = new DefaultPhoneNotifier();
// Get preferred network mode
int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
if (TelephonyManager.getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
preferredNetworkMode = Phone.NT_MODE_GLOBAL;
}
int networkMode = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PREFERRED_NETWORK_MODE, preferredNetworkMode);
Rlog.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkMode));
// As per certain operator requirement, the device is expected to be in global《TAG 1-2》
// mode from boot up, by enabling the property persist.env.phone.global the
// network mode is set to global during boot up.
if (SystemProperties.getBoolean("persist.env.phone.global", false)) {
networkMode = Phone.NT_MODE_LTE_CMDA_EVDO_GSM_WCDMA;
Settings.Global.putInt(context.getContentResolver(),
Settings.Global.PREFERRED_NETWORK_MODE, networkMode);
}
// Get cdmaSubscription mode from Settings.Global
int cdmaSubscription;
cdmaSubscription = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.CDMA_SUBSCRIPTION_MODE,
sPreferredCdmaSubscription);
Rlog.i(LOG_TAG, "Cdma Subscription set to " + cdmaSubscription);
//reads the system properties and makes commandsinterface
sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
// Instantiate UiccController so that all other classes can just call getInstance()
UiccController.make(context, sCommandsInterface);
int phoneType = TelephonyManager.getPhoneType(networkMode);
if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
Rlog.i(LOG_TAG, "Creating GSMPhone");
sProxyPhone = new PhoneProxy(new GSMPhone(context,
sCommandsInterface, sPhoneNotifier));
android.util.Log.d("bill","GSM");
} else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
switch (TelephonyManager.getLteOnCdmaModeStatic()) {
case PhoneConstants.LTE_ON_CDMA_TRUE:
Rlog.i(LOG_TAG, "Creating CDMALTEPhone");
sProxyPhone = new PhoneProxy(new CDMALTEPhone(context,
sCommandsInterface, sPhoneNotifier));
android.util.Log.d("bill","CDMALTE");
break;
case PhoneConstants.LTE_ON_CDMA_FALSE:
default:
Rlog.i(LOG_TAG, "Creating CDMAPhone");
sProxyPhone = new PhoneProxy(new CDMAPhone(context,
sCommandsInterface, sPhoneNotifier));
android.util.Log.d("bill","CDMA");
break;
}
}
sMadeDefaults = true;
}
}
}
可是GSMPhone中并没有setSmscAddress()方法,可是GSMPhone继承了BasePhone类,因此我们在BasePhone中找到了setSmscAddress()方法。
@Override
public void setSmscAddress(String address, Message result) {
mCi.setSmscAddress(address, result);
}
上述代码中的mCi为接口CommandsInterface的实例。mCi的所引用的实例为BasePhone构造函数中传递过来的,因此我们代码<TAG1-1>中找到了该实例为RIL的实例。因此我们在RIL类中找到了setSmscAddress方法;
@Override
public void setSmscAddress(String address, Message result) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_SMSC_ADDRESS, result);
rr.mParcel.writeString(address);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+ " : " + address);
send(rr);
}
private void
send(RILRequest rr) {
Message msg;
if (mSocket == null) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
return;
}
msg = mSender.obtainMessage(EVENT_SEND, rr);
acquireWakeLock();
msg.sendToTarget();
}
在上述代码中send()方法会发消息给RIL中RILSender进行处理,同一时候msg中封装了MSG_SET_SMSC,在RIL的RILSender中运行写卡操作,同一时候发消息给SmscService。
//***** Handler implementation
@Override public void
handleMessage(Message msg) {
RILRequest rr = (RILRequest)(msg.obj);
RILRequest req = null;
switch (msg.what) {
case EVENT_SEND:
/**
* mRequestMessagePending++ already happened for every
* EVENT_SEND, thus we must make sure
* mRequestMessagePending-- happens once and only once
*/
boolean alreadySubtracted = false;
try {
LocalSocket s;
s = mSocket;
if (s == null) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
if (mRequestMessagesPending > 0)
mRequestMessagesPending--;
alreadySubtracted = true;
return;
}
synchronized (mRequestList) {
mRequestList.add(rr);
mRequestMessagesWaiting++;
}
if (mRequestMessagesPending > 0)
mRequestMessagesPending--;
alreadySubtracted = true;
byte[] data;
data = rr.mParcel.marshall();
rr.mParcel.recycle();
rr.mParcel = null;
if (data.length > RIL_MAX_COMMAND_BYTES) {
throw new RuntimeException(
"Parcel larger than max bytes allowed! "
+ data.length);
}
// parcel length in big endian
dataLength[0] = dataLength[1] = 0;
dataLength[2] = (byte)((data.length >> 8) & 0xff);
dataLength[3] = (byte)((data.length) & 0xff);
//Rlog.v(RILJ_LOG_TAG, "writing packet: " + data.length + " bytes");
s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);
} catch (IOException ex) {
Rlog.e(RILJ_LOG_TAG, "IOException", ex);
req = findAndRemoveRequestFromList(rr.mSerial);
// make sure this request has not already been handled,
// eg, if RILReceiver cleared the list.
if (req != null || !alreadySubtracted) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
}
} catch (RuntimeException exc) {
Rlog.e(RILJ_LOG_TAG, "Uncaught exception ", exc);
req = findAndRemoveRequestFromList(rr.mSerial);
// make sure this request has not already been handled,
// eg, if RILReceiver cleared the list.
if (req != null || !alreadySubtracted) {
rr.onError(GENERIC_FAILURE, null);
rr.release();
}
} finally {
// Note: We are "Done" only if there are no outstanding
// requests or replies. Thus this code path will only release
// the wake lock on errors.
releaseWakeLockIfDone();
}
if (!alreadySubtracted && mRequestMessagesPending > 0) {
mRequestMessagesPending--;
}
break;
这里进行推断写卡操作是否成功,并发送广播。
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
AsyncResult ar = (AsyncResult) msg.obj;
String smsc = null;
switch (msg.what) {
case MSG_SET_SMSC:
if (ar.exception != null) {
notifyChange(NOTIFY_SMSC_ERROR, null, 0);
return;
} else {
Bundle bundle = msg.getData();
smsc = bundle.getString(SMSC);
notifyChange(NOTIFY_SMSC_SUCCESS, null, 0);
}
break;
private void notifyChange(String notify, String smsc, int sub) {
Intent intent = new Intent(notify);
intent.putExtra(SMSC, smsc);
intent.putExtra(SUB, sub);
sendBroadcast(intent);
}
我们在MessagingPreferenceActivity的registerReceiver()方法中注冊广播接收器进行监听。
private void registerReceiver() {
if (mReceiver != null) return;
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
/*AddBy:yabin.huang BugID:SWBUG00029352 Date:20140521*/
updateSMSCPref(ALL_SUB, isAirplaneModeOn());
Message msg = new Message();
msg.what = AIR_PLANE_MODE_CHANGED;
msg.arg1 = (isAirplaneModeOn() ?
AIR_PLANE_MODE_ENABLE : AIR_PLANE_MODE_DISABLE);
mAirPlaneModeHandler.sendMessage(msg);
} else if(TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)){
if(isSimReady())
updateSMSCPref(ALL_SUB, isAirplaneModeOn());
} else if (NOTIFY_SMSC_ERROR.equals(action)) {
showToast(R.string.set_smsc_error);
} else if (NOTIFY_SMSC_SUCCESS.equals(action)) {
showToast(R.string.set_smsc_success);
int sub = intent.getIntExtra(SUB, 0);
String summary = intent.getStringExtra(SMSC);
Log.d("bill","summary--"+summary);
mSmscPrefList.get(sub).setSummary(summary);
} else if (NOTIFY_SMSC_UPDATE.equals(action)) {
int sub = intent.getIntExtra(SUB, 0);
if(TextUtils.isEmpty(mSmscPrefList.get(sub).getSummary())){
String summary = intent.getStringExtra(SMSC);
if(summary==null||summary.length()==0){
updateSMSCPref(ALL_SUB, isAirplaneModeOn());
mSmscPrefList.get(sub).setEnabled(false);
mSmscPrefList.get(sub).setSummary(null);
}else{
mSmscPrefList.get(sub).setEnabled(true);
mSmscPrefList.get(sub).setSummary(summary);
}
}else{
mSmscPrefList.get(sub).setEnabled(true);
}
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
filter.addAction(NOTIFY_SMSC_ERROR);
filter.addAction(NOTIFY_SMSC_SUCCESS);
filter.addAction(NOTIFY_SMSC_UPDATE);
registerReceiver(mReceiver, filter);
}
至今。改变整个过程的短信中心号码被整理完毕。
版权声明:本文博客原创文章,博客,未经同意,不得转载。
深度解析:Android在Mms设置页面更改短信中心号码流程的更多相关文章
- 深度分析:Android中Mms设置页面更改短信中心号码流程
相关控件初始化方法:showSmscPref private void showSmscPref() { int count = MSimTelephonyManager.getDef ...
- 【转载】深度解析Android中字体设置
原文:http://mobile.51cto.com/android-265238.htm 1.在Android XML文件中设置字体 可以采用Android:typeface,例如android:t ...
- Android系统自带APP分析——短信app
Android操作系统本身就是一个巨大的开源软件仓库,熟悉它既可以了解到Android系统的设计框架,也可以获得高效的应用程序编写方式.本文所分析的源码来自于Google官方的AOSP源码4.0.1_ ...
- android安全问题(八)伪造短信(利用原生android4.0漏洞)
导读:本文利用android4.0的一个原生漏洞来伪造短信.无须声明任何权限即可伪造发送方为任何号码的短信给用户. android4.0发布已经是很久很久很久很久以前的事情了,这个漏洞早就报了出来,之 ...
- Android设为系统默认的短信应用
要设为系统默认的短信应用首先要配置一下AndroidManifest.xml文件,添加下列: <!-- BroadcastReceiver that listens for incoming S ...
- android接收短信——framework处理流程(android 5.1)
modem层不懂,所以直接从RIL.java开始.以电信卡接收短信为例 modem通知RIL.java中的 RILReceiver处理接收信息 class RILReceiver implements ...
- Android黑科技,读取用户短信+修改系统短信数据库
安卓系统比起ios系统最大的缺点,相信大家都知道,就是系统安全问题.这篇博客就秀一波“黑科技”. 读取用户短信 Android应用能读取用户手机上的短信,相信已经不是什么新鲜事,比如我们收到的短信验证 ...
- 脚本控制向Android模拟拨打电话,发送短信,定位设置功能
做行为触发的时候要向模拟器实现拨打电话,发送短信,定位设置的的功能,可以很方便通过telnet localhost 5554实现. 写个脚本很快的搞定了.网上资料很多,脚本的很少,也所积点德啦. 写 ...
- Android面试收集录 电话、短信和联系人、多媒体技术
1.请写出调用系统拨号界面? Intent intent=new Intent(Intent.ACTION_DIAL,Uri.pase("tel:12345678910")); s ...
随机推荐
- js中 正則表達式
正則表達式使用具体解释 简单介绍 简单的说,正則表達式是一种能够用于模式匹配和替换的强有力的工具.其作用例如以下: 測试字符串的某个模式.比如,能够对一个输入字符串进行測试,看在该字符串是否存在一个电 ...
- LeetCode——Valid Sudoku
Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudoku board could be ...
- Android该HTTP下载
今天学习了Android开发中比較难的一个环节,就是断点续传下载,非常多人看到这个标题就感觉头大.的确,假设没有良好的逻辑思维,这块的确非常难搞明确.以下我就将自己学到的知识和一些见解写下供那些在这个 ...
- mysql按ID排序(转)
自己建表的时候,把一个字段类型创建为varchar(2) ,其实应该建为int(2)的. 因为我只允许输出数字.这本来也没什么,无非就是占点空间,懒得改了.但是今天在后台发现排序有问题.于是,没办法, ...
- coco2d-x 基于视口的地图设计
<pre name="code" class="plain"> 基于视口的地图设计 DionysosLai 2014-06-14 第三人称游戏,玩家 ...
- kb3035583
dism /online /Get-Packages /Format:Table|findstr 3035583 升级到w10补丁
- 【原创】leetCodeOj --- Binary Search Tree Iterator 解题报告
时间挤挤总是有的 太久不做题,脑子都生锈了.来道水题练练手 题目地址: https://leetcode.com/problems/binary-search-tree-iterator/ 题目内容: ...
- DP:树DP
The more, The Better Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth ...
- Sliverlight之 特效
1,OpacityMask控件的部分渐隐(见Project16) (1) 控件的OpacityMask有什么作用 说明:设置所选区域不透明度的画笔,一般结合LinearGradientBrush或Ra ...
- Thinkphp编辑器扩展类kindeditor用法
一, 使用前的准备. 使用前请确认你已经建立好了一个Thinkphp站点项目. 1,Keditor.class.php和JSON.class.php 是编辑器扩展类文件,将他们拷贝到你的站点项目的Th ...