问题回顾
之前分析了Android10开机解锁流程(一),初步结论是底层SIM卡状态流转错误,后转给了Modem端来分析,后面Modem端给出了分析数据和结果,表示底层SIM卡状态流转正常,是上层状态流转异常。
继续分析
于是,抱着挖出root cause的态度,继续深入分析了此问题。首先,按照Modem端的意思,底层跟上层应该都维护着一份SIM卡数据,底层数据更新后,通过广播和回调的形式通知上层更新自己的数据源。于是,我找到了上层SIM卡数据存放的位置,位于frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
,里面定义了一个HashMap
,用于存放在SIM卡数据
public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
//省略部分代码
//存放sim卡数据
HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>();
private static class SimData {
//sim卡状态
public State simState;
//sim卡槽id
public int slotId;
//sim卡id
public int subId;
SimData(State state, int slot, int id) {
simState = state;
slotId = slot;
subId = id;
}
//省略部分代码
@Override
public String toString() {
return "SimData{state=" + simState + ",slotId=" + slotId + ",subId=" + subId + "}";
}
}
//省略部分代码
}
其中map的键为subId(SIM卡id),值为SimData
对象,对象里定义了三个属性:simState
,slotId
,subId
在用户解锁SIM卡后,会依次执行KeyguardSimPinView.verifyPasswordAndUnlock()
->KeyguardUpdateMonitor.reportSimUnlocked()
->handleSimStateChange()
,在handleSimStateChange()
方法里会更新Sim卡的状态,也就是SimData
的simState
值
public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
//省略部分代码
HashMap<Integer, SimData> mSimDatas = new HashMap<Integer, SimData>();
/**
* Handle {@link #MSG_SIM_STATE_CHANGE}
*/
@VisibleForTesting
void handleSimStateChange(int subId, int slotId, State state) {
checkIsHandlerThread();
if (DEBUG_SIM_STATES) {
Log.d(TAG, "handleSimStateChange(subId=" + subId + ", slotId="
+ slotId + ", state=" + state +")");
}
boolean becameAbsent = false;
//判断subId是否有效
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
Log.w(TAG, "invalid subId in handleSimStateChange()");
/* Only handle No SIM(ABSENT) and Card Error(CARD_IO_ERROR) due to
* handleServiceStateChange() handle other case */
if (state == State.ABSENT) {
updateTelephonyCapable(true);
// Even though the subscription is not valid anymore, we need to notify that the
// SIM card was removed so we can update the UI.
becameAbsent = true;
mSimDatas.clear();
} else if (state == State.CARD_IO_ERROR) {
updateTelephonyCapable(true);
} else {
return;
}
}
//通过subId获取SimData
SimData data = mSimDatas.get(subId);
final boolean changed;
if (data == null) {
//获取不到,则创建
data = new SimData(state, slotId, subId);
mSimDatas.put(subId, data);
changed = true; // no data yet; force update
} else {
//更新SimData,changed表示是否有属性更新了
changed = (data.simState != state || data.subId != subId || data.slotId != slotId);
Log.d("jason", "update sim info: subId="+data.subId+", slotId="+data.slotId+", simState="+data.simState+"--->subId="+subId+", slotId="+slotId+", simState="+state);
data.simState = state;
data.subId = subId;
data.slotId = slotId;
}
//调试日志
Log.d("jason", " success update,print all sim data:");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
mSimDatas.forEach(new BiConsumer<Integer, SimData>() {
@Override
public void accept(Integer integer, SimData simData) {
Log.d("jason", " " + simData.toString());
}
});
}
//回调
if ((changed || becameAbsent) && state != State.UNKNOWN) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onSimStateChanged(subId, slotId, state);
}
}
}
}
//省略部分代码
}
当onSimStateChanged回调给上层之后,上层会触发Sim卡订阅信息更新的回调,也就是说,Sim卡状态改变了,Sim卡的订阅信息也随之发生改变,订阅监听器如下
public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
//省略部分代码
private OnSubscriptionsChangedListener mSubscriptionListener =
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
//收到sim卡订阅信息更新后执行此处
mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED);
}
};
//省略部分代码
}
当sim卡订阅信息发生改变时,通过mHandler
发送一个MSG_SIM_SUBSCRIPTION_INFO_CHANGED
消息,mHandler
的handleMessage()
中通过handleSimSubscriptionInfoChanged()
处理此消息
public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
//省略部分代码
private void handleSimSubscriptionInfoChanged() {
if (DEBUG_SIM_STATES) {
Log.v(TAG, "onSubscriptionInfoChanged()");
List<SubscriptionInfo> sil = mSubscriptionManager.getActiveSubscriptionInfoList(false);
if (sil != null) {
for (SubscriptionInfo subInfo : sil) {
Log.v(TAG, "SubInfo:" + subInfo);
}
} else {
Log.v(TAG, "onSubscriptionInfoChanged: list is null");
}
}
//获取sim卡的订阅信息
List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
// Hack level over 9000: Because the subscription id is not yet valid when we see the
// first update in handleSimStateChange, we need to force refresh all all SIM states
// so the subscription id for them is consistent.
ArrayList<SubscriptionInfo> changedSubscriptions = new ArrayList<>();
for (int i = 0; i < subscriptionInfos.size(); i++) {
SubscriptionInfo info = subscriptionInfos.get(i);
//刷新sim卡的状态,如果sim卡状态有变化,添加到变化列表中
boolean changed = refreshSimState(info.getSubscriptionId(), info.getSimSlotIndex());
if (changed) {
changedSubscriptions.add(info);
}
}
//调试日志
Log.d("jason", "changed subscription size is "+changedSubscriptions.size());
Log.d("jason", "print all sim subscription info:");
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
mSimDatas.forEach(new BiConsumer<Integer, SimData>() {
@Override
public void accept(Integer integer, SimData simData) {
Log.d("jason", " "+simData.toString());
}
});
}
//如果有sim卡状态发生变化,则再一次执行回调
for (int i = 0; i < changedSubscriptions.size(); i++) {
SimData data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId());
for (int j = 0; j < mCallbacks.size(); j++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
if (cb != null) {
Log.d("jason", "onSimSubscriptionInfoChanged: subId:"+data.subId+", slotId:"+data.slotId+", simState:"+data.simState);
cb.onSimStateChanged(data.subId, data.slotId, data.simState);
}
}
}
//执行运行商信息更新回调
for (int j = 0; j < mCallbacks.size(); j++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
if (cb != null) {
cb.onRefreshCarrierInfo();
}
}
}
//省略部分代码
}
先获取SIM卡的订阅信息,然后通过订阅信息判断是否有SIM卡状态发生改变,如果有则再次执行SIM卡状态更新回调,判断SIM卡状态发生改变的逻辑在refreshSimState()
方法中
public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
//省略部分代码
/**
* @return true if and only if the state has changed for the specified {@code slotId}
*/
private boolean refreshSimState(int subId, int slotId) {
//代码1
// This is awful. It exists because there are two APIs for getting the SIM status
// that don't return the complete set of values and have different types. In Keyguard we
// need IccCardConstants, but TelephonyManager would only give us
// TelephonyManager.SIM_STATE*, so we retrieve it manually.
//通过TelephonyManager获取SIM卡状态
final TelephonyManager tele = TelephonyManager.from(mContext);
int simState = tele.getSimState(slotId);
State state;
try {
//将状态转化为State
state = State.intToState(simState);
} catch(IllegalArgumentException ex) {
Log.w(TAG, "Unknown sim state: " + simState);
state = State.UNKNOWN;
}
//在本地SIM卡数据源中获取对应的SimData对象
SimData data = mSimDatas.get(subId);
final boolean changed;
//更新本地SimData
if (data == null) {
data = new SimData(state, slotId, subId);
mSimDatas.put(subId, data);
changed = true; // no data yet; force update
} else {
changed = (data.simState != state) || (data.slotId != slotId);
Log.d("jason", "refresh sim state:"+data.simState+"-->"+state);
data.simState = state;
data.slotId = slotId;
}
return changed;
}
//省略部分代码
}
OK,涉及到本地Sim卡数据源mSimDatas
的逻辑就是这些了,接下来通过本地日志查看数据源的变化过程,在捕获的logcat中发现一些问题
可以看到,在解锁完第二张SIM卡后,两张卡的状态均变为了READY
,但在刷新sim卡状态时,卡2的状态又从READY
变成了PIN_REQUIRED
,那为什么会变回去呢,我们来看一下refreshSimState()
方法中的解释,看上面代码1处的注释的解释
// This is awful. It exists because there are two APIs for getting the SIM status
// that don't return the complete set of values and have different types. In Keyguard we
// need IccCardConstants, but TelephonyManager would only give us
// TelephonyManager.SIM_STATE*, so we retrieve it manually.
大概意思是,这个方法存在,是因为有两个API去获取SIM的状态,这两个API返回不同类型的集合,并不是完整的集合,在Keyguard
中为IccCardConstants
,但是在TelephonyManager
中为TelephonyManager.SIM_STATE*
,必须手动将TelephonyManager
类型检索成IccCardConstants
类型,这是一个糟糕的设计。
OK,现在我们明白了,refreshSimState()
方法的作用其实就是为了让本地数据源与真实数据保持同步,也就是本地虽然标记为了READY
,但实际上SIM卡的状态并没有变为READY
,从远程获取到的SIM卡真实状态又重新覆盖了本地数据源的状态,这才导致状态从READY
又变回去了PIN_REQUIRED
解决方案
既然知道了上层问题原因所在,那么有两种方式来处理此问题
- refreshSimData()不执行覆盖操作,以本地数据源为准,即直接返回false
- 继续深究为什么底层真实状态没有更改过来
目前暂时采用方案1,然后小组内进行风险评估后再决定是否最终的处理方式,此问题后续继续跟进。