blob: 4bc791255decc59746cd307c0ef1b34a47c2e29b [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Polymer element for displaying and modifying cellular sim info.
*/
/** @enum {string} */
const ErrorType = {
NONE: 'none',
INCORRECT_PIN: 'incorrect-pin',
INCORRECT_PUK: 'incorrect-puk',
MISMATCHED_PIN: 'mismatched-pin',
INVALID_PIN: 'invalid-pin',
INVALID_PUK: 'invalid-puk'
};
(function() {
const PIN_MIN_LENGTH = 4;
const PUK_MIN_LENGTH = 8;
const TOGGLE_DEBOUNCE_MS = 500;
Polymer({
is: 'network-siminfo',
behaviors: [I18nBehavior],
properties: {
/**
* The network properties associated with the element.
* @type {!CrOnc.NetworkProperties|undefined}
*/
networkProperties: {
type: Object,
observer: 'networkPropertiesChanged_',
},
/**
* Interface for networkingPrivate calls, passed from internet_page.
* @type {NetworkingPrivate}
*/
networkingPrivate: Object,
/**
* Reflects networkProperties.Cellular.SIMLockStatus.LockEnabled for the
* toggle button.
* @private
*/
lockEnabled_: {
type: Boolean,
value: false,
},
/**
* Set to true when a PUK is required to unlock the SIM.
* @private
*/
pukRequired_: {
type: Boolean,
value: false,
observer: 'pukRequiredChanged_',
},
/**
* Set to true when a SIM operation is in progress. Used to disable buttons.
* @private
*/
inProgress_: {
type: Boolean,
value: false,
observer: 'pinOrProgressChange_',
},
/**
* Set to an ErrorType value after an incorrect PIN or PUK entry.
* @private {ErrorType}
*/
error_: {
type: Object,
value: ErrorType.NONE,
},
/**
* Properties enabling pin/puk enter/change buttons.
* @private
*/
enterPinEnabled_: Boolean,
changePinEnabled_: Boolean,
changePukEnabled_: Boolean,
/**
* Properties reflecting pin/puk inputs.
* @private
*/
pin_: {
type: String,
observer: 'pinOrProgressChange_',
},
pin_new1_: {
type: String,
observer: 'pinOrProgressChange_',
},
pin_new2_: {
type: String,
observer: 'pinOrProgressChange_',
},
puk_: {
type: String,
observer: 'pinOrProgressChange_',
},
},
/** @private {boolean} */
sendSimLockEnabled_: false,
/** @private {boolean|undefined} */
setLockEnabled_: undefined,
/** @private {boolean} */
simUnlockSent_: false,
/** @override */
attached: function() {
this.simUnlockSent_ = false;
},
/** @override */
detached: function() {
this.closeDialogs_();
},
/** @private */
closeDialogs_: function() {
if (this.$.enterPinDialog.open) {
this.onEnterPinDialogCancel_();
this.$.enterPinDialog.close();
}
if (this.$.changePinDialog.open)
this.$.changePinDialog.close();
if (this.$.unlockPinDialog.open)
this.$.unlockPinDialog.close();
if (this.$.unlockPukDialog.open)
this.$.unlockPukDialog.close();
},
/** @private */
focusDialogInput_: function() {
if (this.$.enterPinDialog.open)
this.$.enterPin.focus();
else if (this.$.changePinDialog.open)
this.$.changePinOld.focus()();
else if (this.$.unlockPinDialog.open)
this.$.unlockPin.focus();
else if (this.$.unlockPukDialog.open)
this.$.unlockPuk.focus();
},
/** @private */
networkPropertiesChanged_: function() {
if (!this.networkProperties || !this.networkProperties.Cellular)
return;
const simLockStatus = this.networkProperties.Cellular.SIMLockStatus;
this.pukRequired_ =
!!simLockStatus && simLockStatus.LockType == CrOnc.LockType.PUK;
const lockEnabled = !!simLockStatus && simLockStatus.LockEnabled;
if (lockEnabled != this.lockEnabled_) {
this.setLockEnabled_ = lockEnabled;
this.updateLockEnabled_();
} else {
this.setLockEnabled_ = undefined;
}
},
/**
* Wrapper method to prevent changing |lockEnabled_| while a dialog is open
* to avoid confusion while a SIM operation is in progress. This must be
* called after closing any dialog (and not opening another) to set the
* correct state.
* @private
*/
updateLockEnabled_: function() {
if (this.setLockEnabled_ === undefined || this.$.enterPinDialog.open ||
this.$.changePinDialog.open || this.$.unlockPinDialog.open ||
this.$.unlockPukDialog.open) {
return;
}
this.lockEnabled_ = this.setLockEnabled_;
this.setLockEnabled_ = undefined;
},
/** @private */
delayUpdateLockEnabled_: function() {
setTimeout(() => {
this.updateLockEnabled_();
}, TOGGLE_DEBOUNCE_MS);
},
/** @private */
pinOrProgressChange_: function() {
this.enterPinEnabled_ = !this.inProgress_ && !!this.pin_;
this.changePinEnabled_ = !this.inProgress_ && !!this.pin_ &&
!!this.pin_new1_ && !!this.pin_new2_;
this.changePukEnabled_ = !this.inProgress_ && !!this.puk_ &&
!!this.pin_new1_ && !!this.pin_new2_;
},
/** @private */
pukRequiredChanged_: function() {
if (this.$.unlockPukDialog.open) {
if (this.pukRequired_) {
this.$.unlockPuk.focus();
} else {
this.$.unlockPukDialog.close();
this.delayUpdateLockEnabled_();
}
return;
}
if (!this.pukRequired_)
return;
// If the PUK was activated while attempting to enter or change a pin,
// close the dialog and open the unlock PUK dialog.
let showUnlockPuk = false;
if (this.$.enterPinDialog.open) {
this.$.enterPinDialog.close();
showUnlockPuk = true;
}
if (this.$.changePinDialog.open) {
this.$.changePinDialog.close();
showUnlockPuk = true;
}
if (this.$.unlockPinDialog.open) {
this.$.unlockPinDialog.close();
showUnlockPuk = true;
}
if (!showUnlockPuk)
return;
this.showUnlockPukDialog_();
},
/**
* Opens the pin dialog when the sim lock enabled state changes.
* @param {!Event} event
* @private
*/
onSimLockEnabledChange_: function(event) {
if (!this.networkProperties || !this.networkProperties.Cellular)
return;
this.sendSimLockEnabled_ = event.target.checked;
this.error_ = ErrorType.NONE;
this.$.enterPin.value = '';
this.$.enterPinDialog.showModal();
requestAnimationFrame(() => {
this.$.enterPin.focus();
});
},
/** @private */
setInProgress_: function() {
this.error_ = ErrorType.NONE;
this.inProgress_ = true;
this.simUnlockSent_ = true;
},
/**
* @param {!CrOnc.CellularSimState} simState
* @private
*/
setCellularSimState_: function(simState) {
const guid = (this.networkProperties && this.networkProperties.GUID) || '';
this.setInProgress_();
this.networkingPrivate.setCellularSimState(guid, simState, () => {
this.inProgress_ = false;
if (chrome.runtime.lastError) {
this.error_ = ErrorType.INCORRECT_PIN;
this.focusDialogInput_();
} else {
this.error_ = ErrorType.NONE;
this.closeDialogs_();
this.delayUpdateLockEnabled_();
}
});
},
/**
* @param {string} pin
* @param {string|undefined} puk
* @private
*/
unlockCellularSim_: function(pin, puk) {
const guid = (this.networkProperties && this.networkProperties.GUID) || '';
this.setInProgress_();
this.networkingPrivate.unlockCellularSim(guid, pin, puk, () => {
this.inProgress_ = false;
if (chrome.runtime.lastError) {
this.error_ = puk ? ErrorType.INCORRECT_PUK : ErrorType.INCORRECT_PIN;
this.focusDialogInput_();
} else {
this.error_ = ErrorType.NONE;
this.$.closeDialogs_();
this.delayUpdateLockEnabled_();
}
});
},
/**
* Sends the PIN value from the Enter PIN dialog.
* @param {!Event} event
* @private
*/
sendEnterPin_: function(event) {
event.stopPropagation();
if (!this.enterPinEnabled_)
return;
const pin = this.$.enterPin.value;
if (!this.validatePin_(pin))
return;
const simState = /** @type {!CrOnc.CellularSimState} */ ({
currentPin: pin,
requirePin: this.sendSimLockEnabled_,
});
this.setCellularSimState_(simState);
},
/**
* Opens the Change PIN dialog.
* @param {!Event} event
* @private
*/
onChangePinTap_: function(event) {
event.stopPropagation();
if (!this.networkProperties || !this.networkProperties.Cellular)
return;
this.error_ = ErrorType.NONE;
this.$.changePinOld.value = '';
this.$.changePinNew1.value = '';
this.$.changePinNew2.value = '';
this.$.changePinDialog.showModal();
requestAnimationFrame(() => {
this.$.changePinOld.focus();
});
},
/**
* Sends the old and new PIN values from the Change PIN dialog.
* @param {!Event} event
* @private
*/
sendChangePin_: function(event) {
event.stopPropagation();
const newPin = this.$.changePinNew1.value;
if (!this.validatePin_(newPin, this.$.changePinNew2.value))
return;
const simState = /** @type {!CrOnc.CellularSimState} */ ({
requirePin: true,
currentPin: this.$.changePinOld.value,
newPin: newPin
});
this.setCellularSimState_(simState);
},
/**
* Opens the Unlock PIN / PUK dialog.
* @param {!Event} event
* @private
*/
onUnlockPinTap_: function(event) {
event.stopPropagation();
if (this.pukRequired_) {
this.showUnlockPukDialog_();
} else {
this.showUnlockPinDialog_();
}
},
/**
* Sends the PIN value from the Unlock PIN dialog.
* @param {!Event} event
* @private
*/
sendUnlockPin_: function(event) {
event.stopPropagation();
const pin = this.$.unlockPin.value;
if (!this.validatePin_(pin))
return;
this.unlockCellularSim_(pin, '');
},
/** @private */
showUnlockPinDialog_: function() {
this.error_ = ErrorType.NONE;
this.$.unlockPin.value = '';
this.$.unlockPinDialog.showModal();
requestAnimationFrame(() => {
this.$.unlockPin.focus();
});
},
/** @private */
showUnlockPukDialog_: function() {
this.error_ = ErrorType.NONE;
this.$.unlockPuk.value = '';
this.$.unlockPin1.value = '';
this.$.unlockPin2.value = '';
this.$.unlockPukDialog.showModal();
requestAnimationFrame(() => {
this.$.unlockPuk.focus();
});
},
/**
* Sends the PUK value and new PIN value from the Unblock PUK dialog.
* @param {!Event} event
* @private
*/
sendUnlockPuk_: function(event) {
event.stopPropagation();
const puk = this.$.unlockPuk.value;
if (!this.validatePuk_(puk))
return;
const pin = this.$.unlockPin1.value;
if (!this.validatePin_(pin, this.$.unlockPin2.value))
return;
this.unlockCellularSim_(pin, puk);
},
/**
* @return {boolean}
* @private
*/
showSimLocked_: function() {
if (!this.networkProperties || !this.networkProperties.Cellular ||
!this.networkProperties.Cellular.SIMPresent) {
return false;
}
return CrOnc.isSimLocked(this.networkProperties);
},
/**
* @return {boolean}
* @private
*/
showSimUnlocked_: function() {
if (!this.networkProperties || !this.networkProperties.Cellular ||
!this.networkProperties.Cellular.SIMPresent) {
return false;
}
return !CrOnc.isSimLocked(this.networkProperties);
},
/** @private */
getErrorMsg_: function() {
if (this.error_ == ErrorType.NONE)
return '';
// TODO(stevenjb): Translate
let msg;
if (this.error_ == ErrorType.INCORRECT_PIN)
msg = 'Incorrect PIN.';
else if (this.error_ == ErrorType.INCORRECT_PUK)
msg = 'Incorrect PUK.';
else if (this.error_ == ErrorType.MISMATCHED_PIN)
msg = 'PIN values do not match.';
else if (this.error_ == ErrorType.INVALID_PIN)
msg = 'Invalid PIN.';
else if (this.error_ == ErrorType.INVALID_PUK)
msg = 'Invalid PUK.';
else
return 'UNKNOWN ERROR';
const retriesLeft = this.simUnlockSent_ &&
this.get('Cellular.SIMLockStatus.RetriesLeft', this.networkProperties);
if (retriesLeft) {
msg += ' Retries left: ' + retriesLeft.toString();
}
return msg;
},
/**
* Checks whether |pin1| is of the proper length and if opt_pin2 is not
* undefined, whether pin1 and opt_pin2 match. On any failure, sets
* |this.error_| and returns false.
* @param {string} pin1
* @param {string=} opt_pin2
* @return {boolean} True if the pins match and are of minimum length.
* @private
*/
validatePin_: function(pin1, opt_pin2) {
if (!pin1.length)
return false;
if (pin1.length < PIN_MIN_LENGTH) {
this.error_ = ErrorType.INVALID_PIN;
return false;
}
if (opt_pin2 != undefined && pin1 != opt_pin2) {
this.error_ = ErrorType.MISMATCHED_PIN;
return false;
}
return true;
},
/**
* Checks whether |puk| is of the proper length. If not, sets |this.error_|
* and returns false.
* @param {string} puk
* @return {boolean} True if the puk is of minimum length.
* @private
*/
validatePuk_: function(puk) {
if (puk.length < PUK_MIN_LENGTH) {
this.error_ = ErrorType.INVALID_PUK;
return false;
}
return true;
},
/** @private */
onEnterPinDialogCancel_: function() {
this.lockEnabled_ =
this.networkProperties.Cellular.SIMLockStatus.LockEnabled;
},
/** @private */
onEnterPinDialogClose_: function() {
cr.ui.focusWithoutInk(assert(this.$$('#simLockButton')));
},
/** @private */
onChangePinDialogClose_: function() {
cr.ui.focusWithoutInk(assert(this.$$('#changePinButton')));
},
/** @private */
onUnlockPinDialogClose_: function() {
cr.ui.focusWithoutInk(assert(this.$$('#unlockPinButton')));
},
});
})();