Files
frameworks_base/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
Lucas Dupin 00ce73a418 Fix issue where SIM PIN message would be visible
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
2019-08-02 16:20:47 +00:00

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);
}
}