安卓开机主卡选择流程(展讯4.4)

简介: 笔记

安卓4.4原生并不支持双卡,展讯4.4添加了双卡功能。

下面整理一下应用层的开机双卡选择流程:

Telephony开始


packages/services/Telephony/src/com/sprd/phone/BootCompletedReceiver.java

接收modem广播,判断是否是双卡

<receiver android:name="com.sprd.phone.BootCompletedReceiver" >
     <intent-filter>
         <action android:name="com.android.modemassert.MODEM_STAT_CHANGE" />
     </intent-filter>
 </receiver>

if (!TelephonyManager.isMultiSim()) {  //isMultiSim()也是自定义的方法,结尾处有详述
    // SPRD: modify for bug369560
    if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
        //SIM卡状态
        String state = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
        //phoneId,0或1
        int phoneId = intent.getIntExtra(IccCardConstants.INTENT_KEY_PHONE_ID, 0);
        if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(state) ||
                IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state) ||
                IccCardConstants.INTENT_VALUE_ICC_READY.equals(state)) {
            mSimStateChangedFlag |= (1 << phoneId);
            Message simMsg = new Message();
            simMsg.what = EVENT_SIM_STATE_CHANGED;
            simMsg.arg1 = mSimStateChangedFlag;
            mHandler.sendMessage(simMsg);
        }
    }
    return;
}

由mHandler处理

private static final String SHOW_SELECT_PRIMARY_CARD_DIALOG = "android.intent.action.SHOW_SELECT_PRIMARY_CARD_DIALOG";
Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      int phoneCount = TelephonyManager.getPhoneCount();
      switch(msg.what){
            case EVENT_SELECTCARD:
           boolean isSimPresent = false;
           for(int i =0;i<phoneCount;i++){
               if(PhoneFactory.getSimState(i) != State.ABSENT){
                   isSimPresent = true;
                   break;
               }
           }
           if (isSimPresent) {
               System.putInt(mContext.getContentResolver(),
               System.Standby_Select_Card_Show, 1);
               Intent it = new Intent(mContext, StandbyDialogActivity.class);
              //提示SIM卡准备好
               it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
               mContext.startActivity(it);
           }
           break;
    case EVENT_CHARCHANGED:
        //提醒主卡改变
         if (isAllSimChecked() && !mHasPromptCardChanged){
             if (checkCard(mContext)) {
                 mHasPromptCardChanged = true;
                 Intent it = new Intent(mContext, PromptCardChanged.class);
                 it.putExtra(PromptCardChanged.SIM_CHANGE_STATUS, sim_change_status);
                 it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                mContext.startActivity(it);
             }
         }
         break;
        case EVENT_SIM_STATE_CHANGED:  //SIM卡状态改变
        boolean isSimsChanged = false;
        for (int i = 0; i < mPhoneNumber; i++) {
          String lastIccId = preferences.getString(SIM_ICC_ID + i, null);
          String newIccId = getIccId(i);
          if (newIccId == null || !newIccId.equals(lastIccId)) {
            isSimsChanged = true;  //其实也是判断iccid
          }
        }
      if (isSimsChanged) {
        mRadioTaskManager.savePreferredNetworkType(TelephonyManager.NT_UNKNOWN);
        //这里开始双卡选择
        setMultiModeSlotAccordingToPolicy();
      } else {
        int primaryCard = TelephonyManager.from(mContext).getPrimaryCard();
        if (!SimManager.isValidPhoneId(primaryCard)) {
          setMultiModeSlotAccordingToPolicy();  //SIM卡状态没变化 ,但是主卡验证失败也会重新选择
        } else {
          mRadioTaskManager.setDefaultDataPhoneIdNeedUpdate(false);
          mRadioTaskManager.manualSetPrimaryCard(primaryCard);
      }
  }
}
private void setMultiModeSlotAccordingToPolicy() {
      Settings.Secure.putInt( mContext.getContentResolver(),Settings.Secure.SERVICE_PRIMARY_CARD, -1);
    SimManager simManager = SimManager.get(mContext);
    if(simManager == null){
        Log.d(TAG,"simManager = "+null);
        return;
    }
    final Sim[] sims = simManager.getSims();
    if (mPolicy.isPrimaryCardNeedManualSet() && sims.length > 1) {
        prepareForMultiModeSlotChooseDialog();  //如果有两个卡进行双卡选择
    } else {
        mRadioTaskManager.autoSetPrimaryCardAccordingToPolicy();
        String operatorName = SystemProperties.get("ro.operator", "");
        if ("reliance".equals(operatorName)) {//这应该是一种特定运营商,不太清楚
            prepareForMultiModeSlotChooseDialog();  
        }
    }
}
//发送广播进行主卡选择,由Settings里面接收
private void prepareForMultiModeSlotChooseDialog() {
    Intent intent = new Intent(SHOW_SELECT_PRIMARY_CARD_DIALOG);
    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    mContext.sendBroadcast(intent);
}

