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

937 阅读4分钟

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

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

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

问题回顾

之前分析了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中发现一些问题

解锁第二张SIM卡日志.jpg

可以看到,在解锁完第二张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,然后小组内进行风险评估后再决定是否最终的处理方式,此问题后续继续跟进。