'use strict'; /* global SettingsListener, Service */ (function(exports) { const DEBUG = false; var debug = function(str) { //dump('usb_storage: ' + str + '\n'); console.log('usb_storage: ' + str); }; /** * UsbStorage listens to lock and unlock events and changes the * setting which controls automount behavior of USB storage. * Storage operates both on lock/unlock events, as well as the * ums.enabled setting which is set in the settings app. * * Once storage has been enabled (which requires the phone to be * unlocked), we want to keep it enabled, even if the phone is * locked afterwards. However, unplugging the USB cable should * then require being unlocked before enabling again. * * This also needs to work properly if the lockscreen is * disabled. * * Some further potential wrinkles: * * - MTP can be enabled/disabled at will * - UMS can be enabled at will, but requires a USB cable unplug * to complete. * * @class UsbStorage */ function UsbStorage() { // The AutoMounter already sets ums.mode to disabled at startup, so we // just set our internal state. We shouldn't blindly call _setMode since // when B2G is restarted, it only restarts the b2g executable, and // vold isn't restarted. So we need to adapt to the initial conditions // not set them. this._mode = this.automounterDisable; this.bindUsbStorageChanged = this._usbStorageChanged.bind(this); } UsbStorage.prototype = { /** * ums.mode setting value when the automounter is disabled. * @memberof UsbStorage.prototype * @type {Number} */ automounterDisable: 0, /** * ums.mode setting value when the automounter is enabled. * @memberof UsbStorage.prototype * @type {Number} */ automounterUmsEnable: 1, /** * ums.mode setting value when the automounter is disabled * during the lock event. * @memberof UsbStorage.prototype * @type {Number} */ automounterDisableWhenUnplugged: 2, /** * ums.mode setting value when the automounter is in mtp modenot * mcuy * @memberof UsbStorage.prototype * @type {Number} */ automounterMtpEnable: 3, /** * usb.transter setting value for ums mode * @memberof UsbStorage.prototype * @type {String} */ protocolUMS: '0', /** * ums.mode setting value for mtp mode * @memberof UsbStorage.prototype * @type {String} */ protocolMTP: '1', /** * The name of the setting to enable or disable USB storage. * @memberof UsbStorage.prototype * @type {String} */ umsEnabled: 'ums.enabled', /** * The name of the setting that defines automount behavior. * @memberof UsbStorage.prototype * @type {String} */ umsMode: 'ums.mode', /** * The name of the setting that defines usb transfer protocol. * @memberof UsbStorage.prototype * @type {String} */ usbTransferProtocol: 'usb.transfer', /** * The current value of USB tranfer mode. * @memberof UsbStorage.prototype * @type {String} */ _mode: undefined, /** * The current value of whether or not USB storage is enabled. * @memberof UsbStorage.prototype * @type {Boolean} */ _enabled: undefined, /** * The current value of current USB transfer protocol. * @memberof UsbStorage.prototype * @type {String} */ _protocol: undefined, /** * start usb storage * @memberof UsbStorage.prototype */ start: function() { DEBUG && debug('UsbStorage.start called'); window.addEventListener('lockscreen-appopened', this); window.addEventListener('lockscreen-appclosed', this); SettingsListener.observe(this.umsEnabled, false, this.bindUsbStorageChanged); }, /** * stop usb storage * @memberof UsbStorage.prototype */ stop: function() { DEBUG && debug('UsbStorage.stop called'); window.removeEventListener('lockscreen-appopened', this); window.removeEventListener('lockscreen-appclosed', this); SettingsListener.unobserve(this.umsEnabled, this.bindUsbStorageChanged); }, /** * Get a string version of the protocol. * @memberof UsbStorage.prototype * @param {Number} protocol current protocol denotes ums or mtp. */ _protocolStr: function(protocol) { if (protocol == this.protocolUMS) { return 'UMS'; } if (protocol == this.protocolMTP) { return 'MTP'; } return '???'; }, /** * Get a string version of the mode. * @memberof UsbStorage.prototype * @param {Number} mode for automounter. */ _modeStr: function(mode) { if (mode == this.automounterDisable) { return 'Disabled'; } if (mode == this.automounterUmsEnable) { return 'Enable-UMS'; } if (mode == this.automounterDisableWhenUnplugged) { return 'DisabledWhenUnplugged'; } if (mode == this.automounterMtpEnable) { return 'Enable-MTP'; } return '???'; }, /** * Handle USB storage enabled/disabled functionality. * @memberof UsbStorage.prototype * @param {Boolean} enabled enables/disables automounting. */ _usbStorageChanged: function(enabled) { this._enabled = enabled; DEBUG && debug('USB Storage Changed: ' + enabled); this._getUsbProtocol(); }, /** * Retrieve the protocol so that that we know whether MTP or UMS * was enabled/disabled. * @memberof UsbStorage.prototype */ _getUsbProtocol: function() { var req = navigator.mozSettings.createLock() .get(this.usbTransferProtocol); req.onsuccess = function() { var protocol = this._keyMigration( req.result[this.usbTransferProtocol]); this._protocol = protocol; DEBUG && debug('_getUsbProtocol: ' + this._protocolStr(protocol)); this._updateMode(); }.bind(this); }, /** * Set mtp mode as default value. * @memberof UsbStorage.prototype * @param {Number} protocol current protocol denotes ums or mtp. */ _keyMigration: function(protocol) { if (protocol === undefined || protocol === 'auto') { protocol = this.protocolUMS; var storages = navigator.getDeviceStorages('sdcard'); for (var i = 0; i < storages.length; i++) { var storage = storages[i]; if (storage.storageName == 'sdcard') { if (!storage.canBeShared) { protocol = this.protocolMTP; } break; } } var cset = {}; cset[this.usbTransferProtocol] = protocol; navigator.mozSettings.createLock().set(cset); } return protocol; }, /** * Sets the mode to agree with the current state of things. * @memberof UsbStorage.prototype */ _updateMode: function() { if (typeof(this._enabled) == 'undefined') { DEBUG && debug('_updateMode: _enabled not yet set - ignoring'); return; } var mode = this.automounterDisable; if (this._enabled && !Service.query('locked')) { // This is the only time that UMS or MTP should be enabled. if (this._protocol == this.protocolMTP) { mode = this.automounterMtpEnable; } else { mode = this.automounterUmsEnable; } } else { // DisableWhenUnplugged can also be interpreted as EnableUntilUnplugged // so we need to make sure that we only transition to // automounterDisableWhenUnplugged if we're already in an enabled mode. if (this._mode != this.automounterDisable && (this._protocol == this.protocolUMS || this._enabled)) { // For UMS, we can't disable immediately, so we always defer // to when the usb cable is unplugged. // For MTP, if the user disables it, we can act immediately, but // for screen lock we want to defer until the usb cable is unplugged. mode = this.automounterDisableWhenUnplugged; } } this._setMode(mode); }, /** * Sets the automount mode. This is split into a separate * function to facilitate testing, and should only be called * from _updateMode(). * @memberof UsbStorage.prototype * @param {Number} val The value we are setting automount to. * */ _setMode: function(mode) { if (mode != this._mode) { this._mode = mode; var param = {}; param[this.umsMode] = mode; SettingsListener.getSettingsLock().set(param); } }, /** * General event handler interface. * Updates the overlay with as we receive load events. * @memberof UsbStorage.prototype * @param {DOMEvent} evt The event. */ handleEvent: function(e) { switch (e.type) { case 'lockscreen-appopened': case 'lockscreen-appclosed': this._updateMode(); break; default: return; } } }; exports.UsbStorage = UsbStorage; }(window));