Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 深度分析:Android中Mms設置頁面更改短信中心號碼流程

深度分析:Android中Mms設置頁面更改短信中心號碼流程

編輯:關於Android編程

相關控件初始化方法: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) {
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構造函數中傳遞過來的,因此我們代碼中找到了該實例為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);
}

至此,修改短信中心號碼的整個流程都整理完了。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved