背景描述
  · sdk支持的流量管理功能需要配置运营商省份和品牌两个信息后才可以生效
  · 在双卡的场景下,由于sdk的问题,假如卡槽1上的卡做过配置,卡槽2的卡在未正确配置的情况下,sdk会自动分配卡槽1的运营商配置信息给卡槽2,导致卡槽2出现无法通过短信校准流量的问题
  · 假如两张卡都进行过运营商配置,用户手工交换两张卡的位置,sdk无法自动检查这种情况,不能及时更新sim卡配置信息,导致流量校验等功能失效
  监听sim卡插拔并且提醒用户发生配置变更的解决方案
<receiver android:name="com.oneplus.security.network.simcard.SimcardStateChangeReceiver"
android:exported="true"
android:enabled="true" >
<intent-filter>
<action android:name="android.intent.action.SIM_STATE_CHANGED"/>
</intent-filter>
</receiver>
@Override
public void onReceive (Context context, Intent intent) {
String action = intent.getAction();
if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
String state = intent.getStringExtra(SimStateListener.SIM_STATE_KEY);
if (SimStateListener.SS_ABSENT.equals(state)) {
SimcardStateManager.setShouldAlertSimcardHasPopedOut(context, true,
SimcardDataModelInterface.SIM_CARD_ONE_INDEX);
SimcardStateManager.setShouldAlertSimcardHasPopedOut(context, true,
SimcardDataModelInterface.SIM_CARD_TWO_INDEX);
}
}
}
  · 注册静态广播,监听sim卡插拔状态的变化,发生sim卡absent事件时,设置需要提醒用户配置流量标识(alert_data_change_flag)为true,每次启动流量管理界面时,都弹出对话框提示,假如用户跟随对话框提示的指引进入配置页面,则认为用户已经正确做完配置,重置标志位alert_data_change_flag为false
  · 这个方案的问题
  假如用户曾经做过正确的配置,只是因为某些原因重插拔卡,提示会造成干扰
  监听SS_ABSENT的方法不合适,因为手机重新开机时,sim卡会走一遍从SS_ABSENT过渡到SS_READY的过程,因为我们注册了静态广播,即使用户没做过任何插拔卡动作,也会触发提醒
  · 这个问题根本上是解决怎么保存用户配置问题,而不是插拔卡的问题,以监听插拔卡来设置标记的方式,只是针对现象做补救,而不解决根本问题
  怎么保存用户的配置信息
  找到标识sim卡的id信息
  咨询其他同事,可以用sim卡的subId来做标识,在一台手机上,每次插入一张新的sim卡,手机会给这张卡一个递增的subId,即便以后发生过换卡等操作,再次使用这张卡时,也会使用之前保存过的subId来标识旧的sim卡,所以方案演变成
  以sim卡的subId为key做存储,通过检查一个subId对应的value有效性的方式决定要不要提示用户重新配置
  在用户配置运营商信息时,获取被设置卡的subId,以之为key做存储,参考CarrierConfigFragment.java
@Override
public boolean onPreferenceChange (Preference preference, Object newValue) {
if (preference == mProvinceSelection) {
String newProvince = String.valueOf(newValue);
if (null != mOperatorPrefModel) {
mOperatorPrefModel.setProvince(newProvince);
SimcardInfoSaver.saveSimcardProvinceBySubId(getActivity(), mSubId, newProvince);
}
return true;
} else if (preference == mBrandSelection) {
String newBrand = String.valueOf(newValue);
if (null != mOperatorPrefModel) {
mOperatorPrefModel.setBrand(newBrand);
SimcardInfoSaver.saveSimcardBrandBySubId(getActivity(), mSubId, newBrand);
}
return true;
}
return false;
}
  依旧监听sim卡插拔状态的监听,避免做不必要的更新检查
  在需要检查的时候,根据卡槽上某张卡的subId获取province和brand等信息,做有效性检查,有效重新设置,无效发出警告,以下代码在流量管理的主界面管理代码中
private boolean alertSimcardOneIfNecessary () {
return getShouldAlertSimcardChange(SimcardDataModelInterface.SIM_CARD_ONE_INDEX, new SimcardProvinceAndBrandSetter() {
@Override
public void setProvinceAndBrand (String province, String brand) {
if (null != mOperatorDataModelSimOne) {
mOperatorDataModelSimOne.setProvince(SimcardDataModelInterface
.SIM_CARD_ONE_INDEX, province);
mOperatorDataModelSimOne.setOperatorBrand(SimcardDataModelInterface
.SIM_CARD_ONE_INDEX, brand);
}
}
});
}
private boolean alertSimcardTwoIfNecessary() {
return getShouldAlertSimcardChange(SimcardDataModelInterface.SIM_CARD_TWO_INDEX, new SimcardProvinceAndBrandSetter() {
@Override
public void setProvinceAndBrand (String province, String brand) {
if (null != mOperatorDataModelSimTwo) {
mOperatorDataModelSimTwo.setProvince(SimcardDataModelInterface
.SIM_CARD_TWO_INDEX, province);
mOperatorDataModelSimTwo.setOperatorBrand(SimcardDataModelInterface
.SIM_CARD_TWO_INDEX, brand);
}
}
});
}
private boolean getShouldAlertSimcardChange (int slotId, final SimcardProvinceAndBrandSetter
setter) {
if (null == mSimcardDataModel) {
return false;
}
final Context c = getActivity();
if (mCurrentDataSlotId == SimcardDataModelInterface.INVALID_SIM_SLOT_ID) {
return false;
}
if (!SimcardStateManager.getShouldAlertSimcardHasPopedOut(getActivity(), slotId)) {
return false;
}
boolean shouldAlertSimcardChangeDialog = false;
if (mSimcardDataModel.isSlotSimInserted(slotId)
&& mSimcardDataModel.isSlotOperatorSupportedBySdk(slotId)) {
int subId = SimcardDataModel.staticGetSubIdBySlotId(slotId);
String province = SimcardInfoSaver.getSimcardProvinceBySubId(getActivity(), subId);
String brand = SimcardInfoSaver.getSimcardBrandBySubId(getActivity(), subId);
if (SimcardInfoSaver.SIMCARD_BRAND_INVALID_VALUE.equals(brand)
|| SimcardInfoSaver.SIMCARD_PROVINCE_INVALID_VALUE.equals(province)) {
LogUtils.LogD(TAG, "alert slot1 error " + brand + " " + province);
if (SimcardStateManager.getShouldAlertSimcardHasPopedOut(getActivity(), slotId)) {
showReconfigSimcardAlert(c, slotId);
shouldAlertSimcardChangeDialog = true;
} else {
shouldAlertSimcardChangeDialog = false;
}
} else {
LogUtils.LogD(TAG, "detect simcard change and reset sdk information on slot 1.");
setter.setProvinceAndBrand(province, brand);
}
}
return shouldAlertSimcardChangeDialog;
}
private interface SimcardProvinceAndBrandSetter {
void setProvinceAndBrand(String province, String brand);
}
private void showReconfigSimcardAlert (final Context c, final int slotId) {
AlertDialog.Builder builder = new AlertDialog.Builder(c);
builder.setCancelable(true);
StringBuilder sb = new StringBuilder();
sb.append(getSimCardDescriptions(slotId));
sb.append(" ");
sb.append(c.getText(R.string.detect_simcard_change_please_confirm_config));
builder.setMessage(sb.toString());
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick (DialogInterface dialog, int which) {
startSpecificTrafficConfigSettingsActivity(slotId);
}
});
builder.create().show();
}
  代码的触发点有两个位置,一个是进入流量管理界面的动画结束之后,另一个是点击流量校准按钮的时候
@Override
public void pageInAnim () {
super.pageInAnim();
mDataTopView.animtionCircle(true);
if (null != mOperatorDataModel) {
mOperatorDataModel.requesetPkgMonthlyUsageAndTotalInByte(mCurrentDataSlotId);
}
alertSimcardOneIfNecessary();
alertSimcardTwoIfNecessary();
}
mSimDataUsageQueryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick (View v) {
...
if (SimcardDataModelInterface.SIM_CARD_ONE_INDEX == mCurrentDataSlotId) {
if (alertSimcardOneIfNecessary()) {
return;
}
} else if (SimcardDataModelInterface.SIM_CARD_TWO_INDEX == mCurrentDataSlotId) {
if (alertSimcardTwoIfNecessary()) {
return;
}
} else {
// CurrentDataSlotId is invalid, should do nothing but return. actually, in
// this circumstances, data calibrate button is invisible.
return;
}
...
}
};
  测试建议及其他
  · 非sdk支持的运营商卡,不会产生自动配置省份和品牌的行为,因为本身不需要设置 比如小米卡
  · 单张卡插入 从未配置过的sim卡 活动到流量页或者点击流量校准按钮
  · 会弹出需要配置的提示框 点击边缘可以退出提示框使用其他功能 但是重复步骤2会继续提示
  · 有两种方法可以终止提示 a.点击提示框的确定跳转到设置界面 b.点击流量套餐配置跳转到设置界面
  · 已经配置过的sim卡,如果没有做过清除用户数据的操作,即使交换卡位,预期可以自动完成sim卡配置,不会弹出提示框,手工进入 流量套餐配置预期可以查看到正确的配置,流量校准使用正常
  · 双卡的情况下,如果两张卡都配置过,无论是否交换卡位,预期不会弹提示框,进入对应卡槽的配置界面,预期省份及品牌信息显示正确,功能正常
  · 双卡情况下,两张卡均未配置过,会提示用户进行配置
  · 双卡,一张配置、一张未配置,且都被sdk支持,即使交换卡位,预期已配置的卡显示正确,功能正常,未配置过的卡会按照2的逻辑弹出提示
  · 已配置过的卡且或者未配置过的卡进行过4中操作,重启手机不会重复弹框
  · 未配置过的卡并且未进行过4中操作,重启手机,滑动到流量界面,点击校准按钮,都会弹框提示
  终效果
  已经配置过的sim卡,可以正常识别并进行重新设置,不会频繁骚扰用户。