We're resetting the view state when a config change happens, but they can arrive even after the view is not visible anymore. Let's not update the message area if we're GONE, since the message area is shared with the Pattern and PIN pad views. Test: manual Fixes: 138751729 Change-Id: Idb4877f8ed22e8b640670bb9da66bb124a2f8166
395 lines
15 KiB
Java
395 lines
15 KiB
Java
/*
|
|
* Copyright (C) 2012 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.keyguard;
|
|
|
|
import android.app.AlertDialog;
|
|
import android.app.AlertDialog.Builder;
|
|
import android.app.Dialog;
|
|
import android.app.ProgressDialog;
|
|
import android.content.Context;
|
|
import android.content.res.ColorStateList;
|
|
import android.content.res.Configuration;
|
|
import android.content.res.Resources;
|
|
import android.content.res.TypedArray;
|
|
import android.graphics.Color;
|
|
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.View;
|
|
import android.view.WindowManager;
|
|
import android.widget.ImageView;
|
|
|
|
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;
|
|
|
|
/**
|
|
* Displays a PIN pad for unlocking.
|
|
*/
|
|
public class KeyguardSimPinView extends KeyguardPinBasedInputView {
|
|
private static final String LOG_TAG = "KeyguardSimPinView";
|
|
private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
|
|
public static final String TAG = "KeyguardSimPinView";
|
|
|
|
private ProgressDialog mSimUnlockProgressDialog = null;
|
|
private CheckSimPin mCheckSimPinThread;
|
|
|
|
// Below flag is set to true during power-up or when a new SIM card inserted on device.
|
|
// When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
|
|
// be displayed to inform user about the number of remaining PIN attempts left.
|
|
private boolean mShowDefaultMessage = true;
|
|
private int mRemainingAttempts = -1;
|
|
private AlertDialog mRemainingAttemptsDialog;
|
|
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
|
private ImageView mSimImageView;
|
|
|
|
KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
|
|
@Override
|
|
public void onSimStateChanged(int subId, int slotId, State simState) {
|
|
if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
|
|
switch(simState) {
|
|
case READY: {
|
|
mRemainingAttempts = -1;
|
|
resetState();
|
|
break;
|
|
}
|
|
default:
|
|
resetState();
|
|
}
|
|
}
|
|
};
|
|
|
|
public KeyguardSimPinView(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
public KeyguardSimPinView(Context context, AttributeSet attrs) {
|
|
super(context, attrs);
|
|
}
|
|
|
|
@Override
|
|
public void resetState() {
|
|
super.resetState();
|
|
if (DEBUG) Log.v(TAG, "Resetting state");
|
|
handleSubInfoChangeIfNeeded();
|
|
if (mShowDefaultMessage) {
|
|
showDefaultMessage();
|
|
}
|
|
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
|
|
|
|
KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
|
|
esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
|
|
}
|
|
|
|
private void setLockedSimMessage() {
|
|
boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
|
|
int count = TelephonyManager.getDefault().getSimCount();
|
|
Resources rez = getResources();
|
|
String msg;
|
|
TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
|
|
int color = array.getColor(0, Color.WHITE);
|
|
array.recycle();
|
|
if (count < 2) {
|
|
msg = rez.getString(R.string.kg_sim_pin_instructions);
|
|
} else {
|
|
SubscriptionInfo info = KeyguardUpdateMonitor.getInstance(mContext).
|
|
getSubscriptionInfoForSubId(mSubId);
|
|
CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
|
|
msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
|
|
if (info != null) {
|
|
color = info.getIconTint();
|
|
}
|
|
}
|
|
if (isEsimLocked) {
|
|
msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
|
|
}
|
|
|
|
if (mSecurityMessageDisplay != null && getVisibility() == VISIBLE) {
|
|
mSecurityMessageDisplay.setMessage(msg);
|
|
}
|
|
mSimImageView.setImageTintList(ColorStateList.valueOf(color));
|
|
}
|
|
|
|
private void showDefaultMessage() {
|
|
setLockedSimMessage();
|
|
if (mRemainingAttempts >= 0) {
|
|
return;
|
|
}
|
|
|
|
// Sending empty PIN here to query the number of remaining PIN attempts
|
|
new CheckSimPin("", mSubId) {
|
|
void onSimCheckResponse(final int result, final int attemptsRemaining) {
|
|
Log.d(LOG_TAG, "onSimCheckResponse " + " dummy One result" + result +
|
|
" attemptsRemaining=" + attemptsRemaining);
|
|
if (attemptsRemaining >= 0) {
|
|
mRemainingAttempts = attemptsRemaining;
|
|
setLockedSimMessage();
|
|
}
|
|
}
|
|
}.start();
|
|
}
|
|
|
|
private void handleSubInfoChangeIfNeeded() {
|
|
KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
|
|
int subId = monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED);
|
|
if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
|
|
mSubId = subId;
|
|
mShowDefaultMessage = true;
|
|
mRemainingAttempts = -1;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onConfigurationChanged(Configuration newConfig) {
|
|
super.onConfigurationChanged(newConfig);
|
|
resetState();
|
|
}
|
|
|
|
@Override
|
|
protected int getPromptReasonStringRes(int reason) {
|
|
// No message on SIM Pin
|
|
return 0;
|
|
}
|
|
|
|
private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
|
|
String displayMessage;
|
|
int msgId;
|
|
if (attemptsRemaining == 0) {
|
|
displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
|
|
} else if (attemptsRemaining > 0) {
|
|
msgId = isDefault ? R.plurals.kg_password_default_pin_message :
|
|
R.plurals.kg_password_wrong_pin_code;
|
|
displayMessage = getContext().getResources()
|
|
.getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
|
|
} else {
|
|
msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
|
|
displayMessage = getContext().getString(msgId);
|
|
}
|
|
if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
|
|
displayMessage = getResources()
|
|
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
|
|
}
|
|
if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
|
|
+ " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
|
|
return displayMessage;
|
|
}
|
|
|
|
@Override
|
|
protected boolean shouldLockout(long deadline) {
|
|
// SIM PIN doesn't have a timed lockout
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected int getPasswordTextViewId() {
|
|
return R.id.simPinEntry;
|
|
}
|
|
|
|
@Override
|
|
protected void onFinishInflate() {
|
|
super.onFinishInflate();
|
|
|
|
if (mEcaView instanceof EmergencyCarrierArea) {
|
|
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
|
|
}
|
|
mSimImageView = findViewById(R.id.keyguard_sim);
|
|
}
|
|
|
|
@Override
|
|
public void showUsabilityHint() {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onResume(int reason) {
|
|
super.onResume(reason);
|
|
KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
|
|
resetState();
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
// dismiss the dialog.
|
|
if (mSimUnlockProgressDialog != null) {
|
|
mSimUnlockProgressDialog.dismiss();
|
|
mSimUnlockProgressDialog = null;
|
|
}
|
|
KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallback);
|
|
}
|
|
|
|
/**
|
|
* Since the IPC can block, we want to run the request in a separate thread
|
|
* with a callback.
|
|
*/
|
|
private abstract class CheckSimPin extends Thread {
|
|
private final String mPin;
|
|
private int mSubId;
|
|
|
|
protected CheckSimPin(String pin, int subId) {
|
|
mPin = pin;
|
|
mSubId = subId;
|
|
}
|
|
|
|
abstract void onSimCheckResponse(final int result, final int attemptsRemaining);
|
|
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
if (DEBUG) {
|
|
Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
|
|
}
|
|
final int[] result = ITelephony.Stub.asInterface(ServiceManager
|
|
.checkService("phone")).supplyPinReportResultForSubscriber(mSubId, mPin);
|
|
if (DEBUG) {
|
|
Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]);
|
|
}
|
|
post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
onSimCheckResponse(result[0], result[1]);
|
|
}
|
|
});
|
|
} catch (RemoteException e) {
|
|
Log.e(TAG, "RemoteException for supplyPinReportResult:", e);
|
|
post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private Dialog getSimUnlockProgressDialog() {
|
|
if (mSimUnlockProgressDialog == null) {
|
|
mSimUnlockProgressDialog = new ProgressDialog(mContext);
|
|
mSimUnlockProgressDialog.setMessage(
|
|
mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
|
|
mSimUnlockProgressDialog.setIndeterminate(true);
|
|
mSimUnlockProgressDialog.setCancelable(false);
|
|
mSimUnlockProgressDialog.getWindow().setType(
|
|
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
|
|
}
|
|
return mSimUnlockProgressDialog;
|
|
}
|
|
|
|
private Dialog getSimRemainingAttemptsDialog(int remaining) {
|
|
String msg = getPinPasswordErrorMessage(remaining, false);
|
|
if (mRemainingAttemptsDialog == null) {
|
|
Builder builder = new AlertDialog.Builder(mContext);
|
|
builder.setMessage(msg);
|
|
builder.setCancelable(false);
|
|
builder.setNeutralButton(R.string.ok, null);
|
|
mRemainingAttemptsDialog = builder.create();
|
|
mRemainingAttemptsDialog.getWindow().setType(
|
|
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
|
|
} else {
|
|
mRemainingAttemptsDialog.setMessage(msg);
|
|
}
|
|
return mRemainingAttemptsDialog;
|
|
}
|
|
|
|
@Override
|
|
protected void verifyPasswordAndUnlock() {
|
|
String entry = mPasswordEntry.getText();
|
|
|
|
if (entry.length() < 4) {
|
|
// otherwise, display a message to the user, and don't submit.
|
|
mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint);
|
|
resetPasswordText(true /* animate */, true /* announce */);
|
|
mCallback.userActivity();
|
|
return;
|
|
}
|
|
|
|
getSimUnlockProgressDialog().show();
|
|
|
|
if (mCheckSimPinThread == null) {
|
|
mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
|
|
@Override
|
|
void onSimCheckResponse(final int result, final int attemptsRemaining) {
|
|
post(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
mRemainingAttempts = attemptsRemaining;
|
|
if (mSimUnlockProgressDialog != null) {
|
|
mSimUnlockProgressDialog.hide();
|
|
}
|
|
resetPasswordText(true /* animate */,
|
|
result != PhoneConstants.PIN_RESULT_SUCCESS /* announce */);
|
|
if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
|
|
KeyguardUpdateMonitor.getInstance(getContext())
|
|
.reportSimUnlocked(mSubId);
|
|
mRemainingAttempts = -1;
|
|
mShowDefaultMessage = true;
|
|
if (mCallback != null) {
|
|
mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
|
|
}
|
|
} else {
|
|
mShowDefaultMessage = false;
|
|
if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
|
|
if (attemptsRemaining <= 2) {
|
|
// this is getting critical - show dialog
|
|
getSimRemainingAttemptsDialog(attemptsRemaining).show();
|
|
} else {
|
|
// show message
|
|
mSecurityMessageDisplay.setMessage(
|
|
getPinPasswordErrorMessage(attemptsRemaining, false));
|
|
}
|
|
} else {
|
|
// "PIN operation failed!" - no idea what this was and no way to
|
|
// find out. :/
|
|
mSecurityMessageDisplay.setMessage(getContext().getString(
|
|
R.string.kg_password_pin_failed));
|
|
}
|
|
if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
|
|
+ " CheckSimPin.onSimCheckResponse: " + result
|
|
+ " attemptsRemaining=" + attemptsRemaining);
|
|
}
|
|
mCallback.userActivity();
|
|
mCheckSimPinThread = null;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
mCheckSimPinThread.start();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void startAppearAnimation() {
|
|
// noop.
|
|
}
|
|
|
|
@Override
|
|
public boolean startDisappearAnimation(Runnable finishRunnable) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public CharSequence getTitle() {
|
|
return getContext().getString(
|
|
com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock);
|
|
}
|
|
}
|
|
|