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);
        }

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

  • 相关阅读:
    无法重用Linq2Entity Query
    The Joel Test
    MSBuilder directly instead of default VSComplie with keyborad shotcut 原创
    客户端缓存(Client Cache)
    关于代码重构和UT的一些想法,求砖头
    ExtJS2.0实用简明教程 应用ExtJS
    Perl information,doc,module document and FAQ.
    使用 ConTest 进行多线程单元测试 为什么并行测试很困难以及如何使用 ConTest 辅助测试
    史上最简单的Hibernate入门简介
    汽车常识全面介绍 传动系统
  • 原文地址:https://www.cnblogs.com/kevincode/p/3837884.html
Copyright © 2011-2022 走看看