Settings


src/com/android/settings/sim/SimSelectNotification.java

<receiver android:name=".sim.SimSelectNotification">
    <intent-filter>
        <action android:name="android.intent.action.SIM_STATE_CHANGED"></action>
        <action android:name="android.intent.action.SHOW_SELECT_PRIMARY_CARD_DIALOG"></action>
    </intent-filter>
</receiver>

public void onReceive(Context context, Intent intent) {
    /* SPRD: [Bug512963] Add for selecting primary card after boot with SIM card changed. @{ */
    if (PRIMARY_CARD_SELECTION_DIALOG.equals(intent.getAction())) {
        Log.d(TAG, "receive broadcast : SHOW_SELECT_PRIMARY_CARD_DIALOG");
        //其实是打开SimDialogActivity
        Intent targetIntent = new Intent(context, SimDialogActivity.class);
        targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        targetIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
        //弹出双卡选择,设置参数
        targetIntent.putExtra(SimDialogActivity.DIALOG_TYPE_KEY, SimDialogActivity.PRIMARY_PICK);
        targetIntent.putExtra(SimDialogActivity.PRIMARYCARD_PICK_CANCELABLE, true);
        context.startActivity(targetIntent);
    }
    /* @} */
}

src/com/android/settings/sim/SimDialogActivity.java

//在onCreate和onNewIntent方法中调用processIntent();
processIntent();
private void processIntent() {
    final Bundle extras = getIntent().getExtras();
    if (extras == null) {
        Log.e(TAG, "invalid extras null");
        finish();
        return;
    }
    final int dialogType = extras.getInt(DIALOG_TYPE_KEY, INVALID_PICK);
    mIsPrimaryCardCancelable = extras.getBoolean(PRIMARYCARD_PICK_CANCELABLE);
    switch (dialogType) {
        case DATA_PICK:
        case CALLS_PICK:
        case SMS_PICK:
        case PRIMARY_PICK:
            //创建Dialog并显示
            mDialogType = dialogType;
            mSimChooseDialog = createDialog(this, mDialogType);
            mSimChooseDialog.show();
            break;
          case PREFERRED_PICK:
              displayPreferredDialog(extras.getInt(PREFERRED_SIM));
              break;
        default:
            throw new IllegalArgumentException("Invalid dialog type " + dialogType + " sent.");
    }
}

再看一下createDialog方法,逻辑就很简单了:

public Dialog createDialog(final Context context, final int id) {
    dismissSimChooseDialog();
    final ArrayList<String> list = new ArrayList<String>();
    final Sim[] subInfoList = mSimManager.getActiveSims();
    final int selectableSubInfoLength = subInfoList == null ? 0 : subInfoList.length;
    final StatusBarManager statusBarManager = (StatusBarManager) getSystemService(Context.STATUS_BAR_SERVICE);
    final Sim[] subInfoListForCallAndSms = new Sim[selectableSubInfoLength + 1];
    final DialogInterface.OnClickListener selectionListener =
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int value) {
                    // SPRD: modify by add radioButton on set defult sub id
                    setDefaltSubIdByDialogId(context, id, value, subInfoList);
                }
            };
            Dialog.OnKeyListener keyListener = new Dialog.OnKeyListener() {
                @Override
                public boolean onKey(DialogInterface arg0, int keyCode,
                        KeyEvent event) {
                    /* SPRD: add option for selecting primary card @{ */
                    if (keyCode == KeyEvent.KEYCODE_BACK
                            && !mIsPrimaryCardCancelable) {
                        finish();
                        return true;
                    } else if (keyCode == KeyEvent.KEYCODE_BACK
                            && mIsPrimaryCardCancelable) {
                        return true;
                    } else {
                        return false;
                    }
                    /* @} */
                }
            };
    if (id == CALLS_PICK || id == SMS_PICK) {
        list.add(getResources().getString(R.string.sim_calls_ask_first_prefs_title));
        for (int i = 1; i < subInfoListForCallAndSms.length; i++) {
                subInfoListForCallAndSms[i] = subInfoList[i-1];
        }
    }
    for (int i = 0; i < selectableSubInfoLength; ++i) {
        final Sim sir = subInfoList[i];
        CharSequence displayName = sir.getName();
        if (displayName == null) {
            displayName = "";
        }
        list.add(displayName.toString());
    }
    String[] arr = list.toArray(new String[0]);
  AlertDialog.Builder builder = new AlertDialog.Builder(context,
          AlertDialog.THEME_HOLO_LIGHT);
  ListAdapter adapter = new SelectAccountListAdapter(
          (id == CALLS_PICK || id == SMS_PICK)? subInfoListForCallAndSms: subInfoList,
          builder.getContext(),
          R.layout.select_account_list_item,
          arr, id);
  switch (id) {
      case DATA_PICK:
          builder.setTitle(R.string.select_sim_for_data);
          break;
      case CALLS_PICK:
          builder.setTitle(R.string.select_sim_for_calls);
          break;
      case SMS_PICK:
          builder.setTitle(R.string.sim_card_select_title);
          break;
      /* SPRD: add option of selecting primary card @{ */
      case PRIMARY_PICK:
          /* SPRD: add for bug 543820 @{ */
          View titleView = LayoutInflater.from(this).inflate(
                  R.layout.select_primary_card_title, null);
          TextView textview = (TextView) titleView
                  .findViewById(R.id.multi_mode_slot_introduce);
           if (TelephonyManager.isDeviceSupportLte()) {
          textview.setText(getString(R.string.select_primary_slot_description_4g));
            }
           textview.setTextColor(Color.BLACK);
           builder.setCustomTitle(titleView);
           /* @} */
           break;
       /* @} */
       default:
           throw new IllegalArgumentException("Invalid dialog type "
                   + id + " in SIM dialog.");
   }               
   Dialog dialog = builder.setAdapter(adapter, selectionListener).create();
   dialog.setOnKeyListener(keyListener);
   dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
       @Override
       public void onCancel(DialogInterface dialogInterface) {
           finish();
       }
   });
    /* SPRD: add option of selecting primary card @{ */
    if (mIsPrimaryCardCancelable) {
        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                statusBarManager.disable(StatusBarManager.DISABLE_NONE);
            }
        });
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        dialog.setCanceledOnTouchOutside(false);
        statusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
    }
    /* @} */
    return dialog;
}

