深度解析: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 ...
随机推荐
- 第三章 AOP 基于@AspectJ的AOP
在前面,我们分别使用Pointcut.Advice.Advisor接口来描述切点.增强.切面.而现在我们使用@AdpectJ注解来描述. 在下面的例子中,我们是使用Spring自动扫描和管理Bean. ...
- JDBC连接数据库经验技巧(转)
Java数据库连接(JDBC)由一组用 Java 编程语言编写的类和接口组成.JDBC 为工具/数据库开发人员提供了一个标准的 API,使他们能够用纯Java API 来编写数据库应用程序.然而各个开 ...
- client对象层次和0级DOM
刚開始学了两天JS,闲着无聊,顺手画了张图
- 树形dp专辑
hdu 2196 http://acm.hdu.edu.cn/showproblem.php?pid=2196 input 5//5个结点 1 1//表示结点2到结点1有一条权值为1的边 2 1//表 ...
- OGG-01008 Extract displays Discarding bad record (discard recs=1) when using filter or where clause
因为在extract參数文件里使用了where语句,而where后面的的条件列又不是主键,没有为update.delete操作记录日志,因此会报1008错误. Applies to: Oracle G ...
- FusionCharts简单教程---建立第一个FusionCharts图形
由于项目需求需要做一个报表,选择FusionCharts作为工具使用.由于以前没有接触过报表,网上也没有比较详细的fusionCharts教程,所以决定好好研究FusionCharts,同时做一个比较 ...
- WPF 图片浏览 伪3D效果
原文:WPF 图片浏览 伪3D效果 首先上效果图: 因项目要求,需要把图片以"好看"."炫"的效果展示出来,特地研究了一下WPF关于3D方面的制作,奈何最终成果 ...
- 几种流行Webservice控制框架
转会[http://blog.csdn.net/thunder4393/article/details/5787121],写的非常好,以收藏. 1 摘要 开发webservice应用程序中 ...
- 对XSD schema文件中elementFormDefault属性的理解
Schema中的elementFormDefault elementFormDefault取值:qualified 或者 unqualified 在http://www.velocityreviews ...
- C3P0具体的配置说明(com.mchange.v2.c3p0.ComboPooledDataSource)
C3P0它是一个开源JDBC连接池,它lib文件夹和Hibernate一起公布,包含了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对 ...