(M)SIM卡开机流程分析之默认APN设置

来源:互联网 发布:java socket异步接收 编辑:程序博客网 时间:2024/05/17 08:51
近日,一直在研究,默认APN的设置
当我们从代码和手机中看到,默认APN的显示是从content://telephony/carriers/preferapn的数据中查询到的,而这个是通过shared preference来保存的,当手机第一次插卡开机后,我们将preferapn.xml文件导出来后,我们发现其中已经写有值了,但是从TelephonyProvider.Java文件中,我们却没有发现任何写入的地方,那么这个默认APN究竟是如何写入和设定的呢?
结合LOG查看了插卡开机流程多日后,终于找到了其写入的位置,仅以此篇记录近期的成果。

首先,SIM卡的开机流程中,我们知道,是从PhoneFactory.java文件的makeDefaultPhone方法中开始的,而在其中,我们可以看到如下段

for (int i = 0; i < numPhones; i++) {          PhoneBase phone = null;          int phoneType = TelephonyManager.getPhoneType(networkModes[i]);          if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {              phone = new GSMPhone(context,                      sCommandsInterfaces[i], sPhoneNotifier, i);          } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {              phone = new CDMALTEPhone(context,                      sCommandsInterfaces[i], sPhoneNotifier, i);          }          Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);                sProxyPhones[i] = new PhoneProxy(phone);      }
创建Phone对象,今天我们就以GSMPhone对象来分析这个默认APN的流程

public      GSMPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, int phoneId) {          this(context, ci, notifier, false, phoneId);  }public      GSMPhone(Context context, CommandsInterface ci,              PhoneNotifier notifier, boolean unitTestMode, int phoneId) {          super("GSM", notifier, context, ci, unitTestMode, phoneId);                if (ci instanceof SimulatedRadioControl) {              mSimulatedRadioControl = (SimulatedRadioControl) ci;          }                mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);          mCT = new GsmCallTracker(this);                mSST = new GsmServiceStateTracker(this);          mDcTracker = new DcTracker(this);                if (!unitTestMode) {              mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);              mSubInfo = new PhoneSubInfo(this);          }                mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);          mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);          mCi.registerForOn(this, EVENT_RADIO_ON, null);          mCi.setOnUSSD(this, EVENT_USSD, null);          mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);          mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);          mCi.setOnSs(this, EVENT_SS, null);          setProperties();                log("GSMPhone: constructor: sub = " + mPhoneId);                setProperties();  }
其中,有创建DcTracker对象,查看这个类的构造方法

//***** Constructor      public DcTracker(PhoneBase p) {          super(p);          ......          update();          ......  }
首先,先调用了其父类的构造方法

/**       * Default constructor       */      protected DcTrackerBase(PhoneBase phone) {          super();          mPhone = phone;          ......          mUiccController = UiccController.getInstance();          mUiccController.registerForIccChanged(this, DctConstants.EVENT_ICC_CHANGED, null);          ......      } 
再继续回到DcTracker的构造方法中,可以看到还调用了update方法,进入update方法中查看

public void update() {          ......          registerForAllEvents();          onUpdateIcc();          ......      } 
进入onUpdateIcc方法

@Override      protected void onUpdateIcc() {          ......          IccRecords newIccRecords = getUiccRecords(UiccController.APP_FAM_3GPP);                IccRecords r = mIccRecords.get();          if (r != newIccRecords) {              ......              if (newIccRecords != null) {                  if (mPhone.getSubId() >= 0) {                      ......                      mIccRecords.set(newIccRecords);                      newIccRecords.registerForRecordsLoaded(                              this, DctConstants.EVENT_RECORDS_LOADED, null);                  }              }              ......          }  }
通过IccRecords注册DctConstants.EVENT_RECORDS_LOADED消息,当全部加载完成后,发出EVENT_RECORDS_LOADED消息,在DcTracker中接收,并处理

@Override      public void handleMessage (Message msg) {          ......                switch (msg.what) {              case DctConstants.EVENT_RECORDS_LOADED:                  onRecordsLoaded();                  break;          }      } 
private void onRecordsLoaded() {          ......          createAllApnList();          setInitialAttachApn();          if (mPhone.mCi.getRadioState().isOn()) {              if (DBG) log("onRecordsLoaded: notifying data availability");              notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED);          }          setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);  }
再查看createAllApnList方法