TelephonyManager.isMultiSim()


frameworks/base/telephony/java/android/telephony/TelephonyManager.java

/** SPRD: check if is multiple sim */
public static boolean isMultiSim() {
    return SprdPhoneSupport.isMultiSim(); //调用了SprdSupport.java的方法
}

frameworks/base/telephony/java/android/telephony/SprdPhoneSupport.java

//其实是判断的SIM卡插入的个数
 public static boolean isMultiSim() {
     return getPhoneCount() > 1;
 }
public static final int DEFAULT_PHONE_COUNT = 1;
//获取SIM卡个数
public static int getPhoneCount() {
    return SystemProperties.getInt("persist.msms.phone_count", DEFAULT_PHONE_COUNT);
}

所以最终修改的是persist.msms.phone_count的属性值,那么它是在哪里被修改的?看下面

frameworks/base/telephony/java/android/telephony/TelephonyManager.java

//检查系统属性
public static void checkSystemProperties(Context context){
    if(isSystemPropertiesInvalid(context,PROP_PHONE_COUNT)){
        //设置persist.msms.phone_count属性,属性值通过getSystemString获取
        SystemProperties.set(PROP_PHONE_COUNT, getSystemString(context, PROP_PHONE_COUNT));
    }
   ...
    }
//判断属性的合法性
private static boolean isSystemPropertiesInvalid(Context context, String string) {
    Log.d(TAG, "isSystemPropertiesInvalid: string=" + string
            + ", getSystemString=" + getSystemString(context, string)
            + ", getSystemProperties=" + SystemProperties.get(string, ""));
    //属性不为空,且系统属性值与prop文件内的属性值不相等。
    //因为默认persist.msms.phone_count=2,如果只插一张卡(getSytemString为1)就需要修改了
    return (!TextUtils.isEmpty(getSystemString(context, string)) && (!getSystemString(context,
            string).equals(SystemProperties.get(string, ""))));
}
//获取系统属性值
private static String getSystemString(Context context, String string){
    return Settings.System.getString(context.getContentResolver(), string);
}


目录
相关文章
|
1月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
78 6
|
30天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
1月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
27 3
|
1月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
25 2
|
1月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
39 3
|
1月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
18 0
|
2月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
50 4
|
2月前
|
Android开发 开发者
Android面试之Activity启动流程简述
每个Android开发者都熟悉的Activity,但你是否了解它的启动流程呢?本文将带你深入了解。启动流程涉及四个关键角色:Launcher进程、SystemServer的AMS、应用程序的ActivityThread及Zygote进程。核心在于AMS与ActivityThread间的通信。文章详细解析了从Launcher启动Activity的过程,包括通过AIDL获取AMS、Zygote进程启动以及ActivityThread与AMS的通信机制。接着介绍了如何创建Application及Activity的具体步骤。整体流程清晰明了,帮助你更深入理解Activity的工作原理。
53 0
|
3月前
|
Android开发
我的Android进阶修炼:安卓启动流程之init(1)
本文深入分析了Android系统中的init进程,包括其源码结构、主要功能以及启动流程的详细注解,旨在帮助读者理解init作为用户空间的1号进程在Android启动过程中的关键作用。
68 1
|
2月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
153 0

推荐镜像

更多
下一篇
无影云桌面