Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
316 lines
9.2 KiB
JavaScript
316 lines
9.2 KiB
JavaScript
'use strict';
|
|
|
|
/* globals addMixin, DebugMixin, PinView, DEFAULTS, PIN_DEFAULTS */
|
|
/* exported PinController */
|
|
|
|
(function(exports) {
|
|
|
|
const PIN_STATE = {
|
|
UNKNOWN: 'unknown',
|
|
ENABLED: 'enabled',
|
|
DISABLED: 'disabled',
|
|
BLOCKED: 'blocked',
|
|
};
|
|
|
|
var _defaultPin = DEFAULTS.PIN_VALUE;
|
|
|
|
var PinController = function(simDataSource, id, viewId, templateId) {
|
|
this._simDataSource = simDataSource;
|
|
this._id = id;
|
|
this._view = new PinView(viewId, PIN_DEFAULTS.PIN_LEN, templateId);
|
|
|
|
addMixin(this, DebugMixin);
|
|
};
|
|
|
|
PinController.prototype = {
|
|
_id: null,
|
|
_simDataSource: null,
|
|
_view: null,
|
|
|
|
// TODO consider removing
|
|
_simPinState: PIN_STATE.UNKNOWN,
|
|
|
|
_pinCheckInProgress: false,
|
|
_lastInputedPin: null,
|
|
|
|
get pinCheckInProgress() {
|
|
return this._pinCheckInProgress;
|
|
},
|
|
|
|
set defaultPin(value) {
|
|
_defaultPin = value;
|
|
},
|
|
|
|
get view() {
|
|
return this._view._el;
|
|
},
|
|
|
|
hideView: function() {
|
|
this._view.hide();
|
|
},
|
|
|
|
verifyPin: function() {
|
|
return this._requestPinVerification().then((result) => {
|
|
if(result) {
|
|
this.hideView();
|
|
}
|
|
return result;
|
|
});
|
|
},
|
|
|
|
enablePin: function() {
|
|
return this._requestPinVerification()
|
|
.then((verified) => {
|
|
if(verified) {
|
|
this.debug('Pin verified');
|
|
this._simPinState = PIN_STATE.ENABLED;
|
|
this._lastInputedPin = null;
|
|
return true;
|
|
} else {
|
|
this.debug('Pin verification failed, pin probably disabled');
|
|
var pin = this._lastInputedPin;
|
|
this._lastInputedPin = null;
|
|
return this._enableSimPin(pin);
|
|
}
|
|
});
|
|
},
|
|
|
|
disablePin: function() {
|
|
return this._requestPinVerification()
|
|
.then((verified) => {
|
|
if(verified) {
|
|
this.debug('Pin verified');
|
|
var pin = this._lastInputedPin;
|
|
this._lastInputedPin = null;
|
|
return this._disableSimPin(pin);
|
|
} else {
|
|
this.debug('Pin verification failed, pin probably disabled');
|
|
this._lastInputedPin = null;
|
|
return true;
|
|
}
|
|
});
|
|
},
|
|
|
|
changePin: function() {
|
|
var pin;
|
|
return this._requestPinVerification(this._view.REQUEST_BEFORE_CHANGE)
|
|
.then((verified) => {
|
|
if(!verified) {
|
|
this.debug('Pin not verfied, not changing');
|
|
this._lastInputedPin = null;
|
|
return false;
|
|
}
|
|
|
|
pin = this._lastInputedPin;
|
|
this._lastInputedPin = null;
|
|
return this._getNewPin();
|
|
})
|
|
.then((newPin) => {
|
|
this.debug('New pin value ' + newPin);
|
|
var pinBytes = this._formatPin(pin);
|
|
var newPinBytes = this._formatPin(newPin);
|
|
return this._simDataSource.changePIN(pinBytes, newPinBytes);
|
|
})
|
|
.then((result) => this._handlePinChangeResult(result));
|
|
},
|
|
|
|
cancelPinAction: function() {
|
|
this._view.cancelPinRequest();
|
|
},
|
|
|
|
_handlePinChangeResult: function(result) {
|
|
this.debug('Pin change result ' + result);
|
|
if(result === '9000') {
|
|
return true;
|
|
} else {
|
|
this.debug('Unexpected error');
|
|
return false;
|
|
}
|
|
},
|
|
|
|
_getNewPin: function(prevAttempFailed) {
|
|
var newPin;
|
|
return this._getUserPin(PIN_DEFAULTS.MAX_PUK_ATTEMPTS, prevAttempFailed,
|
|
this._view.REQUEST_NEW_PIN)
|
|
.then((pin1) => {
|
|
newPin = pin1;
|
|
return this._getUserPin(PIN_DEFAULTS.MAX_PUK_ATTEMPTS, false,
|
|
this._view.REQUEST_CONFIRM);
|
|
})
|
|
.then((pin2) => {
|
|
if(newPin === pin2) {
|
|
return newPin;
|
|
}
|
|
|
|
this.debug('Pins do not match');
|
|
return this._getNewPin(true);
|
|
});
|
|
},
|
|
|
|
_requestPinVerification: function(reqType) {
|
|
this.debug('_requestPinVerification, SIM PIN : ' + this._simPinState);
|
|
// TODO verify if we need this
|
|
//if(this._pinCheckInProgress) {
|
|
// return Promise.resolve(false);
|
|
//}
|
|
|
|
this._pinCheckInProgress = true;
|
|
this._view.show();
|
|
return this._performVerification(PIN_DEFAULTS.MAX_PIN_ATTEMPTS, false, reqType)
|
|
.then((verified) => {
|
|
if(verified) {
|
|
//this._view.hide();
|
|
// TODO think this through
|
|
this._pinCheckInProgress = false;
|
|
}
|
|
|
|
return verified;
|
|
});
|
|
},
|
|
|
|
_getUserPin: function(attemptsLeft, lastVerFailed, reqType, pinLen) {
|
|
return new Promise((resolve, reject) => {
|
|
var listener = {
|
|
onEvent: (id, data) => {
|
|
this._view.removeListener(listener);
|
|
if (data.pin) {
|
|
this.debug('got PIN: ' + data.pin);
|
|
resolve(data.pin);
|
|
} else {
|
|
reject('pin-cancelled');
|
|
}
|
|
}
|
|
};
|
|
|
|
this._view.addListener(listener);
|
|
if(!this._view.isVisible) {
|
|
this._view.show();
|
|
}
|
|
|
|
this.debug('waiting for PIN input from user');
|
|
this._view.requestPin(attemptsLeft, lastVerFailed, reqType, pinLen);
|
|
});
|
|
},
|
|
|
|
_performVerification: function(attemptsLeft, lastVerFailed, reqType) {
|
|
return this._getUserPin(attemptsLeft, lastVerFailed, reqType)
|
|
.then((pin) => {
|
|
this._lastInputedPin = pin;
|
|
var pinBytes = this._formatPin(pin);
|
|
this.debug('verifying PIN ' + pinBytes);
|
|
return this._simDataSource.verifyPIN(pinBytes);
|
|
})
|
|
.then((result) => this._handleVerficationResult(result));
|
|
//TODO should we catch here?
|
|
},
|
|
|
|
_handleVerficationResult: function(result) {
|
|
this.debug('Verification finished, status word:' + result);
|
|
if(result === '9000') {
|
|
return true;
|
|
} else if(result === '63C0' || result === '6983') {
|
|
this.debug('Pin blocked');
|
|
return this._handlePinBlocked();
|
|
} else if(result.startsWith('63C')) {
|
|
var attemptsLeft = Number.parseInt(result[3]);
|
|
this.debug('Verification failed, tries left: ' + attemptsLeft);
|
|
return this._performVerification(attemptsLeft, true);
|
|
} else {
|
|
this.debug('Unknown status word, PIN probably not enabled');
|
|
return false;
|
|
}
|
|
},
|
|
|
|
_handlePinBlocked: function() {
|
|
this._simPinState = PIN_STATE.BLOCKED;
|
|
|
|
this.debug('PUK unblock flow start.');
|
|
var unblockRequested = () => this._performPinUnblock(PIN_DEFAULTS.MAX_PUK_ATTEMPTS);
|
|
return this._view.blockPin().then(unblockRequested);
|
|
},
|
|
|
|
_performPinUnblock: function(attemptsLeft, lastUnblockFailed) {
|
|
var puk;
|
|
return this._getUserPin(attemptsLeft, lastUnblockFailed,
|
|
this._view.REQUEST_PUK, PIN_DEFAULTS.MAX_SIM_PIN_LEN)
|
|
.then((inputedPuk) => {
|
|
this.debug('Inputed PUK ' + inputedPuk);
|
|
puk = inputedPuk;
|
|
return this._getNewPin(false);
|
|
})
|
|
.then((inputedPin) => {
|
|
this.debug('Inputed PIN ' + inputedPin);
|
|
var bytePuk = this._formatPin(puk);
|
|
var bytePin = this._formatPin(inputedPin);
|
|
// if we're part of disable/enable flow we need to save pin here
|
|
this._lastInputedPin = inputedPin;
|
|
return this._simDataSource.unblockPIN(bytePuk, bytePin);
|
|
})
|
|
.then((result) => this._handlePinUnblockResult(result));
|
|
},
|
|
|
|
_handlePinUnblockResult: function(result) {
|
|
this.debug('PIN unblock result ' + result);
|
|
if(result === '9000') {
|
|
return true;
|
|
} else if(result === '63C0' || !result.startsWith('63C')) {
|
|
this.debug('Complate block');
|
|
return this._view.blockPuk();
|
|
} else {
|
|
var attemptsLeft = Number.parseInt(result[3]);
|
|
this.debug('PIN unblock failed, tries left: ' + attemptsLeft);
|
|
return this._performPinUnblock(attemptsLeft, true);
|
|
}
|
|
},
|
|
|
|
_enableSimPin: function(pin) {
|
|
var pinBytes = this._formatPin(pin);
|
|
return this._simDataSource.enablePIN(pinBytes)
|
|
.then((result) => this._handlePinEnabledResult(result));
|
|
},
|
|
|
|
_handlePinEnabledResult: function(result) {
|
|
this.debug('PIN enable result ' + result);
|
|
if(result === '9000') {
|
|
this._simPinState = PIN_STATE.ENABLED;
|
|
//this._view.hide();
|
|
return true;
|
|
} else if(result === '63C0' || result === '6983') {
|
|
this.debug('Pin blocked');
|
|
return this._handlePinBlocked();
|
|
} else {
|
|
var tries = result.startsWith('63C') ? Number.parseInt(result[3]) : 0;
|
|
this._view.showEnableFailed(tries, _defaultPin);
|
|
return false;
|
|
}
|
|
},
|
|
|
|
_disableSimPin: function(pin) {
|
|
var pinBytes = this._formatPin(pin);
|
|
return this._simDataSource.disablePIN(pinBytes)
|
|
.then((result) => this._handlePinDisableResult(result));
|
|
},
|
|
|
|
_handlePinDisableResult: function(result) {
|
|
this.debug('PIN disable result ' + result);
|
|
if(result === '9000') {
|
|
this._simPinState = PIN_STATE.DISABLED;
|
|
//this._view.hide();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
_formatPin: function(pin) {
|
|
var pinBytes = pin.split('').map((d) => d.charCodeAt());
|
|
for(var i = pinBytes.length; i < PIN_DEFAULTS.MAX_SIM_PIN_LEN; i++) {
|
|
pinBytes.push(PIN_DEFAULTS.PIN_PADDING);
|
|
}
|
|
|
|
return pinBytes;
|
|
}
|
|
};
|
|
|
|
exports.PinController = PinController;
|
|
}((typeof exports === 'undefined') ? window : exports));
|