diff options
Diffstat (limited to 'b2g/chrome/content/devtools/adb.js')
-rw-r--r-- | b2g/chrome/content/devtools/adb.js | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/b2g/chrome/content/devtools/adb.js b/b2g/chrome/content/devtools/adb.js new file mode 100644 index 000000000..cebc6696b --- /dev/null +++ b/b2g/chrome/content/devtools/adb.js @@ -0,0 +1,233 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// This file is only loaded on Gonk to manage ADB state + +Components.utils.import("resource://gre/modules/FileUtils.jsm"); + +const DEBUG = false; +var debug = function(str) { + dump("AdbController: " + str + "\n"); +} + +var AdbController = { + locked: undefined, + remoteDebuggerEnabled: undefined, + lockEnabled: undefined, + disableAdbTimer: null, + disableAdbTimeoutHours: 12, + umsActive: false, + + setLockscreenEnabled: function(value) { + this.lockEnabled = value; + DEBUG && debug("setLockscreenEnabled = " + this.lockEnabled); + this.updateState(); + }, + + setLockscreenState: function(value) { + this.locked = value; + DEBUG && debug("setLockscreenState = " + this.locked); + this.updateState(); + }, + + setRemoteDebuggerState: function(value) { + this.remoteDebuggerEnabled = value; + DEBUG && debug("setRemoteDebuggerState = " + this.remoteDebuggerEnabled); + this.updateState(); + }, + + startDisableAdbTimer: function() { + if (this.disableAdbTimer) { + this.disableAdbTimer.cancel(); + } else { + this.disableAdbTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + try { + this.disableAdbTimeoutHours = + Services.prefs.getIntPref("b2g.adb.timeout-hours"); + } catch (e) { + // This happens if the pref doesn't exist, in which case + // disableAdbTimeoutHours will still be set to the default. + } + } + if (this.disableAdbTimeoutHours <= 0) { + DEBUG && debug("Timer to disable ADB not started due to zero timeout"); + return; + } + + DEBUG && debug("Starting timer to disable ADB in " + + this.disableAdbTimeoutHours + " hours"); + let timeoutMilliseconds = this.disableAdbTimeoutHours * 60 * 60 * 1000; + this.disableAdbTimer.initWithCallback(this, timeoutMilliseconds, + Ci.nsITimer.TYPE_ONE_SHOT); + }, + + stopDisableAdbTimer: function() { + DEBUG && debug("Stopping timer to disable ADB"); + if (this.disableAdbTimer) { + this.disableAdbTimer.cancel(); + this.disableAdbTimer = null; + } + }, + + notify: function(aTimer) { + if (aTimer == this.disableAdbTimer) { + this.disableAdbTimer = null; + // The following dump will be the last thing that shows up in logcat, + // and will at least give the user a clue about why logcat was + // disconnected, if the user happens to be using logcat. + debug("ADB timer expired - disabling ADB\n"); + navigator.mozSettings.createLock().set( + {'debugger.remote-mode': 'disabled'}); + } + }, + + updateState: function() { + this.umsActive = false; + }, + + updateStateInternal: function() { + DEBUG && debug("updateStateInternal: called"); + + if (this.remoteDebuggerEnabled === undefined || + this.lockEnabled === undefined || + this.locked === undefined) { + // Part of initializing the settings database will cause the observers + // to trigger. We want to wait until both have been initialized before + // we start changing ther adb state. Without this then we can wind up + // toggling adb off and back on again (or on and back off again). + // + // For completeness, one scenario which toggles adb is using the unagi. + // The unagi has adb enabled by default (prior to b2g starting). If you + // have the phone lock disabled and remote debugging enabled, then we'll + // receive an unlock event and an rde event. However at the time we + // receive the unlock event we haven't yet received the rde event, so + // we turn adb off momentarily, which disconnects a logcat that might + // be running. Changing the defaults (in AdbController) just moves the + // problem to a different phone, which has adb disabled by default and + // we wind up turning on adb for a short period when we shouldn't. + // + // By waiting until both values are properly initialized, we avoid + // turning adb on or off accidentally. + DEBUG && debug("updateState: Waiting for all vars to be initialized"); + return; + } + + // Check if we have a remote debugging session going on. If so, we won't + // disable adb even if the screen is locked. + let isDebugging = USBRemoteDebugger.isDebugging; + DEBUG && debug("isDebugging=" + isDebugging); + + // If USB Mass Storage, USB tethering, or a debug session is active, + // then we don't want to disable adb in an automatic fashion (i.e. + // when the screen locks or due to timeout). + let sysUsbConfig = libcutils.property_get("sys.usb.config").split(","); + let usbFuncActive = this.umsActive || isDebugging; + usbFuncActive |= (sysUsbConfig.indexOf("rndis") >= 0); + usbFuncActive |= (sysUsbConfig.indexOf("mtp") >= 0); + + let enableAdb = this.remoteDebuggerEnabled && + (!(this.lockEnabled && this.locked) || usbFuncActive); + + let useDisableAdbTimer = true; + try { + if (Services.prefs.getBoolPref("marionette.defaultPrefs.enabled")) { + // Marionette is enabled. Marionette requires that adb be on (and also + // requires that remote debugging be off). The fact that marionette + // is enabled also implies that we're doing a non-production build, so + // we want adb enabled all of the time. + enableAdb = true; + useDisableAdbTimer = false; + } + } catch (e) { + // This means that the pref doesn't exist. Which is fine. We just leave + // enableAdb alone. + } + + // Check wakelock to prevent adb from disconnecting when phone is locked + let lockFile = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile); + lockFile.initWithPath('/sys/power/wake_lock'); + if(lockFile.exists()) { + let foStream = Cc["@mozilla.org/network/file-input-stream;1"] + .createInstance(Ci.nsIFileInputStream); + let coStream = Cc["@mozilla.org/intl/converter-input-stream;1"] + .createInstance(Ci.nsIConverterInputStream); + let str = {}; + foStream.init(lockFile, FileUtils.MODE_RDONLY, 0, 0); + coStream.init(foStream, "UTF-8", 0, 0); + coStream.readString(-1, str); + coStream.close(); + foStream.close(); + let wakeLockContents = str.value.replace(/\n/, ""); + let wakeLockList = wakeLockContents.split(" "); + if (wakeLockList.indexOf("adb") >= 0) { + enableAdb = true; + useDisableAdbTimer = false; + DEBUG && debug("Keeping ADB enabled as ADB wakelock is present."); + } else { + DEBUG && debug("ADB wakelock not found."); + } + } else { + DEBUG && debug("Wake_lock file not found."); + } + + DEBUG && debug("updateState: enableAdb = " + enableAdb + + " remoteDebuggerEnabled = " + this.remoteDebuggerEnabled + + " lockEnabled = " + this.lockEnabled + + " locked = " + this.locked + + " usbFuncActive = " + usbFuncActive); + + // Configure adb. + let currentConfig = libcutils.property_get("persist.sys.usb.config"); + let configFuncs = currentConfig.split(","); + if (currentConfig == "" || currentConfig == "none") { + // We want to treat none like the empty string. + // "".split(",") yields [""] and not [] + configFuncs = []; + } + let adbIndex = configFuncs.indexOf("adb"); + + if (enableAdb) { + // Add adb to the list of functions, if not already present + if (adbIndex < 0) { + configFuncs.push("adb"); + } + } else { + // Remove adb from the list of functions, if present + if (adbIndex >= 0) { + configFuncs.splice(adbIndex, 1); + } + } + let newConfig = configFuncs.join(","); + if (newConfig == "") { + // Convert the empty string back into none, since that's what init.rc + // needs. + newConfig = "none"; + } + if (newConfig != currentConfig) { + DEBUG && debug("updateState: currentConfig = " + currentConfig); + DEBUG && debug("updateState: newConfig = " + newConfig); + try { + libcutils.property_set("persist.sys.usb.config", newConfig); + } catch(e) { + Cu.reportError("Error configuring adb: " + e); + } + } + if (useDisableAdbTimer) { + if (enableAdb && !usbFuncActive) { + this.startDisableAdbTimer(); + } else { + this.stopDisableAdbTimer(); + } + } + } +}; + +SettingsListener.observe("lockscreen.locked", false, + AdbController.setLockscreenState.bind(AdbController)); +SettingsListener.observe("lockscreen.enabled", false, + AdbController.setLockscreenEnabled.bind(AdbController)); |