问题回顾

之前剖析了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目标,目标里定义了三个特点:simStateslotIdsubId

在用户解锁SIM卡后,会顺次履行KeyguardSimPinView.verifyPasswordAndUnlock()->KeyguardUpdateMonitor.reportSimUnlocked()->handleSimStateChange(),在handleSimStateChange()办法里会更新Sim卡的状况,也便是SimDatasimState

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音讯,mHandlerhandleMessage()中经过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中发现一些问题

Android10开机解锁问题分析(二)

可以看到,在解锁完第二张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

解决方案

既然知道了上层问题原因地点,那么有两种方式来处理此问题

  1. refreshSimData()不履行掩盖操作,以本地数据源为准,即直接回来false
  2. 持续深究为什么底层实在状况没有更改过来

现在暂时选用方案1,然后小组内进行危险评价后再决议是否最终的处理方式,此问题后续持续跟进。