From 52a6133f4ba8b1d08f5158d802790d6a1b16568d Mon Sep 17 00:00:00 2001 From: Jim Miller Date: Wed, 12 Nov 2014 19:29:51 -0800 Subject: [PATCH] Add multi-sim support to keyguard Use new telephony APIs. Clean up SIM state machine code. Use cached copy of SubscriptionInfo. Make SIM PIN and SIM PUK work. Tested on single and multi-SIM devices. Fixes bug 18147652 Change-Id: Ic69a4d2898999a5438e6a70b5851705bc05443f1 --- packages/Keyguard/res/values/strings.xml | 4 + .../src/com/android/keyguard/CarrierText.java | 2 +- .../com/android/keyguard/EmergencyButton.java | 18 +- .../android/keyguard/KeyguardConstants.java | 3 +- .../keyguard/KeyguardSecurityModel.java | 13 +- .../android/keyguard/KeyguardSimPinView.java | 67 +++++- .../android/keyguard/KeyguardSimPukView.java | 77 +++++-- .../keyguard/KeyguardUpdateMonitor.java | 216 ++++++++++++++---- .../KeyguardUpdateMonitorCallback.java | 3 +- .../keyguard/KeyguardViewMediator.java | 39 ++-- 10 files changed, 345 insertions(+), 97 deletions(-) diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml index a136acf1007fb..fbe371277b7de 100644 --- a/packages/Keyguard/res/values/strings.xml +++ b/packages/Keyguard/res/values/strings.xml @@ -240,12 +240,16 @@ Draw your pattern Enter SIM PIN + + Enter SIM PIN for \"%1$s\" Enter PIN Enter Password SIM is now disabled. Enter PUK code to continue. Contact carrier for details. + + SIM \"%1$s\" is now disabled. Enter PUK code to continue. Contact carrier for details. Enter desired PIN code diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java index ad07a7a9d16af..55bfe499a91d2 100644 --- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java +++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java @@ -48,7 +48,7 @@ public class CarrierText extends TextView { } @Override - public void onSimStateChanged(IccCardConstants.State simState) { + public void onSimStateChanged(int subId, int slotId, State simState) { mSimState = simState; updateCarrierText(mSimState, mPlmn, mSpn); } diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java index e0507a8df042c..50ac2612e550c 100644 --- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java +++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java @@ -36,22 +36,18 @@ import com.android.internal.widget.LockPatternUtils; * allows the user to return to the call. */ public class EmergencyButton extends Button { - - private static final int EMERGENCY_CALL_TIMEOUT = 10000; // screen timeout after starting e.d. private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL"; KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { @Override - public void onSimStateChanged(State simState) { - int phoneState = KeyguardUpdateMonitor.getInstance(mContext).getPhoneState(); - updateEmergencyCallButton(simState, phoneState); + public void onSimStateChanged(int subId, int slotId, State simState) { + updateEmergencyCallButton(); } @Override public void onPhoneStateChanged(int phoneState) { - State simState = KeyguardUpdateMonitor.getInstance(mContext).getSimState(); - updateEmergencyCallButton(simState, phoneState); + updateEmergencyCallButton(); } }; private LockPatternUtils mLockPatternUtils; @@ -87,9 +83,7 @@ public class EmergencyButton extends Button { takeEmergencyCallAction(); } }); - int phoneState = KeyguardUpdateMonitor.getInstance(mContext).getPhoneState(); - State simState = KeyguardUpdateMonitor.getInstance(mContext).getSimState(); - updateEmergencyCallButton(simState, phoneState); + updateEmergencyCallButton(); } /** @@ -112,12 +106,12 @@ public class EmergencyButton extends Button { } } - private void updateEmergencyCallButton(State simState, int phoneState) { + private void updateEmergencyCallButton() { boolean enabled = false; if (mLockPatternUtils.isInCall()) { enabled = true; // always show "return to call" if phone is off-hook } else if (mLockPatternUtils.isEmergencyCallCapable()) { - boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext).isSimLocked(); + final boolean simLocked = KeyguardUpdateMonitor.getInstance(mContext).isSimPinVoiceSecure(); if (simLocked) { // Some countries can't handle emergency calls while SIM is locked. enabled = mLockPatternUtils.isEmergencyCallEnabledWhileSimLocked(); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java b/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java index 77643bdb7a3fa..10baf230d627a 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardConstants.java @@ -25,5 +25,6 @@ public class KeyguardConstants { * Turns on debugging information for the whole Keyguard. This is very verbose and should only * be used temporarily for debugging. */ - public static final boolean DEBUG = false; + public static final boolean DEBUG = true; + public static final boolean DEBUG_SIM_STATES = true; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java index 3166ad413c6ea..a5d260d9ac53d 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java @@ -17,11 +17,15 @@ package com.android.keyguard; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import com.android.internal.telephony.IccCardConstants; import com.android.internal.widget.LockPatternUtils; +import java.util.List; + public class KeyguardSecurityModel { /** @@ -75,12 +79,13 @@ public class KeyguardSecurityModel { } SecurityMode getSecurityMode() { - KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); - final IccCardConstants.State simState = updateMonitor.getSimState(); + KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); SecurityMode mode = SecurityMode.None; - if (simState == IccCardConstants.State.PIN_REQUIRED) { + if (monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED) + != SubscriptionManager.INVALID_SUB_ID) { mode = SecurityMode.SimPin; - } else if (simState == IccCardConstants.State.PUK_REQUIRED + } else if (monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED) + != SubscriptionManager.INVALID_SUB_ID && mLockPatternUtils.isPukUnlockScreenEnable()) { mode = SecurityMode.SimPuk; } else { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java index 5a0fdb2ff4716..d8feaf8ae03f9 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPinView.java @@ -17,15 +17,21 @@ package com.android.keyguard; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.IccCardConstants.State; import com.android.internal.telephony.PhoneConstants; import android.content.Context; +import android.content.res.Resources; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.Dialog; import android.app.ProgressDialog; import android.os.RemoteException; import android.os.ServiceManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; import android.view.WindowManager; @@ -35,13 +41,22 @@ import android.view.WindowManager; */ public class KeyguardSimPinView extends KeyguardPinBasedInputView { private static final String LOG_TAG = "KeyguardSimPinView"; - private static final boolean DEBUG = KeyguardConstants.DEBUG; + private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES; public static final String TAG = "KeyguardSimPinView"; private ProgressDialog mSimUnlockProgressDialog = null; private CheckSimPin mCheckSimPinThread; private AlertDialog mRemainingAttemptsDialog; + private int mSubId; + + KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onSimStateChanged(int subId, int slotId, State simState) { + if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + resetState(); + }; + }; public KeyguardSimPinView(Context context) { this(context, null); @@ -53,7 +68,22 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { public void resetState() { super.resetState(); - mSecurityMessageDisplay.setMessage(R.string.kg_sim_pin_instructions, true); + if (DEBUG) Log.v(TAG, "Resetting state"); + KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); + mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED); + if (mSubId != SubscriptionManager.INVALID_SUB_ID) { + int count = TelephonyManager.getDefault().getSimCount(); + Resources rez = getResources(); + final String msg; + if (count < 2) { + msg = rez.getString(R.string.kg_sim_pin_instructions); + } else { + SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId); + CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash + msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName); + } + mSecurityMessageDisplay.setMessage(msg, true); + } } private String getPinPasswordErrorMessage(int attemptsRemaining) { @@ -94,6 +124,18 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { } } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback); + } + @Override public void showUsabilityHint() { } @@ -113,9 +155,11 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { */ private abstract class CheckSimPin extends Thread { private final String mPin; + private int mSubId; - protected CheckSimPin(String pin) { + protected CheckSimPin(String pin, int subId) { mPin = pin; + mSubId = subId; } abstract void onSimCheckResponse(final int result, final int attemptsRemaining); @@ -123,10 +167,14 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { @Override public void run() { try { - Log.v(TAG, "call supplyPinReportResult()"); + if (DEBUG) { + Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")"); + } final int[] result = ITelephony.Stub.asInterface(ServiceManager - .checkService("phone")).supplyPinReportResult(mPin); - Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]); + .checkService("phone")).supplyPinReportResultForSubscriber(mSubId, mPin); + if (DEBUG) { + Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]); + } post(new Runnable() { public void run() { onSimCheckResponse(result[0], result[1]); @@ -187,15 +235,17 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { getSimUnlockProgressDialog().show(); if (mCheckSimPinThread == null) { - mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText()) { + mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) { void onSimCheckResponse(final int result, final int attemptsRemaining) { post(new Runnable() { public void run() { if (mSimUnlockProgressDialog != null) { mSimUnlockProgressDialog.hide(); } + resetPasswordText(true /* animate */); if (result == PhoneConstants.PIN_RESULT_SUCCESS) { - KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(); + KeyguardUpdateMonitor.getInstance(getContext()) + .reportSimUnlocked(mSubId); mCallback.dismiss(true); } else { if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { @@ -216,7 +266,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " + " CheckSimPin.onSimCheckResponse: " + result + " attemptsRemaining=" + attemptsRemaining); - resetPasswordText(true /* animate */); } mCallback.userActivity(); mCheckSimPinThread = null; diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java index f0c5805f4e77d..c5d940c9950f0 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSimPukView.java @@ -17,18 +17,24 @@ package com.android.keyguard; import android.content.Context; +import android.content.res.Resources; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.os.RemoteException; import android.os.ServiceManager; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; import android.view.WindowManager; import com.android.internal.telephony.ITelephony; +import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.IccCardConstants.State; /** @@ -45,6 +51,23 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { private String mPinText; private StateMachine mStateMachine = new StateMachine(); private AlertDialog mRemainingAttemptsDialog; + private int mSubId; + + KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onSimStateChanged(int subId, int slotId, State simState) { + if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + resetState(); + }; + }; + + public KeyguardSimPukView(Context context) { + this(context, null); + } + + public KeyguardSimPukView(Context context, AttributeSet attrs) { + super(context, attrs); + } private class StateMachine { final int ENTER_PUK = 0; @@ -89,7 +112,21 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { mPinText=""; mPukText=""; state = ENTER_PUK; - mSecurityMessageDisplay.setMessage(R.string.kg_puk_enter_puk_hint, true); + KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); + mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED); + if (mSubId != SubscriptionManager.INVALID_SUB_ID) { + int count = TelephonyManager.getDefault().getSimCount(); + Resources rez = getResources(); + final String msg; + if (count < 2) { + msg = rez.getString(R.string.kg_puk_enter_puk_hint); + } else { + SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId); + CharSequence displayName = info != null ? info.getDisplayName() : ""; + msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); + } + mSecurityMessageDisplay.setMessage(msg, true); + } mPasswordEntry.requestFocus(); } } @@ -111,14 +148,6 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { return displayMessage; } - public KeyguardSimPukView(Context context) { - this(context, null); - } - - public KeyguardSimPukView(Context context, AttributeSet attrs) { - super(context, attrs); - } - public void resetState() { super.resetState(); mStateMachine.reset(); @@ -145,6 +174,18 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { } } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback); + } + @Override public void showUsabilityHint() { } @@ -165,10 +206,12 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { private abstract class CheckSimPuk extends Thread { private final String mPin, mPuk; + private final int mSubId; - protected CheckSimPuk(String puk, String pin) { + protected CheckSimPuk(String puk, String pin, int subId) { mPuk = puk; mPin = pin; + mSubId = subId; } abstract void onSimLockChangedResponse(final int result, final int attemptsRemaining); @@ -176,10 +219,12 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { @Override public void run() { try { - Log.v(TAG, "call supplyPukReportResult()"); + if (DEBUG) Log.v(TAG, "call supplyPukReportResult()"); final int[] result = ITelephony.Stub.asInterface(ServiceManager - .checkService("phone")).supplyPukReportResult(mPuk, mPin); - Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]); + .checkService("phone")).supplyPukReportResultForSubscriber(mSubId, mPuk, mPin); + if (DEBUG) { + Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]); + } post(new Runnable() { public void run() { onSimLockChangedResponse(result[0], result[1]); @@ -254,15 +299,17 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { getSimUnlockProgressDialog().show(); if (mCheckSimPukThread == null) { - mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText) { + mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) { void onSimLockChangedResponse(final int result, final int attemptsRemaining) { post(new Runnable() { public void run() { if (mSimUnlockProgressDialog != null) { mSimUnlockProgressDialog.hide(); } + resetPasswordText(true /* animate */); if (result == PhoneConstants.PIN_RESULT_SUCCESS) { - KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(); + KeyguardUpdateMonitor.getInstance(getContext()) + .reportSimUnlocked(mSubId); mCallback.dismiss(true); } else { if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index aa5819e6cac7b..48b2eac8e8809 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -51,11 +51,17 @@ import android.os.UserHandle; import android.provider.Settings; import com.android.internal.telephony.IccCardConstants; +import com.android.internal.telephony.IccCardConstants.State; +import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.TelephonyProperties; import android.service.fingerprint.FingerprintManager; import android.service.fingerprint.FingerprintManagerReceiver; import android.service.fingerprint.FingerprintUtils; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionListener; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; import android.util.SparseBooleanArray; @@ -64,6 +70,9 @@ import com.google.android.collect.Lists; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; /** * Watches for updates that may be interesting to the keyguard, and provides @@ -79,7 +88,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final String TAG = "KeyguardUpdateMonitor"; private static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final boolean DEBUG_SIM_STATES = DEBUG || false; + private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES; private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 3; private static final int LOW_BATTERY_THRESHOLD = 20; @@ -113,13 +122,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final int MSG_FINGERPRINT_PROCESSED = 323; private static final int MSG_FINGERPRINT_ACQUIRED = 324; private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 325; + private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 326; private static KeyguardUpdateMonitor sInstance; private final Context mContext; + HashMap mSimDatas = new HashMap(); - // Telephony state - private IccCardConstants.State mSimState = IccCardConstants.State.READY; private CharSequence mTelephonyPlmn; private CharSequence mTelephonySpn; private int mRingMode; @@ -149,6 +158,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private boolean mSwitchingUser; private boolean mScreenOn; + protected List mSubscriptionInfo; private final Handler mHandler = new Handler() { @Override @@ -164,7 +174,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { handleCarrierInfoUpdate(); break; case MSG_SIM_STATE_CHANGE: - handleSimStateChange((SimArgs) msg.obj); + handleSimStateChange(msg.arg1, msg.arg2, (State) msg.obj); break; case MSG_RINGER_MODE_CHANGED: handleRingerModeChange(msg.arg1); @@ -220,10 +230,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { case MSG_FACE_UNLOCK_STATE_CHANGED: handleFaceUnlockStateChanged(msg.arg1 != 0, msg.arg2); break; + case MSG_SIM_SUBSCRIPTION_INFO_CHANGED: + handleSimSubscriptionInfoChanged(); + break; } } }; + private SubscriptionListener mSubscriptionListener = new SubscriptionListener() { + @Override + public void onSubscriptionInfoChanged() { + mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED); + } + }; + private SparseBooleanArray mUserHasTrust = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray(); private SparseBooleanArray mUserFingerprintRecognized = new SparseBooleanArray(); @@ -244,6 +264,40 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } + protected void handleSimSubscriptionInfoChanged() { + if (DEBUG_SIM_STATES) { + Log.v(TAG, "onSubscriptionInfoChanged()"); + for (SubscriptionInfo subInfo : SubscriptionManager.getActiveSubscriptionInfoList()) { + Log.v(TAG, "SubInfo:" + subInfo); + } + } + List 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. + for (int i = 0; i < subscriptionInfos.size(); i++) { + SubscriptionInfo info = subscriptionInfos.get(i); + refreshSimState(info.getSubscriptionId(), info.getSimSlotIndex()); + } + for (int i = 0; i < subscriptionInfos.size(); i++) { + SimData data = mSimDatas.get(mSubscriptionInfo.get(i).getSubscriptionId()); + for (int j = 0; j < mCallbacks.size(); j++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get(); + if (cb != null) { + cb.onSimStateChanged(data.subId, data.slotId, data.simState); + } + } + } + } + + List getSubscriptionInfo(boolean forceReload) { + if (mSubscriptionInfo == null || forceReload) { + mSubscriptionInfo = SubscriptionManager.getActiveSubscriptionInfoList(); + } + return mSubscriptionInfo; + } + @Override public void onTrustManagedChanged(boolean managed, int userId) { mUserTrustIsManaged.put(userId, managed); @@ -382,12 +436,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health)); mHandler.sendMessage(msg); } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { + SimData args = SimData.fromIntent(intent); if (DEBUG_SIM_STATES) { - Log.v(TAG, "action " + action + " state" + - intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)); + Log.v(TAG, "action " + action + + " state: " + intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE) + + " slotId: " + args.slotId + " subid: " + args.subId); } - mHandler.sendMessage(mHandler.obtainMessage( - MSG_SIM_STATE_CHANGE, SimArgs.fromIntent(intent))); + mHandler.obtainMessage(MSG_SIM_STATE_CHANGE, args.subId, args.slotId, args.simState) + .sendToTarget(); } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0)); @@ -449,19 +505,26 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { * we need a single object to pass to the handler. This class helps decode * the intent and provide a {@link SimCard.State} result. */ - private static class SimArgs { - public final IccCardConstants.State simState; + private static class SimData { + public State simState; + public int slotId; + public int subId; - SimArgs(IccCardConstants.State state) { + SimData(State state, int slot, int id) { simState = state; + slotId = slot; + subId = id; } - static SimArgs fromIntent(Intent intent) { - IccCardConstants.State state; + static SimData fromIntent(Intent intent) { + State state; if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) { throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED"); } String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); + int slotId = intent.getIntExtra(PhoneConstants.SLOT_KEY, 0); + int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, + SubscriptionManager.INVALID_SUB_ID); if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { final String absentReason = intent .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); @@ -494,11 +557,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } else { state = IccCardConstants.State.UNKNOWN; } - return new SimArgs(state); + return new SimData(state, slotId, subId); } public String toString() { - return simState.toString(); + return "SimData{state=" + simState + ",slotId=" + slotId + ",subId=" + subId + "}"; } } @@ -605,7 +668,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } // Take a guess at initial SIM state, battery status and PLMN until we get an update - mSimState = IccCardConstants.State.NOT_READY; mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0); mTelephonyPlmn = getDefaultPlmn(); @@ -636,6 +698,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { context.registerReceiverAsUser(mBroadcastAllReceiver, UserHandle.ALL, allUserFilter, null, null); + SubscriptionManager.register(mContext, mSubscriptionListener, + SubscriptionListener.LISTEN_SUBSCRIPTION_INFO_LIST_CHANGED); try { ActivityManagerNative.getDefault().registerUserSwitchObserver( new IUserSwitchObserver.Stub() { @@ -881,20 +945,35 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { /** * Handle {@link #MSG_SIM_STATE_CHANGE} */ - private void handleSimStateChange(SimArgs simArgs) { - final IccCardConstants.State state = simArgs.simState; + private void handleSimStateChange(int subId, int slotId, State state) { - if (DEBUG) { - Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " " - + "state resolved to " + state.toString()); + if (DEBUG_SIM_STATES) { + Log.d(TAG, "handleSimStateChange(subId=" + subId + ", slotId=" + + slotId + ", state=" + state +")"); } - if (state != IccCardConstants.State.UNKNOWN && state != mSimState) { - mSimState = state; + if (subId == SubscriptionManager.INVALID_SUB_ID) { + Log.w(TAG, "invalid subId in handleSimStateChange()"); + return; + } + + 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 { + changed = (data.simState != state || data.subId != subId || data.slotId != slotId); + data.simState = state; + data.subId = subId; + data.slotId = slotId; + } + if (changed && state != State.UNKNOWN) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { - cb.onSimStateChanged(state); + cb.onSimStateChanged(subId, slotId, state); } } } @@ -1070,7 +1149,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { callback.onPhoneStateChanged(mPhoneState); callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn); callback.onClockVisibilityChanged(); - callback.onSimStateChanged(mSimState); + for (Entry data : mSimDatas.entrySet()) { + final SimData state = data.getValue(); + callback.onSimStateChanged(state.subId, state.slotId, state.simState); + } } public void sendKeyguardVisibilityChanged(boolean showing) { @@ -1095,10 +1177,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget(); } - public IccCardConstants.State getSimState() { - return mSimState; - } - /** * Report that the user successfully entered the SIM PIN or PUK/SIM PIN so we * have the information earlier than waiting for the intent @@ -1107,8 +1185,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { * NOTE: Because handleSimStateChange() invokes callbacks immediately without going * through mHandler, this *must* be called from the UI thread. */ - public void reportSimUnlocked() { - handleSimStateChange(new SimArgs(IccCardConstants.State.READY)); + public void reportSimUnlocked(int subId) { + if (DEBUG_SIM_STATES) Log.v(TAG, "reportSimUnlocked(subId=" + subId + ")"); + int slotId = SubscriptionManager.getSlotId(subId); + handleSimStateChange(subId, slotId, State.READY); } /** @@ -1184,18 +1264,44 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { mAlternateUnlockEnabled = enabled; } - public boolean isSimLocked() { - return isSimLocked(mSimState); - } - - public static boolean isSimLocked(IccCardConstants.State state) { - return state == IccCardConstants.State.PIN_REQUIRED - || state == IccCardConstants.State.PUK_REQUIRED - || state == IccCardConstants.State.PERM_DISABLED; + public boolean isSimPinVoiceSecure() { + // TODO: only count SIMs that handle voice + return isSimPinSecure(); } public boolean isSimPinSecure() { - return isSimPinSecure(mSimState); + // True if any SIM is pin secure + for (SubscriptionInfo info : getSubscriptionInfo(false /* forceReload */)) { + if (isSimPinSecure(getSimState(info.getSubscriptionId()))) return true; + } + return false; + } + + private State getSimState(int subId) { + if (mSimDatas.containsKey(subId)) { + return mSimDatas.get(subId).simState; + } else { + return State.UNKNOWN; + } + } + + private void refreshSimState(int subId, int slotId) { + + // 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. + final int phoneId = SubscriptionManager.getPhoneId(subId); + final String stateString = TelephonyManager.getTelephonyProperty(phoneId, + TelephonyProperties.PROPERTY_SIM_STATE, ""); + State state; + try { + state = State.valueOf(stateString); + } catch(IllegalArgumentException ex) { + Log.w(TAG, "Unknown sim state: " + stateString); + state = State.UNKNOWN; + } + mSimDatas.put(subId, new SimData(state, slotId, subId)); } public static boolean isSimPinSecure(IccCardConstants.State state) { @@ -1228,4 +1334,34 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { public boolean isScreenOn() { return mScreenOn; } + + /** + * Find the next SubscriptionId for a SIM in the given state, favoring lower slot numbers first. + * @param state + * @return subid or {@link SubscriptionManager#INVALID_SUB_ID} if none found + */ + public int getNextSubIdForState(State state) { + List list = getSubscriptionInfo(false /* forceReload */); + int resultId = SubscriptionManager.INVALID_SUB_ID; + int bestSlotId = Integer.MAX_VALUE; // Favor lowest slot first + for (int i = 0; i < list.size(); i++) { + final SubscriptionInfo info = list.get(i); + final int id = info.getSubscriptionId(); + int slotId = SubscriptionManager.getSlotId(id); + if (state == getSimState(id) && bestSlotId > slotId ) { + resultId = id; + bestSlotId = slotId; + } + } + return resultId; + } + + public SubscriptionInfo getSubscriptionInfoForSubId(int subId) { + List list = getSubscriptionInfo(false /* forceReload */); + for (int i = 0; i < list.size(); i++) { + SubscriptionInfo info = list.get(i); + if (subId == info.getSubscriptionId()) return info; + } + return null; // not found + } } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 2b40903d005fa..de72ddd4b6bf4 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -121,9 +121,10 @@ public class KeyguardUpdateMonitorCallback { /** * Called when the SIM state changes. + * @param slotId * @param simState */ - public void onSimStateChanged(IccCardConstants.State simState) { } + public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) { } /** * Called when a user is removed. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 20e418cb44cee..5ec61fad5f3dc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -42,6 +42,7 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Log; @@ -56,6 +57,7 @@ import com.android.internal.policy.IKeyguardExitCallback; import com.android.internal.policy.IKeyguardShowCallback; import com.android.internal.telephony.IccCardConstants; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardConstants; import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; @@ -115,7 +117,8 @@ public class KeyguardViewMediator extends SystemUI { private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000; private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000; - final static boolean DEBUG = false; + private static final boolean DEBUG = KeyguardConstants.DEBUG; + private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES; private final static boolean DBG_WAKE = false; private final static String TAG = "KeyguardViewMediator"; @@ -358,8 +361,12 @@ public class KeyguardViewMediator extends SystemUI { } @Override - public void onSimStateChanged(IccCardConstants.State simState) { - if (DEBUG) Log.d(TAG, "onSimStateChanged: " + simState); + public void onSimStateChanged(int subId, int slotId, IccCardConstants.State simState) { + + if (DEBUG_SIM_STATES) { + Log.d(TAG, "onSimStateChanged(subId=" + subId + ", slotId=" + slotId + + ",state=" + simState + ")"); + } switch (simState) { case NOT_READY: @@ -369,7 +376,7 @@ public class KeyguardViewMediator extends SystemUI { synchronized (this) { if (shouldWaitForProvisioning()) { if (!isShowing()) { - if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing," + if (DEBUG_SIM_STATES) Log.d(TAG, "ICC_ABSENT isn't showing," + " we need to show the keyguard since the " + "device isn't provisioned yet."); doKeyguardLocked(null); @@ -383,7 +390,8 @@ public class KeyguardViewMediator extends SystemUI { case PUK_REQUIRED: synchronized (this) { if (!isShowing()) { - if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't " + if (DEBUG_SIM_STATES) Log.d(TAG, + "INTENT_VALUE_ICC_LOCKED and keygaurd isn't " + "showing; need to show keyguard so user can enter sim pin"); doKeyguardLocked(null); } else { @@ -394,11 +402,11 @@ public class KeyguardViewMediator extends SystemUI { case PERM_DISABLED: synchronized (this) { if (!isShowing()) { - if (DEBUG) Log.d(TAG, "PERM_DISABLED and " + if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED and " + "keygaurd isn't showing."); doKeyguardLocked(null); } else { - if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to" + if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED, resetStateLocked to" + "show permanently disabled message in lockscreen."); resetStateLocked(); } @@ -411,6 +419,9 @@ public class KeyguardViewMediator extends SystemUI { } } break; + default: + if (DEBUG_SIM_STATES) Log.v(TAG, "Ignoring state: " + simState); + break; } } @@ -909,13 +920,13 @@ public class KeyguardViewMediator extends SystemUI { } // if the setup wizard hasn't run yet, don't show - final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", - false); - final IccCardConstants.State state = mUpdateMonitor.getSimState(); - final boolean lockedOrMissing = state.isPinLocked() - || ((state == IccCardConstants.State.ABSENT - || state == IccCardConstants.State.PERM_DISABLED) - && requireSim); + final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false); + final boolean absent = mUpdateMonitor.getNextSubIdForState( + IccCardConstants.State.ABSENT) != SubscriptionManager.INVALID_SUB_ID; + final boolean disabled = mUpdateMonitor.getNextSubIdForState( + IccCardConstants.State.PERM_DISABLED) != SubscriptionManager.INVALID_SUB_ID; + final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure() + || ((absent || disabled) && requireSim); if (!lockedOrMissing && shouldWaitForProvisioning()) { if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"