/**      * Based on the sim operator numeric, create a list for all possible      * Data Connections and setup the preferredApn.      */      private void createAllApnList() {          mAllApnSettings = new ArrayList<ApnSetting>();          IccRecords r = mIccRecords.get();          String operator = (r != null) ? r.getOperatorNumeric() : "";          if (operator != null) {              String selection = "numeric = '" + operator + "'";              String orderBy = "_id";              // query only enabled apn.              // carrier_enabled : 1 means enabled apn, 0 disabled apn.              // selection += " and carrier_enabled = 1";              if (DBG) log("createAllApnList: selection=" + selection);                    Cursor cursor = mPhone.getContext().getContentResolver().query(                      Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);                    if (cursor != null) {                  if (cursor.getCount() > 0) {                      mAllApnSettings = createApnList(cursor);                  }                  cursor.close();              }          }                addEmergencyApnSetting();                dedupeApnSettings();                if (mAllApnSettings.isEmpty()) {              if (DBG) log("createAllApnList: No APN found for carrier: " + operator);              mPreferredApn = null;              // TODO: What is the right behavior?              //notifyNoData(DataConnection.FailCause.MISSING_UNKNOWN_APN);          } else {              mPreferredApn = getPreferredApn();              if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) {                  mPreferredApn = null;                  setPreferredApn(-1);              }              if (DBG) log("createAllApnList: mPreferredApn=" + mPreferredApn);          }          if (DBG) log("createAllApnList: X mAllApnSettings=" + mAllApnSettings);                setDataProfilesAsNeeded();  }
可以看到,在这个方法中,主要是通过当前SIM卡的numeric字段,从content://telephony/carriers数据库中获取所有numeric字段对应的APN信息(数据库信息是从哪儿来的,请查看TelephonyProvider.java文件),并且将所有的数据均插入mAllApnSettings中,在这段代码下方可以看到,还有获取preferred apn的数据,由于此前并未设定,因此此处无法获取到
继续查看setupDataOnConnectableApns方法
private void setupDataOnConnectableApns(String reason) {          setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);  }
private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {          ......          for (ApnContext apnContext : mPrioritySortedApnContexts) {              ArrayList<ApnSetting> waitingApns = null;              ......              if (apnContext.getState() == DctConstants.State.FAILED                      || apnContext.getState() == DctConstants.State.RETRYING) {                  ......                      // RetryFailures.ONLY_ON_CHANGE - check if the apns have changed                      int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();                      ArrayList<ApnSetting> originalApns = apnContext.getOriginalWaitingApns();                      if (originalApns != null && originalApns.isEmpty() == false) {                          waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);                          if (originalApns.size() != waitingApns.size() ||                                  originalApns.containsAll(waitingApns) == false) {                              apnContext.releaseDataConnection(reason);                          } else {                              continue;                          }                      } else {                          continue;                      }                  ......              }              if (apnContext.isConnectable()) {                  log("setupDataOnConnectableApns: isConnectable() call trySetupData");                  apnContext.setReason(reason);                  trySetupData(apnContext, waitingApns);              }          }  }
这边有一个buildWaitingApns方法和trySetupData方法比较重要,我们继续分析这段
/**      * Build a list of APNs to be used to create PDP's.      *      * @param requestedApnType      * @return waitingApns list to be used to create PDP      *          error when waitingApns.isEmpty()      */      private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType, int radioTech) {          ......          ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();          ......          IccRecords r = mIccRecords.get();          String operator = (r != null) ? r.getOperatorNumeric() : "";                ......          boolean usePreferred = true;          try {              // 返回usePreferred的值为true              usePreferred = ! mPhone.getContext().getResources().getBoolean(com.android.                      internal.R.bool.config_dontPreferApn);          } catch (Resources.NotFoundException e) {              if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");              usePreferred = true;          }          // 我们知道此地,一样,无法取得值,返回mPreferredApn的值为null          if (usePreferred) {              mPreferredApn = getPreferredApn();          }          if (DBG) {              log("buildWaitingApns: usePreferred=" + usePreferred                      + " canSetPreferApn=" + mCanSetPreferApn                      + " mPreferredApn=" + mPreferredApn                      + " operator=" + operator + " radioTech=" + radioTech                      + " IccRecords r=" + r);          }                ......          if (mAllApnSettings != null) {              ......              // 刚刚通过createAllApnList方法获取到的值              for (ApnSetting apn : mAllApnSettings) {                  if (apn.canHandleType(requestedApnType)) {                      if (ServiceState.bitmaskHasTech(apn.bearerBitmask, radioTech)) {                          if (DBG) log("buildWaitingApns: adding apn=" + apn);                          apnList.add(apn);                      } else {                          if (DBG) {                              log("buildWaitingApns: bearerBitmask:" + apn.bearerBitmask + " does " +                                      "not include radioTech:" + radioTech);                          }                      }                  } else if (DBG) {                      log("buildWaitingApns: couldn't handle requesedApnType="                              + requestedApnType);                  }              }          } else {              loge("mAllApnSettings is null!");          }          if (DBG) log("buildWaitingApns: X apnList=" + apnList);          return apnList;  }
返回一个apnList,这个list是从mAllApnSettings中取到的,而其判断条件为该ApnSetting中是否包含需要的apnType,和是否符合我们的需要
再查看trySetupData方法,其是将刚刚的buildWaitingApns返回的apnList传入的
private boolean trySetupData(ApnContext apnContext, ArrayList<ApnSetting> waitingApns) {          ......                    apnContext.setWaitingApns(waitingApns);                ......          boolean retValue = setupData(apnContext, radioTech);          notifyOffApnsOfAvailability(apnContext.getReason());          ......  }
再看setupData方法

private boolean setupData(ApnContext apnContext, int radioTech) {          ......          ApnSetting apnSetting;          DcAsyncChannel dcac = null;                // 直接获取mWaitingApn List中的第一个ApnSetting          apnSetting = apnContext.getNextWaitingApn();          ......                apnContext.setDataConnectionAc(dcac);          apnContext.setApnSetting(apnSetting);          apnContext.setState(DctConstants.State.CONNECTING);          mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());                Message msg = obtainMessage();          msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;          msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);          dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech,                  mAutoAttachOnCreation.get(), msg, generation);                if (DBG) log("setupData: initing!");          return true;  }
创建DataConnection,并设置其参数,设置完成后发送EVENT_DATA_SETUP_COMPLETE消息,在其父类DcTrackerBase.java文件中接收,并处理

@Override      public void handleMessage(Message msg) {          switch (msg.what) {              ......              case DctConstants.EVENT_DATA_SETUP_COMPLETE:                  onDataSetupComplete((AsyncResult) msg.obj);                  break;          }      } 
在DcTracker的onDataSetupComplete方法中进行处理

/**      * A SETUP (aka bringUp) has completed, possibly with an error. If      * there is an error this method will call {@link #onDataSetupCompleteError}.      */      @Override      protected void onDataSetupComplete(AsyncResult ar) {                DcFailCause cause = DcFailCause.UNKNOWN;          boolean handleError = false;          ApnContext apnContext = getValidApnContext(ar, "onDataSetupComplete");                ......                if (ar.exception == null) {              DcAsyncChannel dcac = apnContext.getDcAc();                    ......                                ApnSetting apn = apnContext.getApnSetting();                  ......                        // everything is setup                  if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {                      SystemProperties.set(PUPPET_MASTER_RADIO_STRESS_TEST, "true");                      if (mCanSetPreferApn && mPreferredApn == null) {                          if (DBG) log("onDataSetupComplete: PREFERED APN is null");                          mPreferredApn = apn;                          if (mPreferredApn != null) {                              setPreferredApn(mPreferredApn.id);                          }                      }                  }              ......          }          ......  }

可以看到,preferred apn就是在此处进行第一次设定的,而ApnSetting就是我们之前在buildWaitingApn中获取到的apnList的第一个ApnSetting
好了,到此结束
    仅以此篇暂时记录目前的研究成果,若有错误,后续改正

0 0