zoukankan      html  css  js  c++  java
  • 深度分析: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);
        }

    至此,修改短信中心号码的整个流程都整理完了。

  • 相关阅读:
    C++中类模板的概念和意义
    欢迎访问新博客aiyoupass.com
    P2327
    P2885
    P1968
    Link-Cut-Tree
    树的重心
    点分治笔记
    SPOJ 375
    树链剖分
  • 原文地址:https://www.cnblogs.com/kevincode/p/3837884.html
Copyright © 2011-2022 走看看