深度解析: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 ...
随机推荐
- hash在Coreseek 中配置bigint
304 $sphinxapi->SetSelect('id, domain_hash'); 304 $sphinxapi->SetConnectTimeout(3); 305 $sphin ...
- PDO进行sql报表编制结果集介绍及操作(两)
<span style="font-size:18px;">一个:运行准备好的语句和绑定参数insert try { $pdo=new PDO("mysql: ...
- MVC模块化架构
全面解析ASP.NET MVC模块化架构方案 什么叫架构?揭开架构神秘的面纱,无非就是:分层+模块化.任意复杂的架构,你也会发现架构师也就做了这两件事. 本文将会全面的介绍我们团队在模块化设计方面取得 ...
- Windows Phone开发(26):启动器与选择器之MediaPlayerLauncher和SearchTask
原文:Windows Phone开发(26):启动器与选择器之MediaPlayerLauncher和SearchTask 启动器与选择器简单的地方在于,它们的使用方法几乎一模一样,从前面几节中,我相 ...
- lambda 3
.NET笔记系列:LAMBDA表达式常用写法 这里主要是将数据库中的常用操作用LAMBDA表达式重新表示了下,用法不多,但相对较常用,等有时间了还会扩展,并将查询语句及LINQ到时也一并重新整理下 ...
- With As 获取 id parentId 递归获取所有
Declare @Id Int Set @Id = 5; ---在此修改父节点 With RootNodeCTE(Id,ParentId) As ( Select Id,Paren ...
- 重新想象 Windows 8 Store Apps (11) - 控件之 ListView 和 GridView
原文:重新想象 Windows 8 Store Apps (11) - 控件之 ListView 和 GridView [源码下载] 重新想象 Windows 8 Store Apps (11) - ...
- Javascript设计模式系列一
定义类,方法赋值给prototype属性各种写法: 方法一: <script type="text/javascript"> ////定义Message类 var Me ...
- JSP+Ajax站点开发小知识
一.JSP基础 1.<select name="love" size="3">当中的size属性指定了列表框显示选项的条数.假设全部选项多于这个 ...
- HTML介绍JS
首先,该脚本的链接插入HTML代码: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU2h1aVRpYW5OYWlMdW8=/font/5a6L5L2T/f ...