安卓开机主卡选择流程(展讯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);
}


目录
相关文章
|
安全 算法 小程序
【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
1054 28
【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
1012 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
NoSQL 应用服务中间件 PHP
布谷一对一直播源码android版环境配置流程及功能明细
部署需基于 CentOS 7.9 系统,硬盘不低于 40G,使用宝塔面板安装环境,包括 PHP 7.3(含 Redis、Fileinfo 扩展)、Nginx、MySQL 5.6、Redis 和最新 Composer。Swoole 扩展需按步骤配置。2021.08.05 后部署需将站点目录设为 public 并用 ThinkPHP 伪静态。开发环境建议 Windows 操作系统与最新 Android Studio,基础配置涉及 APP 名称修改、接口域名更换、包名调整及第三方登录分享(如 QQ、微信)的配置,同时需完成阿里云与腾讯云相关设置。
|
缓存 前端开发 Android开发
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
743 12
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
535 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
10475 2
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
656 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
测试技术 Android开发 开发者
【03】优雅草央千澈详解关于APP签名以及分发-上架完整流程-第三篇安卓APP上架华为商店后面的步骤-华为应用商店相对比较麻烦一些-华为商店安卓上架
【03】优雅草央千澈详解关于APP签名以及分发-上架完整流程-第三篇安卓APP上架华为商店后面的步骤-华为应用商店相对比较麻烦一些-华为商店安卓上架
365 16
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
368 6
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
478 3

热门文章

最新文章