深度分析: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);
}
至此,修改短信中心号码的整个流程都整理完了。