Uicc之CatService

来源:互联网 发布:淘宝刷好评买家封号 编辑:程序博客网 时间:2024/04/25 04:49

CatService主要负责STK菜单的相关事宜,是Stk架构在Framework层的核心类。
作用:
1,解析自RIL上报的SIM卡中的数据然后以广播的形式上报给Stk app,方法为handleCommand()
2,上层Stk app持有CatService实例ie,通过调用其onCmdResponse()方法向RIL传递用户操作。

一、CatService的创建过程
在前面我们分析过,在UiccCard的更新过程中,会初始化CatService对象:

UiccCard.java    public void update(Context c, CommandsInterface ci, IccCardStatus ics) {        synchronized (mLock) {            //[L51_porting] s: Angela            if (mDestroyed) {                loge("Updated after destroyed! Fix me!");                return;            }            //[L51_porting] e: Angela            CardState oldState = mCardState;            mCardState = ics.mCardState;            mUniversalPinState = ics.mUniversalPinState;            mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;            mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;            mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;            mContext = c;            mCi = ci;        ....            createAndUpdateCatService();            }    protected void createAndUpdateCatService() {        // [JB_422_PORTING] s: Mavis        if (mDestroyed) {            loge("createAndUpdateCatService after destroyed! Fix me!");            return;        }        // [JB_422_PORTING] e: Mavis        if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {            // Initialize or Reinitialize CatService            if (mCatService == null) {        //创建CatService                 mCatService = CatService.getInstance(mCi, mContext, this, mPhoneId);            } else {                ((CatService)mCatService).update(mCi, mContext, this);            }        } else {            if (mCatService != null) {                mCatService.dispose();            }            mCatService = null;        }    }

那什么时候更新UiccCard啦?这的从Phone应用启动开始说起。
Phone应用开机启动进入PhoneGlobals.onCreate()创建Phone对象

public void onCreate() {        if (VDBG) Log.v(LOG_TAG, "onCreate()...");        ContentResolver resolver = getContentResolver();        // Cache the "voice capable" flag.        // This flag currently comes from a resource (which is        // overrideable on a per-product basis):        sVoiceCapable =                getResources().getBoolean(com.android.internal.R.bool.config_voice_capable);        // ...but this might eventually become a PackageManager "system        // feature" instead, in which case we'd do something like:        // sVoiceCapable =        //   getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY_VOICE_CALLS);        if (mCM == null) {            // Initialize the telephony framework            PhoneFactory.makeDefaultPhones(this);    .... }
    public static void makeDefaultPhone(Context context) {    ....        // Instantiate UiccController so that all other classes can just        // call getInstance()        sUiccController = UiccController.make(context, sCommandsInterfaces);    ....   }

这里就会去创建UiccController对象,该对象只有一个

   public static UiccController make(Context c, CommandsInterface[] ci) {        synchronized (mLock) {            if (mInstance != null) {                throw new RuntimeException("MSimUiccController.make() should only be called once");            }            mInstance = new UiccController(c, ci);            return (UiccController)mInstance;        }    }
  private UiccController(Context c, CommandsInterface []ci) {        if (DBG) log("Creating UiccController");        mContext = c;        mCis = ci;        for (int i = 0; i < mCis.length; i++) {            Integer index = new Integer(i);            mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);            // TODO remove this once modem correctly notifies the unsols            // If the device has been decrypted or FBE is supported, read SIM when radio state is            // available.            // Else wait for radio to be on. This is needed for the scenario when SIM is locked --            // to avoid overlap of CryptKeeper and SIM unlock screen.            if (DECRYPT_STATE.equals(SystemProperties.get("vold.decrypt")) ||                    StorageManager.isFileEncryptedNativeOrEmulated()) {                mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);            } else {                mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);            }            mCis[i].registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, index);            mCis[i].registerForIccRefresh(this, EVENT_SIM_REFRESH, index);        }        mLauncher = new UiccStateChangedLauncher(c, this);    }

可以看到在创建UiccController的时候根据index的不同分别想不同的RIL实例中注册的监听,这里主要就是mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
之后在RIL收到Modem主动上报的SIM状态改变的消息后就会一层层上报:

RIL.java    private void    processUnsolicited (Parcel p, int type) {     int response;        Object ret;        response = p.readInt();     switch(response) {            case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED:                if (RILJ_LOGD) unsljLog(response);                if (mIccStatusChangedRegistrants != null) {                    mIccStatusChangedRegistrants.notifyRegistrants();                }                break;}

mIccStatusChangedRegistrants.notifyRegistrants()这里就会通知前面注册的UiccController

 public void handleMessage (Message msg) {        synchronized (mLock) {            Integer index = getCiIndex(msg);            if (index < 0 || index >= mCis.length) {                Rlog.e(LOG_TAG, "Invalid index : " + index + " received with event " + msg.what);                return;            }            AsyncResult ar = (AsyncResult)msg.obj;            switch (msg.what) {                case EVENT_ICC_STATUS_CHANGED:                    if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus, index=" + index);                    mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));                    break;            case EVENT_GET_ICC_STATUS_DONE:                    if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE, index=" + index);                    onGetIccCardStatusDone(ar, index);                    break;    }

调用mCis[index].getIccCardStatus()去获得卡的状态在回到case EVENT_GET_ICC_STATUS_DONE:

   private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) {        if (ar.exception != null) {            Rlog.e(LOG_TAG,"Error getting ICC status. "                    + "RIL_REQUEST_GET_ICC_STATUS should "                    + "never return an error", ar.exception);            return;        }        if (!isValidCardIndex(index)) {            Rlog.e(LOG_TAG,"onGetIccCardStatusDone: invalid index : " + index);            return;        }        IccCardStatus status = (IccCardStatus)ar.result;        if (mUiccCards[index] == null) {            //Create new card            // [L_50_PORTING] s: Mavis 20140923            // mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index);            mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index, mPhone[index]);            // [L_50_PORTING] e: Mavis 20140923        } else {            //Update already existing card            mUiccCards[index].update(mContext, mCis[index] , status);        }        if (DBG) log("Notifying IccChangedRegistrants");        mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));    }

这里就回到了开头说的UiccCard.update()会去创建CatService。
其中,Phone,RIL,UiccCard,CatService的实例数量与SIM卡数量相同,一一对应。UiccController只有一个,里面管理一个UiccCard数组。

二、CatService的消息机制
先来看看CatService的构造方法

    private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir,            Context context, IccFileHandler fh, UiccCard ic, int slotId) {        if (ci == null || ca == null || ir == null || context == null || fh == null                || ic == null) {            throw new NullPointerException(                    "Service: Input parameters must not be null");        }        mCmdIf = ci;        mContext = context;        mSlotId = slotId;        mHandlerThread = new HandlerThread("Cat Telephony service" + slotId);        mHandlerThread.start();        // Get the RilMessagesDecoder for decoding the messages.        mMsgDecoder = RilMessageDecoder.getInstance(this, fh, slotId);        if (null == mMsgDecoder) {            CatLog.d(this, "Null RilMessageDecoder instance");            return;        }        mMsgDecoder.start();        // Register ril events handling.        mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null);        mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null);        mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null);        mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null);        //mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null);        mCmdIf.registerForIccRefresh(this, MSG_ID_ICC_REFRESH, null);        mCmdIf.setOnCatCcAlphaNotify(this, MSG_ID_ALPHA_NOTIFY, null);        mIccRecords = ir;        mUiccApplication = ca;        // Register for SIM ready event.        mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null);        CatLog.d(this, "registerForRecordsLoaded slotid=" + mSlotId + " instance:" + this);        mUiccController = UiccController.getInstance();        mUiccController.registerForIccChanged(this, MSG_ID_ICC_CHANGED, null);        // Check if STK application is available        mStkAppInstalled = isStkAppInstalled();        // + HTC_PHONE        registerHtcReceiver(true, mContext);        // - HTC_PHONE        CatLog.d(this, "Running CAT service on Slotid: " + mSlotId +                ". STK app installed:" + mStkAppInstalled);    }

我们看到在CatService的初始化过程中主要完成了一下三个任务:
1、获取RilMessageDecoder对象mMsgDecoder;
2、向RIL注册相关的通知监听;
3、向UiccCardApplication、IccRecords注册SIM卡和Record的监听;

在上面的CatService构造函数中我们看到,CatService注册了六个主要的监听器,有四个是比较重要的,按照接收的先后顺序分别是:
MSG_ID_ICC_CHANGED —-在7.0以后取代了
MSG_ID_SIM_READY,由RIL发出,通知CatService,SIM卡已经就绪,需要CatService反馈是否就绪
MSG_ID_PROACTIVE_COMMAND —-CatService拿到SIM卡中的STK信息
MSG_ID_RIL_MSG_DECODED —-RilMessageDecoder解析完STK数据后给CatService发送通知
MSG_ID_SESSION_END —-RIL上传当前消息已经传输完毕

2.2、 MSG_ID_ICC_CHANGED

  @Override    public void handleMessage(Message msg) {        CatLog.d(this, "handleMessage[" + msg.what + "]");        switch (msg.what) {        case MSG_ID_ICC_CHANGED:            CatLog.d(this, "MSG_ID_ICC_CHANGED");            updateIccAvailability();            break;    }}
    void updateIccAvailability() {        if (null == mUiccController) {            return;        }        CardState newState = CardState.CARDSTATE_ABSENT;        UiccCard newCard = mUiccController.getUiccCard(mSlotId);        if (newCard != null) {            newState = newCard.getCardState();        }        CardState oldState = mCardState;        mCardState = newState;        CatLog.d(this,"New Card State = " + newState + " " + "Old Card State = " + oldState);        if (oldState == CardState.CARDSTATE_PRESENT &&                newState != CardState.CARDSTATE_PRESENT) {            broadcastCardStateAndIccRefreshResp(newState, null);        } else if (oldState != CardState.CARDSTATE_PRESENT &&                newState == CardState.CARDSTATE_PRESENT) {            // Card moved to PRESENT STATE.            mCmdIf.reportStkServiceIsRunning(null);        }    }

在这里会判断当前SIM卡的状态,如果SIM卡不是PRESENT就会发出广播broadcastCardStateAndIccRefreshResp(newState, null),如果是PRESENT就调用 mCmdIf.reportStkServiceIsRunning(null),RIL将会向Modem发送RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING的消息,从而告诉Modem,CatService已经准备好接受STK的数据了。

2.2、MSG_ID_PROACTIVE_COMMAND
Modem接收到CatService已经启动的消息后,就会上报MSG_ID_PROACTIVE_COMMAND的消息,该消息中包含了SIM卡中存储的STK信息,此时CatService需要将这些信息解析并保存,用于构建STK菜单。

   public void handleMessage(Message msg) {          switch (msg.what) {              case MSG_ID_SESSION_END:              case MSG_ID_PROACTIVE_COMMAND:              case MSG_ID_EVENT_NOTIFY:              case MSG_ID_REFRESH:                  String data = null;                  if (msg.obj != null) {                      AsyncResult ar = (AsyncResult) msg.obj;                      if (ar != null && ar.result != null) {                          try {                              data = (String) ar.result;                          } catch (ClassCastException e) {                              break;                          }                      }                  }                  //通过RilMessageDecoder去解析数据                  mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data));                  break;              default:                  throw new AssertionError("Unrecognized CAT command: " + msg.what);          }      }  

我们看到,对于拿到的数据时通过mMsgDecoder去解析的,也就是RilMessageDecoder的对象,这个对象是在CatService的构造函数中被创建的。
当数据解析完成后CatService中将会收到MSG_ID_RIL_MSG_DECODED的消息。

2.3、MSG_ID_RIL_MSG_DECODED
这个消息并不是从Ril上传上来的,而是在6.2.1中,对MSG_ID_PROACTIVE_COMMAND消息处理之后,由RilMessageDecoder发送过来的,它标志着刚才从Modem接收到的数据已经被解析完毕。而这个消息要做的任务就是将解析后的数据传递给RIL和StkAppService.java。

    @CatService.java      public void handleMessage(Message msg) {          switch (msg.what) {              case MSG_ID_RIL_MSG_DECODED:                  handleRilMsg((RilMessage) msg.obj);                  break;               default:                  throw new AssertionError("Unrecognized CAT command: " + msg.what);          }      }  

接着往下看:

private void handleRilMsg(RilMessage rilMsg) {      CommandParams cmdParams = null;      switch (rilMsg.mId) {          case MSG_ID_PROACTIVE_COMMAND:              try {                  //拿到解析后的数据                  cmdParams = (CommandParams) rilMsg.mData;              } catch (ClassCastException e) {              }              if (cmdParams != null) {                  if (rilMsg.mResCode == ResultCode.OK) {                      //处理当前的数据                      handleCommand(cmdParams, true);                  } else {                      sendTerminalResponse(cmdParams.mCmdDet, rilMsg.mResCode, false, 0, null);                  }              }              break;      }  }  
private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) {      CharSequence message;      CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams);      switch (cmdParams.getCommandType()) {          case SET_UP_MENU:              if (removeMenu(cmdMsg.getMenu())) {                  mMenuCmd = null;              } else {                  mMenuCmd = cmdMsg;              }              sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);              break;          case DISPLAY_TEXT:              if (!cmdMsg.geTextMessage().responseNeeded) {                  sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null);              }              break;          case SELECT_ITEM:          case GET_INPUT:          case GET_INKEY:              break;          case SEND_DTMF:          case SEND_SMS:          case SEND_SS:          case SEND_USSD:              if ((((DisplayTextParams)cmdParams).mTextMsg.text != null) && (((DisplayTextParams)cmdParams).mTextMsg.text.equals(STK_DEFAULT))) {                  message = mContext.getText(com.android.internal.R.string.sending);                  ((DisplayTextParams)cmdParams).mTextMsg.text = message.toString();              }              break;          case PLAY_TONE:              break;          case SET_UP_CALL:              if ((((CallSetupParams) cmdParams).mConfirmMsg.text != null) && (((CallSetupParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) {                  message = mContext.getText(com.android.internal.R.string.SetupCallDefault);                  ((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString();              }              break;          default:              CatLog.d(this, "Unsupported command");              return;      }      mCurrntCmd = cmdMsg;      Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);      intent.putExtra("STK CMD", cmdMsg);      mContext.sendBroadcast(intent);  

我们看到,在handleCommand()中,对于主要的几种数据类型,比如SET_UP_MENU、DISPLAY_TEXT的最后都有sendTerminalResponse()的操作,而这个操作的作用就是将数据封装后发送给RIL。并且在handleCommand()的最后,通过广播mContext.sendBroadcast(intent);的形式将当前解析的结果发送出来,发送之后就有STK应用的StkAppService.java负责接收,并构建STK菜单。

2.4、MSG_ID_SESSION_END
这个消息主要是RIL用于告诉CatService,当前的会话已经传输完毕,和MSG_ID_PROACTIVE_COMMAND的消息流程类似,只是更加的简单,略。

至此,CatService的主要作用分析完毕!

原创粉丝点击