diff --git a/CLOBBER b/CLOBBER index 37b2285a1c32..d15932f19fab 100644 --- a/CLOBBER +++ b/CLOBBER @@ -22,9 +22,4 @@ # changes to stick? As of bug 928195, this shouldn't be necessary! Please # don't change CLOBBER for WebIDL changes any more. -Bug 1105308 - Cleanup BluetoothUtils.{cpp,h} - -This patch set moves some files around and requires a rebuild -of the build system's dependency information. - -Merge day clobber +Bug 1066383 - Clobber needed due to build system not reliably picking up an IDL removal. diff --git a/b2g/components/LogShake.jsm b/b2g/components/LogShake.jsm index 7fd4a0fd494c..2da6506ed8b0 100644 --- a/b2g/components/LogShake.jsm +++ b/b2g/components/LogShake.jsm @@ -262,12 +262,16 @@ function getSdcardPrefix() { return volumeService.getVolumeByName('sdcard').mountPoint; } +function getLogDirectoryRoot() { + return 'logs'; +} + function getLogDirectory() { let d = new Date(); d = new Date(d.getTime() - d.getTimezoneOffset() * 60000); let timestamp = d.toISOString().slice(0, -5).replace(/[:T]/g, '-'); // return directory name of format 'logs/timestamp/' - return OS.Path.join('logs', timestamp); + return timestamp; } /** @@ -281,9 +285,10 @@ function saveLogs(logArrays) { }); } - let sdcardPrefix, dirName; + let sdcardPrefix, dirNameRoot, dirName; try { sdcardPrefix = getSdcardPrefix(); + dirNameRoot = getLogDirectoryRoot(); dirName = getLogDirectory(); } catch(e) { // Return promise failed with exception e @@ -291,33 +296,39 @@ function saveLogs(logArrays) { return Promise.reject(e); } - debug('making a directory all the way from '+sdcardPrefix+' to '+(sdcardPrefix + '/' + dirName)); - return OS.File.makeDir(OS.Path.join(sdcardPrefix, dirName), {from: sdcardPrefix}) - .then(function() { - // Now the directory is guaranteed to exist, save the logs - let logFilenames = []; - let saveRequests = []; + debug('making a directory all the way from '+sdcardPrefix+' to '+(sdcardPrefix + '/' + dirNameRoot + '/' + dirName)); + let logsRoot = OS.Path.join(sdcardPrefix, dirNameRoot); + return OS.File.makeDir(logsRoot, {from: sdcardPrefix}).then( + function() { + let logsDir = OS.Path.join(logsRoot, dirName); + return OS.File.makeDir(logsDir, {ignoreExisting: false}).then( + function() { + // Now the directory is guaranteed to exist, save the logs + let logFilenames = []; + let saveRequests = []; - for (let logLocation in logArrays) { - debug('requesting save of ' + logLocation); - let logArray = logArrays[logLocation]; - // The filename represents the relative path within the SD card, not the - // absolute path because Gaia will refer to it using the DeviceStorage - // API - let filename = OS.Path.join(dirName, getLogFilename(logLocation)); - logFilenames.push(filename); - let saveRequest = OS.File.writeAtomic(OS.Path.join(sdcardPrefix, filename), logArray); - saveRequests.push(saveRequest); - } + for (let logLocation in logArrays) { + debug('requesting save of ' + logLocation); + let logArray = logArrays[logLocation]; + // The filename represents the relative path within the SD card, not the + // absolute path because Gaia will refer to it using the DeviceStorage + // API + let filename = OS.Path.join(dirNameRoot, dirName, getLogFilename(logLocation)); + logFilenames.push(filename); + let saveRequest = OS.File.writeAtomic(OS.Path.join(sdcardPrefix, filename), logArray); + saveRequests.push(saveRequest); + } - return Promise.all(saveRequests).then(function() { - debug('returning logfilenames: '+logFilenames.toSource()); - return { - logFilenames: logFilenames, - logPrefix: dirName - }; + return Promise.all(saveRequests).then( + function() { + debug('returning logfilenames: '+logFilenames.toSource()); + return { + logFilenames: logFilenames, + logPrefix: OS.Path.join(dirNameRoot, dirName) + }; + }); + }); }); - }); } LogShake.init(); diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index 4d97b2b66713..e30db4ea6c80 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -12,10 +12,10 @@ - + - + @@ -113,7 +113,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index b8e313a63f1b..3e01ee2c593d 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 32afd2fa4ce0..05f78ea89633 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 6cf8daedb49d..84d94e8a5902 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -12,10 +12,10 @@ - + - + @@ -113,7 +113,7 @@ - + @@ -130,7 +130,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index b8e313a63f1b..3e01ee2c593d 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index 07b0daa266cc..16389265c201 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -12,10 +12,10 @@ - + - + @@ -113,7 +113,7 @@ - + @@ -137,9 +137,11 @@ - + + + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index c187faaddb84..33f051fbb55d 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 268db5789540..579ec00f4971 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "24f27472ae6ddb6c819227cb3b7d398a6925ef86", + "revision": "b60aedd37a5ccdb71893d31761988bcc17a82676", "repo_path": "integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index c3ccf6c249a8..f5626e39599b 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 4befdd30e25e..3509fad831b5 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index 413f4e5dc31d..0d6d16d4654b 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 29a9290d3b16..6fd6417cb997 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 59ace3c21438..024bbc6d7b06 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -401,6 +401,8 @@ @BINPATH@/components/nsSidebar.js @BINPATH@/components/nsAsyncShutdown.manifest @BINPATH@/components/nsAsyncShutdown.js +@BINPATH@/components/htmlMenuBuilder.js +@BINPATH@/components/htmlMenuBuilder.manifest ; WiFi, NetworkManager, NetworkStats #ifdef MOZ_WIDGET_GONK diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index d6e4b1de03a3..c886033e57ed 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -259,10 +259,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter", "nsICrashReporter"); #endif -XPCOMUtils.defineLazyGetter(this, "PageMenu", function() { +XPCOMUtils.defineLazyGetter(this, "PageMenuParent", function() { let tmp = {}; Cu.import("resource://gre/modules/PageMenu.jsm", tmp); - return new tmp.PageMenu(); + return new tmp.PageMenuParent(); }); /** diff --git a/browser/base/content/content.js b/browser/base/content/content.js index b2356a6fe4e7..9fc0fd64847e 100644 --- a/browser/base/content/content.js +++ b/browser/base/content/content.js @@ -43,6 +43,11 @@ XPCOMUtils.defineLazyGetter(this, "SimpleServiceDiscovery", function() { }); return ssdp; }); +XPCOMUtils.defineLazyGetter(this, "PageMenuChild", function() { + let tmp = {}; + Cu.import("resource://gre/modules/PageMenu.jsm", tmp); + return new tmp.PageMenuChild(); +}); // TabChildGlobal var global = this; @@ -102,6 +107,10 @@ addMessageListener("SecondScreen:tab-mirror", function(message) { } }); +addMessageListener("ContextMenu:DoCustomCommand", function(message) { + PageMenuChild.executeMenu(message.data); +}); + addEventListener("DOMFormHasPassword", function(event) { InsecurePasswordUtils.checkForInsecurePasswords(event.target); LoginManagerContent.onFormPassword(event); @@ -148,7 +157,8 @@ let handleContentContextMenu = function (event) { InlineSpellCheckerContent.initContextMenu(event, editFlags, this); } - sendSyncMessage("contextmenu", { editFlags, spellInfo, addonInfo }, { event, popupNode: event.target }); + let customMenuItems = PageMenuChild.build(event.target); + sendSyncMessage("contextmenu", { editFlags, spellInfo, customMenuItems, addonInfo }, { event, popupNode: event.target }); } else { // Break out to the parent window and pass the add-on info along diff --git a/browser/base/content/nsContextMenu.js b/browser/base/content/nsContextMenu.js index 4dc54af0cdcc..196882fd942f 100644 --- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -24,10 +24,15 @@ nsContextMenu.prototype = { return; this.hasPageMenu = false; - // FIXME (bug 1047751) - The page menu is disabled in e10s. - if (!aIsShift && !this.isRemote) { - this.hasPageMenu = PageMenu.maybeBuildAndAttachMenu(this.target, - aXulMenu); + if (!aIsShift) { + if (this.isRemote) { + this.hasPageMenu = + PageMenuParent.addToPopup(gContextMenuContentData.customMenuItems, + this.browser, aXulMenu); + } + else { + this.hasPageMenu = PageMenuParent.buildAndAddToPopup(this.target, aXulMenu); + } } this.isFrameImage = document.getElementById("isFrameImage"); @@ -1766,7 +1771,7 @@ nsContextMenu.prototype = { } // Check if this is a page menu item: - if (e.target.hasAttribute(PageMenu.GENERATEDITEMID_ATTR)) { + if (e.target.hasAttribute(PageMenuParent.GENERATEDITEMID_ATTR)) { this._telemetryClickID = "custom-page-item"; } else { this._telemetryClickID = (e.target.id || "unknown").replace(/^context-/i, ""); diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index b081551681c6..87d36f9722f0 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3175,6 +3175,7 @@ browser: browser, editFlags: aMessage.data.editFlags, spellInfo: spellInfo, + customMenuItems: aMessage.data.customMenuItems, addonInfo: aMessage.data.addonInfo }; let popup = browser.ownerDocument.getElementById("contentAreaContextMenu"); let event = gContextMenuContentData.event; diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 7322e5b95d66..a1232ad10570 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -72,6 +72,7 @@ support-files = redirect_bug623155.sjs searchSuggestionEngine.sjs searchSuggestionEngine.xml + subtst_contextmenu.html test-mixedcontent-securityerrors.html test_bug435035.html test_bug462673.html @@ -486,4 +487,5 @@ skip-if = e10s # bug 1100687 - test directly manipulates content (content.docume [browser_mcb_redirect.js] skip-if = e10s # bug 1084504 - [e10s] Mixed content detection does not take redirection into account [browser_windowactivation.js] +[browser_contextmenu_childprocess.js] [browser_bug963945.js] diff --git a/browser/base/content/test/general/browser_contextmenu_childprocess.js b/browser/base/content/test/general/browser_contextmenu_childprocess.js new file mode 100644 index 000000000000..c967137919ff --- /dev/null +++ b/browser/base/content/test/general/browser_contextmenu_childprocess.js @@ -0,0 +1,87 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const gBaseURL = "https://example.com/browser/browser/base/content/test/general/"; + +add_task(function *() { + let tab = gBrowser.addTab(); + let browser = gBrowser.getBrowserForTab(tab); + + gBrowser.selectedTab = tab; + yield promiseTabLoadEvent(tab, gBaseURL + "subtst_contextmenu.html"); + + let popupShownPromise = promiseWaitForEvent(window, "popupshown", true); + + // Get the point of the element with the page menu (test-pagemenu) and + // synthesize a right mouse click there. + let eventDetails = { type : "contextmenu", button : 2 }; + let rect = browser.contentWindow.document.getElementById("test-pagemenu").getBoundingClientRect(); + EventUtils.synthesizeMouse(browser, rect.x + rect.width / 2, rect.y + rect.height / 2, eventDetails, window); + + let event = yield popupShownPromise; + + let contextMenu = document.getElementById("contentAreaContextMenu"); + checkMenu(contextMenu); + contextMenu.hidePopup(); + gBrowser.removeCurrentTab(); +}); + +function checkItems(menuitem, arr) +{ + for (let i = 0; i < arr.length; i += 2) { + let str = arr[i]; + let details = arr[i + 1]; + if (str == "---") { + is(menuitem.localName, "menuseparator", "menuseparator"); + } + else if ("children" in details) { + is(menuitem.localName, "menu", "submenu"); + is(menuitem.getAttribute("label"), str, str + " label"); + checkItems(menuitem.firstChild.firstChild, details.children); + } + else { + is(menuitem.localName, "menuitem", str + " menuitem"); + + is(menuitem.getAttribute("label"), str, str + " label"); + is(menuitem.getAttribute("type"), details.type, str + " type"); + is(menuitem.getAttribute("image"), details.icon ? gBaseURL + details.icon : "", str + " icon"); + + if (details.checked) + is(menuitem.getAttribute("checked"), "true", str + " checked"); + else + ok(!menuitem.hasAttribute("checked"), str + " checked"); + + if (details.disabled) + is(menuitem.getAttribute("disabled"), "true", str + " disabled"); + else + ok(!menuitem.hasAttribute("disabled"), str + " disabled"); + } + + menuitem = menuitem.nextSibling; + } +} + +function checkMenu(contextMenu) +{ + let items = [ "Plain item", {type: "", icon: "", checked: false, disabled: false}, + "Disabled item", {type: "", icon: "", checked: false, disabled: true}, + "Item w/ textContent", {type: "", icon: "", checked: false, disabled: false}, + "---", null, + "Checkbox", {type: "checkbox", icon: "", checked: true, disabled: false}, + "---", null, + "Radio1", {type: "checkbox", icon: "", checked: true, disabled: false}, + "Radio2", {type: "checkbox", icon: "", checked: false, disabled: false}, + "Radio3", {type: "checkbox", icon: "", checked: false, disabled: false}, + "---", null, + "Item w/ icon", {type: "", icon: "favicon.ico", checked: false, disabled: false}, + "Item w/ bad icon", {type: "", icon: "", checked: false, disabled: false}, + "---", null, + "Submenu", { children: + ["Radio1", {type: "checkbox", icon: "", checked: false, disabled: false}, + "Radio2", {type: "checkbox", icon: "", checked: true, disabled: false}, + "Radio3", {type: "checkbox", icon: "", checked: false, disabled: false}, + "---", null, + "Checkbox", {type: "checkbox", icon: "", checked: false, disabled: false}] } + ]; + checkItems(contextMenu.childNodes[2], items); +} diff --git a/browser/base/content/test/general/test_contextmenu.html b/browser/base/content/test/general/test_contextmenu.html index edb7c705d502..c87ae2b5c40e 100644 --- a/browser/base/content/test/general/test_contextmenu.html +++ b/browser/base/content/test/general/test_contextmenu.html @@ -495,7 +495,7 @@ function runTest(testNum) { "context-viewinfo", true ].concat(inspectItems)); - invokeItemAction("0"); + invokeItemAction("1"); closeContextMenu(); // run mozRequestFullScreen on the element we're testing diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 0feaf0fe704f..a2f944bb616d 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -542,6 +542,8 @@ @RESPATH@/components/Identity.manifest @RESPATH@/components/recording-cmdline.js @RESPATH@/components/recording-cmdline.manifest +@RESPATH@/components/htmlMenuBuilder.js +@RESPATH@/components/htmlMenuBuilder.manifest @RESPATH@/components/PermissionSettings.js @RESPATH@/components/PermissionSettings.manifest diff --git a/browser/locales/Makefile.in b/browser/locales/Makefile.in index 679c3ef0b67e..c7cb1a39413b 100644 --- a/browser/locales/Makefile.in +++ b/browser/locales/Makefile.in @@ -77,7 +77,7 @@ SEARCHPLUGINS_PATH := $(FINAL_TARGET)/searchplugins # metro build call a searchplugins target for search engine plugins .PHONY: searchplugins SEARCHPLUGINS_TARGET := libs searchplugins -SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_NAMES)),$(or $(wildcard $(call MERGE_FILE,searchplugins/$(plugin))),$(info Missing searchplugin: $(plugin)))) +SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_NAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(info Missing searchplugin: $(plugin)))) # Some locale-specific search plugins may have preprocessor directives, but the # default en-US ones do not. SEARCHPLUGINS_FLAGS := --silence-missing-directive-warnings diff --git a/config/config.mk b/config/config.mk index 948a71af2092..141dda16fa5f 100644 --- a/config/config.mk +++ b/config/config.mk @@ -614,6 +614,13 @@ MERGE_FILE = $(LOCALE_SRCDIR)/$(1) endif MERGE_FILES = $(foreach f,$(1),$(call MERGE_FILE,$(f))) +# These marcros are similar to MERGE_FILE, but no merging, and en-US first. +# They're used for searchplugins, for example. +EN_US_OR_L10N_FILE = $(firstword \ + $(wildcard $(srcdir)/en-US/$(1)) \ + $(LOCALE_SRCDIR)/$(1) ) +EN_US_OR_L10N_FILES = $(foreach f,$(1),$(call EN_US_OR_L10N_FILE,$(f))) + ifneq (WINNT,$(OS_ARCH)) RUN_TEST_PROGRAM = $(LIBXUL_DIST)/bin/run-mozilla.sh endif # ! WINNT diff --git a/config/makefiles/xpidl/Makefile.in b/config/makefiles/xpidl/Makefile.in index 178b7f2168f9..2c2b1b76627b 100644 --- a/config/makefiles/xpidl/Makefile.in +++ b/config/makefiles/xpidl/Makefile.in @@ -22,48 +22,39 @@ include $(topsrcdir)/config/rules.mk # As an optimization to reduce overall CPU usage, we process all .idl # belonging to a module with a single command invocation. This prevents # redundant parsing of .idl files and significantly reduces CPU cycles. -# -# Future improvement: Headers are currently written to a local directory then -# installed in the distribution directory. It is preferable to write headers -# directly into the distribution directory. However, PGO builds remove the dist -# directory via rm -rf (with no regards to manifests). Since the cost of -# processing XPIDL files is not trivial, it is preferrable to cache the headers -# and reinstall them rather than regenerate them. Ideally the dist pruning is -# performed with manifests. At that time we can write headers directly to the -# dist directory. # For dependency files. idl_deps_dir := .deps -# Where we put our final, linked .xpt files. -idl_xpt_dir := xpt - dist_idl_dir := $(DIST)/idl dist_include_dir := $(DIST)/include process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py -# TODO we should use py_action, but that would require extra directories to be -# in the virtualenv. -idlprocess := $(PYTHON_PATH) $(PLY_INCLUDE) -I$(IDL_PARSER_DIR) -I$(IDL_PARSER_CACHE_DIR) \ - $(process_py) --cache-dir $(IDL_PARSER_CACHE_DIR) $(dist_idl_dir) \ - $(dist_include_dir) $(idl_xpt_dir) $(idl_deps_dir) - ifdef LIBXUL_SDK -idlprocess += -I$(LIBXUL_SDK)/idl +libxul_sdk_includes := -I$(LIBXUL_SDK)/idl endif +# TODO we should use py_action, but that would require extra directories to be +# in the virtualenv. +%.xpt: + @echo "$(@F)" + $(PYTHON_PATH) $(PLY_INCLUDE) -I$(IDL_PARSER_DIR) -I$(IDL_PARSER_CACHE_DIR) \ + $(process_py) --cache-dir $(IDL_PARSER_CACHE_DIR) $(dist_idl_dir) \ + $(dist_include_dir) $(@D) $(idl_deps_dir) $(libxul_sdk_includes) \ + $(basename $(notdir $@ $(filter %.idl,$^))) + xpidl_modules := @xpidl_modules@ +xpt_files := @xpt_files@ @xpidl_rules@ -linked_xpt_files := $(addprefix $(idl_xpt_dir)/,$(addsuffix .xpt,$(xpidl_modules))) depends_files := $(foreach root,$(xpidl_modules),$(idl_deps_dir)/$(root).pp) -GARBAGE += $(linked_xpt_files) $(depends_files) +GARBAGE += $(xpt_files) $(depends_files) -xpidl:: $(linked_xpt_files) +xpidl:: $(xpt_files) -$(linked_xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir) $(idl_xpt_dir)) +$(xpt_files): $(process_py) $(call mkdir_deps,$(idl_deps_dir) $(dist_include_dir)) $(call include_deps,$(depends_files)) diff --git a/config/rules.mk b/config/rules.mk index cec5deec5eeb..f5f21f5a13a4 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -1157,11 +1157,6 @@ endif ifdef XPT_NAME #{ ifndef NO_DIST_INSTALL -_XPT_NAME_FILES := $(DEPTH)/config/makefiles/xpidl/xpt/$(XPT_NAME) -_XPT_NAME_DEST := $(FINAL_TARGET)/components -_XPT_NAME_TARGET := misc -INSTALL_TARGETS += _XPT_NAME - ifndef NO_INTERFACES_MANIFEST misc:: $(call mkdir_deps,$(FINAL_TARGET)/components) $(call py_action,buildlist,$(FINAL_TARGET)/components/interfaces.manifest 'interfaces $(XPT_NAME)') diff --git a/configure.in b/configure.in index 6d612e1d50e4..26f0ef5ea9e4 100644 --- a/configure.in +++ b/configure.in @@ -286,6 +286,7 @@ if test -n "$gonkdir" ; then 21) GONK_INCLUDES="-I$gonkdir/frameworks/native/include -I$gonkdir/frameworks/av/include -I$gonkdir/frameworks/av/include/media -I$gonkdir/frameworks/av/include/camera -I$gonkdir/frameworks/native/include/media/openmax -I$gonkdir/frameworks/av/media/libstagefright/include" MOZ_AUDIO_OFFLOAD=1 + MOZ_OMX_DECODER=1 AC_SUBST(MOZ_AUDIO_OFFLOAD) AC_DEFINE(MOZ_AUDIO_OFFLOAD) MOZ_FMP4= diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 5fe40e37cd7a..f1f66db2f044 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -819,7 +819,12 @@ NotifyOffThreadScriptLoadCompletedRunnable::Run() { MOZ_ASSERT(NS_IsMainThread()); - nsresult rv = mLoader->ProcessOffThreadRequest(mRequest, &mToken); + // We want these to be dropped on the main thread, once we return from this + // function. + nsRefPtr request = mRequest.forget(); + nsRefPtr loader = mLoader.forget(); + + nsresult rv = loader->ProcessOffThreadRequest(request, &mToken); if (mToken) { // The result of the off thread parse was not actually needed to process diff --git a/dom/bluetooth/BluetoothRilListener.cpp b/dom/bluetooth/BluetoothRilListener.cpp index 650d3c533e65..998337954873 100644 --- a/dom/bluetooth/BluetoothRilListener.cpp +++ b/dom/bluetooth/BluetoothRilListener.cpp @@ -101,13 +101,6 @@ MobileConnectionListener::NotifyDataChanged() return NS_OK; } -NS_IMETHODIMP -MobileConnectionListener::NotifyUssdReceived(const nsAString & message, - bool sessionEnded) -{ - return NS_OK; -} - NS_IMETHODIMP MobileConnectionListener::NotifyDataError(const nsAString & message) { diff --git a/dom/bluetooth2/BluetoothRilListener.cpp b/dom/bluetooth2/BluetoothRilListener.cpp index 650d3c533e65..998337954873 100644 --- a/dom/bluetooth2/BluetoothRilListener.cpp +++ b/dom/bluetooth2/BluetoothRilListener.cpp @@ -101,13 +101,6 @@ MobileConnectionListener::NotifyDataChanged() return NS_OK; } -NS_IMETHODIMP -MobileConnectionListener::NotifyUssdReceived(const nsAString & message, - bool sessionEnded) -{ - return NS_OK; -} - NS_IMETHODIMP MobileConnectionListener::NotifyDataError(const nsAString & message) { diff --git a/dom/html/HTMLMenuElement.cpp b/dom/html/HTMLMenuElement.cpp index 8553534119f5..a15e7d0ea79f 100644 --- a/dom/html/HTMLMenuElement.cpp +++ b/dom/html/HTMLMenuElement.cpp @@ -9,11 +9,13 @@ #include "mozilla/EventDispatcher.h" #include "mozilla/dom/HTMLMenuElementBinding.h" #include "mozilla/dom/HTMLMenuItemElement.h" +#include "nsIMenuBuilder.h" #include "nsAttrValueInlines.h" #include "nsContentUtils.h" -#include "nsXULContextMenuBuilder.h" #include "nsIURI.h" +#define HTMLMENUBUILDER_CONTRACTID "@mozilla.org/content/html-menu-builder;1" + NS_IMPL_NS_NEW_HTML_ELEMENT(Menu) namespace mozilla { @@ -97,12 +99,8 @@ HTMLMenuElement::CreateBuilder(nsIMenuBuilder** _retval) { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR); - *_retval = nullptr; - - if (mType == MENU_TYPE_CONTEXT) { - NS_ADDREF(*_retval = new nsXULContextMenuBuilder()); - } - + nsCOMPtr builder = CreateBuilder(); + builder.swap(*_retval); return NS_OK; } @@ -113,8 +111,9 @@ HTMLMenuElement::CreateBuilder() return nullptr; } - nsCOMPtr ret = new nsXULContextMenuBuilder(); - return ret.forget(); + nsCOMPtr builder = do_CreateInstance(HTMLMENUBUILDER_CONTRACTID); + NS_WARN_IF(!builder); + return builder.forget(); } NS_IMETHODIMP diff --git a/dom/html/htmlMenuBuilder.js b/dom/html/htmlMenuBuilder.js new file mode 100644 index 000000000000..863fc4d74b13 --- /dev/null +++ b/dom/html/htmlMenuBuilder.js @@ -0,0 +1,132 @@ +/* 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/. */ + +// This component is used to build the menus for the HTML contextmenu attribute. + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const Cc = Components.classes; +const Ci = Components.interfaces; + +// A global value that is used to identify each menu item. It is +// incremented with each one that is found. +var gGeneratedId = 1; + +function HTMLMenuBuilder() { + this.currentNode = null; + this.root = null; + this.items = {}; + this.nestedStack = []; +}; + +// Building is done in two steps: +// The first generates a hierarchical JS object that contains the menu structure. +// This object is returned by toJSONString. +// +// The second step can take this structure and generate a XUL menu hierarchy or +// other UI from this object. The default UI is done in PageMenu.jsm. +// +// When a multi-process browser is used, the first step is performed by the child +// process and the second step is performed by the parent process. + +HTMLMenuBuilder.prototype = +{ + classID: Components.ID("{51c65f5d-0de5-4edc-9058-60e50cef77f8}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMenuBuilder]), + + currentNode: null, + root: null, + items: {}, + nestedStack: [], + + toJSONString: function() { + return JSON.stringify(this.root); + }, + + openContainer: function(aLabel) { + if (!this.currentNode) { + this.root = { + type: "menu", + children: [] + }; + this.currentNode = this.root; + } + else { + let parent = this.currentNode; + this.currentNode = { + type: "menu", + label: aLabel, + children: [] + }; + parent.children.push(this.currentNode); + this.nestedStack.push(parent); + } + }, + + addItemFor: function(aElement, aCanLoadIcon) { + if (!("children" in this.currentNode)) { + return; + } + + let item = { + type: "menuitem", + label: aElement.label + }; + + let elementType = aElement.type; + if (elementType == "checkbox" || elementType == "radio") { + item.checkbox = true; + + if (aElement.checked) { + item.checked = true; + } + } + + let icon = aElement.icon; + if (icon.length > 0 && aCanLoadIcon) { + item.icon = icon; + } + + if (aElement.disabled) { + item.disabled = true; + } + + item.id = gGeneratedId++; + this.currentNode.children.push(item); + + this.items[item.id] = aElement; + }, + + addSeparator: function() { + if (!("children" in this.currentNode)) { + return; + } + + this.currentNode.children.push({ type: "separator"}); + }, + + undoAddSeparator: function() { + if (!("children" in this.currentNode)) { + return; + } + + let children = this.currentNode.children; + if (children.length && children[children.length - 1].type == "separator") { + children.pop(); + } + }, + + closeContainer: function() { + this.currentNode = this.nestedStack.length ? this.nestedStack.pop() : this.root; + }, + + click: function(id) { + let item = this.items[id]; + if (item) { + item.click(); + } + } +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HTMLMenuBuilder]); diff --git a/dom/html/htmlMenuBuilder.manifest b/dom/html/htmlMenuBuilder.manifest new file mode 100644 index 000000000000..b245f8fe2de5 --- /dev/null +++ b/dom/html/htmlMenuBuilder.manifest @@ -0,0 +1,3 @@ +component {51c65f5d-0de5-4edc-9058-60e50cef77f8} htmlMenuBuilder.js +contract @mozilla.org/content/html-menu-builder;1 {51c65f5d-0de5-4edc-9058-60e50cef77f8} + diff --git a/dom/html/moz.build b/dom/html/moz.build index cd725e70d2a4..97f6d1799026 100644 --- a/dom/html/moz.build +++ b/dom/html/moz.build @@ -215,6 +215,11 @@ SOURCES += [ 'PluginDocument.cpp', ] +EXTRA_COMPONENTS += [ + 'htmlMenuBuilder.js', + 'htmlMenuBuilder.manifest' +] + FAIL_ON_WARNINGS = True MSVC_ENABLE_PGO = True diff --git a/dom/html/nsIMenuBuilder.idl b/dom/html/nsIMenuBuilder.idl index a925fbcb709e..02664480fa46 100644 --- a/dom/html/nsIMenuBuilder.idl +++ b/dom/html/nsIMenuBuilder.idl @@ -11,7 +11,7 @@ interface nsIDOMHTMLMenuItemElement; * An interface used to construct native toolbar or context menus from */ -[scriptable, uuid(12724737-f7db-43b4-94ab-708a7b86e115)] +[scriptable, uuid(93F4A48F-D043-4F45-97FD-9771EA1AF976)] interface nsIMenuBuilder : nsISupports { @@ -49,4 +49,28 @@ interface nsIMenuBuilder : nsISupports */ void closeContainer(); + /** + * Returns a JSON string representing the menu hierarchy. For a context menu, + * it will be of the form: + * { + * type: "menu", + * children: [ + * { + * type: "menuitem", + * label: "label", + * icon: "image.png" + * }, + * { + * type: "separator", + * }, + * ]; + */ + AString toJSONString(); + + /** + * Invoke the action of the menuitem with assigned id aGeneratedItemId. + * + * @param aGeneratedItemId the menuitem id + */ + void click(in DOMString aGeneratedItemId); }; diff --git a/dom/icc/Assertions.cpp b/dom/icc/Assertions.cpp index 4d4c76cf34cd..03f7148d88b1 100644 --- a/dom/icc/Assertions.cpp +++ b/dom/icc/Assertions.cpp @@ -94,6 +94,18 @@ ASSERT_ICC_CONTACT_TYPE_EQUALITY(Sdn, CARD_CONTACT_TYPE_SDN); #undef ASSERT_ICC_CONTACT_TYPE_EQUALITY +/** + * Enum IccMvnoType + */ +#define ASSERT_ICC_MVNO_TYPE_EQUALITY(webidlState, xpidlState) \ + ASSERT_EQUALITY(IccMvnoType, webidlState, xpidlState) + +ASSERT_ICC_MVNO_TYPE_EQUALITY(Imsi, CARD_MVNO_TYPE_IMSI); +ASSERT_ICC_MVNO_TYPE_EQUALITY(Spn, CARD_MVNO_TYPE_SPN); +ASSERT_ICC_MVNO_TYPE_EQUALITY(Gid, CARD_MVNO_TYPE_GID); + +#undef ASSERT_ICC_MVNO_TYPE_EQUALITY + #undef ASSERT_EQUALITY } // namespace icc diff --git a/dom/icc/Icc.cpp b/dom/icc/Icc.cpp index c693837ae970..4199df4f4656 100644 --- a/dom/icc/Icc.cpp +++ b/dom/icc/Icc.cpp @@ -384,8 +384,7 @@ Icc::UpdateContact(const JSContext* aCx, IccContactType aContactType, } already_AddRefed -Icc::MatchMvno(const nsAString& aMvnoType, - const nsAString& aMvnoData, +Icc::MatchMvno(IccMvnoType aMvnoType, const nsAString& aMvnoData, ErrorResult& aRv) { if (!mProvider) { @@ -395,8 +394,8 @@ Icc::MatchMvno(const nsAString& aMvnoType, nsRefPtr request; nsresult rv = mProvider->MatchMvno(mClientId, GetOwner(), - aMvnoType, aMvnoData, - getter_AddRefs(request)); + static_cast(aMvnoType), + aMvnoData, getter_AddRefs(request)); if (NS_FAILED(rv)) { aRv.Throw(rv); return nullptr; diff --git a/dom/icc/Icc.h b/dom/icc/Icc.h index 4bf502480959..cdc34ebe3975 100644 --- a/dom/icc/Icc.h +++ b/dom/icc/Icc.h @@ -98,7 +98,7 @@ public: ErrorResult& aRv); already_AddRefed - MatchMvno(const nsAString& aMvnoType, const nsAString& aMatchData, + MatchMvno(IccMvnoType aMvnoType, const nsAString& aMatchData, ErrorResult& aRv); IMPL_EVENT_HANDLER(iccinfochange) diff --git a/dom/icc/interfaces/nsIIccProvider.idl b/dom/icc/interfaces/nsIIccProvider.idl index 7d7769129e0a..011d84be222a 100644 --- a/dom/icc/interfaces/nsIIccProvider.idl +++ b/dom/icc/interfaces/nsIIccProvider.idl @@ -20,7 +20,7 @@ interface nsIIccListener : nsISupports /** * XPCOM component (in the content process) that provides the ICC information. */ -[scriptable, uuid(937213c3-f64e-4f58-b4e0-3010f219d0c3)] +[scriptable, uuid(c3e3c1a9-6ac4-4916-a777-7d49ffd89c46)] interface nsIIccProvider : nsISupports { // MUST match enum IccCardState in MozIcc.webidl! @@ -85,6 +85,11 @@ interface nsIIccProvider : nsISupports const unsigned long CARD_CONTACT_TYPE_FDN = 1; const unsigned long CARD_CONTACT_TYPE_SDN = 2; + // MUST match with enum IccMvnoType in MozIcc.webidl + const unsigned long CARD_MVNO_TYPE_IMSI = 0; + const unsigned long CARD_MVNO_TYPE_SPN = 1; + const unsigned long CARD_MVNO_TYPE_GID = 2; + /** * Called when a content process registers receiving unsolicited messages from * RadioInterfaceLayer in the chrome process. Only a content process that has @@ -182,6 +187,6 @@ interface nsIIccProvider : nsISupports */ nsIDOMDOMRequest matchMvno(in unsigned long clientId, in nsIDOMWindow window, - in DOMString mvnoType, + in unsigned long mvnoType, in DOMString mvnoData); }; diff --git a/dom/media/gstreamer/GStreamerReader.cpp b/dom/media/gstreamer/GStreamerReader.cpp index d80901ec3e08..4fdd44b4c4e3 100644 --- a/dom/media/gstreamer/GStreamerReader.cpp +++ b/dom/media/gstreamer/GStreamerReader.cpp @@ -70,6 +70,7 @@ GStreamerReader::GStreamerReader(AbstractMediaDecoder* aDecoder) mMP3FrameParser(aDecoder->GetResource()->GetLength()), mDataOffset(0), mUseParserDuration(false), + mLastParserDuration(-1), #if GST_VERSION_MAJOR >= 1 mAllocator(nullptr), mBufferPool(nullptr), diff --git a/dom/media/omx/MediaCodecReader.cpp b/dom/media/omx/MediaCodecReader.cpp index 43cdc4911512..09244072d57c 100644 --- a/dom/media/omx/MediaCodecReader.cpp +++ b/dom/media/omx/MediaCodecReader.cpp @@ -1250,7 +1250,9 @@ MediaCodecReader::DestroyMediaSources() { mAudioTrack.mSource = nullptr; mVideoTrack.mSource = nullptr; +#if ANDROID_VERSION >= 21 mAudioOffloadTrack.mSource = nullptr; +#endif } void @@ -1327,7 +1329,15 @@ MediaCodecReader::CreateMediaCodec(sp& aLooper, if (aTrack.mType == Track::kVideo && aTrack.mCodec->getCapability(&capability) == OK && (capability & MediaCodecProxy::kCanExposeGraphicBuffer) == MediaCodecProxy::kCanExposeGraphicBuffer) { +#if ANDROID_VERSION >= 21 + android::sp producer; + android::sp consumer; + GonkBufferQueue::createBufferQueue(&producer, &consumer); + aTrack.mNativeWindow = new GonkNativeWindow(consumer); + aTrack.mGraphicBufferProducer = producer; +#else aTrack.mNativeWindow = new GonkNativeWindow(); +#endif } if (!aAsync) { @@ -1354,7 +1364,11 @@ MediaCodecReader::ConfigureMediaCodec(Track& aTrack) sp surface; if (aTrack.mNativeWindow != nullptr) { +#if ANDROID_VERSION >= 21 + surface = new Surface(aTrack.mGraphicBufferProducer); +#else surface = new Surface(aTrack.mNativeWindow->getBufferQueue()); +#endif } sp sourceFormat = aTrack.mSource->getFormat(); @@ -1399,6 +1413,9 @@ MediaCodecReader::DestroyMediaCodec(Track& aTrack) { aTrack.mCodec = nullptr; aTrack.mNativeWindow = nullptr; +#if ANDROID_VERSION >= 21 + aTrack.mGraphicBufferProducer = nullptr; +#endif } bool diff --git a/dom/media/omx/MediaCodecReader.h b/dom/media/omx/MediaCodecReader.h index 858a97ccc86b..4e745a10e673 100644 --- a/dom/media/omx/MediaCodecReader.h +++ b/dom/media/omx/MediaCodecReader.h @@ -139,6 +139,9 @@ protected: android::Vector > mInputBuffers; android::Vector > mOutputBuffers; android::sp mNativeWindow; +#if ANDROID_VERSION >= 21 + android::sp mGraphicBufferProducer; +#endif // pipeline copier nsAutoPtr mInputCopier; diff --git a/dom/media/omx/MediaOmxReader.cpp b/dom/media/omx/MediaOmxReader.cpp index 7ab49a5fe209..8717612be740 100644 --- a/dom/media/omx/MediaOmxReader.cpp +++ b/dom/media/omx/MediaOmxReader.cpp @@ -112,7 +112,7 @@ private: mOffset += length; } - if (mOffset < mFullLength) { + if (static_cast(mOffset) < mFullLength) { // We cannot read data in the main thread because it // might block for too long. Instead we post an IO task // to the IO thread if there is more data available. diff --git a/dom/media/omx/OmxDecoder.cpp b/dom/media/omx/OmxDecoder.cpp index f0fb361c228a..b2c3c0951769 100644 --- a/dom/media/omx/OmxDecoder.cpp +++ b/dom/media/omx/OmxDecoder.cpp @@ -254,8 +254,18 @@ bool OmxDecoder::AllocateMediaResources() NS_ASSERTION(err == OK, "Failed to connect to OMX in mediaserver."); sp omx = client.interface(); +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21 + sp producer; + sp consumer; + GonkBufferQueue::createBufferQueue(&producer, &consumer); + mNativeWindow = new GonkNativeWindow(consumer); +#else mNativeWindow = new GonkNativeWindow(); -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 +#endif + +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21 + mNativeWindowClient = new GonkNativeWindowClient(producer); +#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow->getBufferQueue()); #else mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow); diff --git a/dom/media/webaudio/AudioBufferSourceNode.cpp b/dom/media/webaudio/AudioBufferSourceNode.cpp index cd8df5566c47..ceed1a3988c7 100644 --- a/dom/media/webaudio/AudioBufferSourceNode.cpp +++ b/dom/media/webaudio/AudioBufferSourceNode.cpp @@ -7,6 +7,7 @@ #include "AudioBufferSourceNode.h" #include "mozilla/dom/AudioBufferSourceNodeBinding.h" #include "mozilla/dom/AudioParam.h" +#include "mozilla/FloatingPoint.h" #include "nsMathUtils.h" #include "AudioNodeEngine.h" #include "AudioNodeStream.h" @@ -110,7 +111,7 @@ public: mBeginProcessing = mStart + 0.5; break; case AudioBufferSourceNode::DOPPLERSHIFT: - mDopplerShift = aParam > 0 && aParam == aParam ? aParam : 1.0; + mDopplerShift = (aParam <= 0 || mozilla::IsNaN(aParam)) ? 1.0 : aParam; break; default: NS_ERROR("Bad AudioBufferSourceNodeEngine double parameter."); @@ -415,7 +416,7 @@ public: } else { playbackRate = mPlaybackRateTimeline.GetValueAtTime(mSource->GetCurrentPosition()); } - if (playbackRate <= 0 || playbackRate != playbackRate) { + if (playbackRate <= 0 || mozilla::IsNaN(playbackRate)) { playbackRate = 1.0f; } diff --git a/dom/media/webaudio/WebAudioUtils.h b/dom/media/webaudio/WebAudioUtils.h index ee22ea07e21d..baaa373535c8 100644 --- a/dom/media/webaudio/WebAudioUtils.h +++ b/dom/media/webaudio/WebAudioUtils.h @@ -181,7 +181,7 @@ namespace WebAudioUtils { static_assert(mozilla::IsFloatingPoint::value == true, "FloatType must be a floating point type"); - if (f != f) { + if (mozilla::IsNaN(f)) { // It is the responsibility of the caller to deal with NaN values. // If we ever get to this point, we have a serious bug to fix. NS_RUNTIMEABORT("We should never see a NaN here"); diff --git a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp index a885ceb96675..4dcc4a39ee5c 100644 --- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp +++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp @@ -691,40 +691,72 @@ MediaEngineGonkVideoSource::RotateImage(layers::Image* aImage, uint32_t aWidth, gfx::BackendType::NONE, layers::TextureFlags::DEFAULT, layers::ALLOC_DISALLOW_BUFFERTEXTURECLIENT); - if (!textureClient) { - return; + if (textureClient) { + RefPtr grallocTextureClient = + static_cast(textureClient.get()); + + android::sp destBuffer = grallocTextureClient->GetGraphicBuffer(); + + void* destMem = nullptr; + destBuffer->lock(android::GraphicBuffer::USAGE_SW_WRITE_OFTEN, &destMem); + uint8_t* dstPtr = static_cast(destMem); + + int32_t yStride = destBuffer->getStride(); + // Align to 16 bytes boundary + int32_t uvStride = ((yStride / 2) + 15) & ~0x0F; + + libyuv::ConvertToI420(srcPtr, size, + dstPtr, yStride, + dstPtr + (yStride * dstHeight + (uvStride * dstHeight / 2)), uvStride, + dstPtr + (yStride * dstHeight), uvStride, + 0, 0, + aWidth, aHeight, + aWidth, aHeight, + static_cast(mRotation), + libyuv::FOURCC_NV21); + destBuffer->unlock(); + + layers::GrallocImage::GrallocData data; + + data.mPicSize = gfx::IntSize(dstWidth, dstHeight); + data.mGraphicBuffer = textureClient; + videoImage->SetData(data); + } else { + // Handle out of gralloc case. + image = mImageContainer->CreateImage(ImageFormat::PLANAR_YCBCR); + layers::PlanarYCbCrImage* videoImage = static_cast(image.get()); + uint8_t* dstPtr = videoImage->AllocateAndGetNewBuffer(size); + + libyuv::ConvertToI420(srcPtr, size, + dstPtr, dstWidth, + dstPtr + (dstWidth * dstHeight), half_width, + dstPtr + (dstWidth * dstHeight * 5 / 4), half_width, + 0, 0, + aWidth, aHeight, + aWidth, aHeight, + static_cast(mRotation), + ConvertPixelFormatToFOURCC(graphicBuffer->getPixelFormat())); + + const uint8_t lumaBpp = 8; + const uint8_t chromaBpp = 4; + + layers::PlanarYCbCrData data; + data.mYChannel = dstPtr; + data.mYSize = IntSize(dstWidth, dstHeight); + data.mYStride = dstWidth * lumaBpp / 8; + data.mCbCrStride = dstWidth * chromaBpp / 8; + data.mCbChannel = dstPtr + dstHeight * data.mYStride; + data.mCrChannel = data.mCbChannel + data.mCbCrStride * (dstHeight / 2); + data.mCbCrSize = IntSize(dstWidth / 2, dstHeight / 2); + data.mPicX = 0; + data.mPicY = 0; + data.mPicSize = IntSize(dstWidth, dstHeight); + data.mStereoMode = StereoMode::MONO; + + videoImage->SetDataNoCopy(data); } - RefPtr grallocTextureClient = - static_cast(textureClient.get()); - - android::sp destBuffer = grallocTextureClient->GetGraphicBuffer(); - - void* destMem = nullptr; - destBuffer->lock(android::GraphicBuffer::USAGE_SW_WRITE_OFTEN, &destMem); - uint8_t* dstPtr = static_cast(destMem); - - int32_t yStride = destBuffer->getStride(); - // Align to 16 bytes boundary - int32_t uvStride = ((yStride / 2) + 15) & ~0x0F; - - libyuv::ConvertToI420(srcPtr, size, - dstPtr, yStride, - dstPtr + (yStride * dstHeight + (uvStride * dstHeight / 2)), uvStride, - dstPtr + (yStride * dstHeight), uvStride, - 0, 0, - aWidth, aHeight, - aWidth, aHeight, - static_cast(mRotation), - libyuv::FOURCC_NV21); - destBuffer->unlock(); graphicBuffer->unlock(); - layers::GrallocImage::GrallocData data; - - data.mPicSize = gfx::IntSize(dstWidth, dstHeight); - data.mGraphicBuffer = textureClient; - videoImage->SetData(data); - // Implicitly releases last preview image. mImage = image.forget(); } @@ -767,9 +799,14 @@ MediaEngineGonkVideoSource::OnNewMediaBufferFrame(MediaBuffer* aBuffer) MonitorAutoLock enter(mMonitor); if (mImage) { - GonkCameraImage* cameraImage = static_cast(mImage.get()); - - cameraImage->SetBuffer(aBuffer); + if (mImage->AsGrallocImage()) { + // MediaEngineGonkVideoSource expects that GrallocImage is GonkCameraImage. + // See Bug 938034. + GonkCameraImage* cameraImage = static_cast(mImage.get()); + cameraImage->SetBuffer(aBuffer); + } else { + LOG(("mImage is non-GrallocImage")); + } uint32_t len = mSources.Length(); for (uint32_t i = 0; i < len; i++) { @@ -780,12 +817,15 @@ MediaEngineGonkVideoSource::OnNewMediaBufferFrame(MediaBuffer* aBuffer) // Unfortunately, clock in gonk camera looks like is a different one // comparing to MSG. As result, it causes time inaccurate. (frames be // queued in MSG longer and longer as time going by in device like Frame) - AppendToTrack(mSources[i], cameraImage, mTrackID, 1); + AppendToTrack(mSources[i], mImage, mTrackID, 1); } } - // Clear MediaBuffer immediately, it prevents MediaBuffer is kept in - // MediaStreamGraph thread. - cameraImage->ClearBuffer(); + if (mImage->AsGrallocImage()) { + GonkCameraImage* cameraImage = static_cast(mImage.get()); + // Clear MediaBuffer immediately, it prevents MediaBuffer is kept in + // MediaStreamGraph thread. + cameraImage->ClearBuffer(); + } } return NS_OK; diff --git a/dom/mobileconnection/MobileConnection.cpp b/dom/mobileconnection/MobileConnection.cpp index 95b79d38973e..a94310893493 100644 --- a/dom/mobileconnection/MobileConnection.cpp +++ b/dom/mobileconnection/MobileConnection.cpp @@ -11,7 +11,6 @@ #include "mozilla/dom/MozEmergencyCbModeEvent.h" #include "mozilla/dom/MozOtaStatusEvent.h" #include "mozilla/dom/ToJSValue.h" -#include "mozilla/dom/USSDReceivedEvent.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "nsIDOMDOMRequest.h" @@ -592,48 +591,6 @@ MobileConnection::GetVoicePrivacyMode(ErrorResult& aRv) return request.forget(); } -already_AddRefed -MobileConnection::SendMMI(const nsAString& aMMIString, ErrorResult& aRv) -{ - if (!mMobileConnection) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - nsRefPtr request = new DOMRequest(GetOwner()); - nsRefPtr requestCallback = - new MobileConnectionCallback(GetOwner(), request); - - nsresult rv = mMobileConnection->SendMMI(aMMIString, requestCallback); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return nullptr; - } - - return request.forget(); -} - -already_AddRefed -MobileConnection::CancelMMI(ErrorResult& aRv) -{ - if (!mMobileConnection) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - - nsRefPtr request = new DOMRequest(GetOwner()); - nsRefPtr requestCallback = - new MobileConnectionCallback(GetOwner(), request); - - nsresult rv = mMobileConnection->CancelMMI(requestCallback); - if (NS_FAILED(rv)) { - aRv.Throw(rv); - return nullptr; - } - - return request.forget(); -} - already_AddRefed MobileConnection::GetCallForwardingOption(uint16_t aReason, ErrorResult& aRv) { @@ -990,26 +947,6 @@ MobileConnection::NotifyDataChanged() return DispatchTrustedEvent(NS_LITERAL_STRING("datachange")); } -NS_IMETHODIMP -MobileConnection::NotifyUssdReceived(const nsAString& aMessage, - bool aSessionEnded) -{ - if (!CheckPermission("mobileconnection")) { - return NS_OK; - } - - USSDReceivedEventInit init; - init.mBubbles = false; - init.mCancelable = false; - init.mMessage = aMessage; - init.mSessionEnded = aSessionEnded; - - nsRefPtr event = - USSDReceivedEvent::Constructor(this, NS_LITERAL_STRING("ussdreceived"), init); - - return DispatchTrustedEvent(event); -} - NS_IMETHODIMP MobileConnection::NotifyDataError(const nsAString& aMessage) { diff --git a/dom/mobileconnection/MobileConnection.h b/dom/mobileconnection/MobileConnection.h index ba0aa1274d91..c639cc524f0d 100644 --- a/dom/mobileconnection/MobileConnection.h +++ b/dom/mobileconnection/MobileConnection.h @@ -107,12 +107,6 @@ public: already_AddRefed GetVoicePrivacyMode(ErrorResult& aRv); - already_AddRefed - SendMMI(const nsAString& aMmi, ErrorResult& aRv); - - already_AddRefed - CancelMMI(ErrorResult& aRv); - already_AddRefed SetCallForwardingOption(const MozCallForwardingOptions& aOptions, ErrorResult& aRv); @@ -150,7 +144,6 @@ public: IMPL_EVENT_HANDLER(voicechange) IMPL_EVENT_HANDLER(datachange) - IMPL_EVENT_HANDLER(ussdreceived) IMPL_EVENT_HANDLER(dataerror) IMPL_EVENT_HANDLER(cfstatechange) IMPL_EVENT_HANDLER(emergencycbmodechange) diff --git a/dom/mobileconnection/MobileConnectionCallback.cpp b/dom/mobileconnection/MobileConnectionCallback.cpp index d2ba2c1b938e..bc0d9cc9dd8f 100644 --- a/dom/mobileconnection/MobileConnectionCallback.cpp +++ b/dom/mobileconnection/MobileConnectionCallback.cpp @@ -4,7 +4,6 @@ #include "MobileConnectionCallback.h" -#include "mozilla/dom/DOMMMIError.h" #include "mozilla/dom/MobileNetworkInfo.h" #include "mozilla/dom/MozMobileConnectionBinding.h" #include "mozilla/dom/ToJSValue.h" @@ -31,28 +30,6 @@ MobileConnectionCallback::MobileConnectionCallback(nsPIDOMWindow* aWindow, { } -/** - * Notify Success for Send/CancelMmi. - */ -nsresult -MobileConnectionCallback::NotifySendCancelMmiSuccess(const MozMMIResult& aResult) -{ - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(mWindow))) { - return NS_ERROR_FAILURE; - } - - JSContext* cx = jsapi.cx(); - JS::Rooted jsResult(cx); - - if (!ToJSValue(cx, aResult, &jsResult)) { - JS_ClearPendingException(cx); - return NS_ERROR_TYPE_ERR; - } - - return NotifySuccess(jsResult); -} - /** * Notify Success. */ @@ -130,131 +107,6 @@ MobileConnectionCallback::NotifyGetNetworksSuccess(uint32_t aCount, return NotifySuccess(jsResult); } -nsresult -MobileConnectionCallback::NotifySendCancelMmiSuccess(const nsAString& aServiceCode, - const nsAString& aStatusMessage) -{ - MozMMIResult result; - result.mServiceCode.Assign(aServiceCode); - result.mStatusMessage.Assign(aStatusMessage); - - return NotifySendCancelMmiSuccess(result); -} - -nsresult -MobileConnectionCallback::NotifySendCancelMmiSuccessWithInteger(const nsAString& aServiceCode, - const nsAString& aStatusMessage, - uint16_t aAdditionalInformation) -{ - MozMMIResult result; - result.mServiceCode.Assign(aServiceCode); - result.mStatusMessage.Assign(aStatusMessage); - result.mAdditionalInformation.Construct().SetAsUnsignedShort() = aAdditionalInformation; - - return NotifySendCancelMmiSuccess(result); -} - -nsresult -MobileConnectionCallback::NotifySendCancelMmiSuccessWithStrings(const nsAString& aServiceCode, - const nsAString& aStatusMessage, - uint32_t aCount, - const char16_t** aAdditionalInformation) -{ - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(mWindow))) { - return NS_ERROR_FAILURE; - } - - JSContext* cx = jsapi.cx(); - RootedDictionary result(cx); - - result.mServiceCode.Assign(aServiceCode); - result.mStatusMessage.Assign(aStatusMessage); - - nsTArray additionalInformation; - for (uint32_t i = 0; i < aCount; i++) { - additionalInformation.AppendElement(nsDependentString(aAdditionalInformation[i])); - } - - JS::Rooted jsAdditionalInformation(cx); - if (!ToJSValue(cx, additionalInformation, &jsAdditionalInformation)) { - JS_ClearPendingException(cx); - return NS_ERROR_TYPE_ERR; - } - - result.mAdditionalInformation.Construct().SetAsObject() = - &jsAdditionalInformation.toObject(); - - return NotifySendCancelMmiSuccess(result); -} - -nsresult -MobileConnectionCallback::NotifySendCancelMmiSuccessWithCallForwardingOptions( - const nsAString& aServiceCode, - const nsAString& aStatusMessage, - uint32_t aCount, - nsIMobileCallForwardingOptions** aResults) -{ - AutoJSAPI jsapi; - if (NS_WARN_IF(!jsapi.Init(mWindow))) { - return NS_ERROR_FAILURE; - } - - JSContext* cx = jsapi.cx(); - RootedDictionary result(cx); - - result.mServiceCode.Assign(aServiceCode); - result.mStatusMessage.Assign(aStatusMessage); - - nsTArray additionalInformation; - for (uint32_t i = 0; i < aCount; i++) - { - MozCallForwardingOptions options; - int16_t pShort; - nsString pString; - bool pBool; - - aResults[i]->GetActive(&pBool); - options.mActive.Construct(pBool); - - aResults[i]->GetAction(&pShort); - if (pShort != nsIMobileConnection::CALL_FORWARD_ACTION_UNKNOWN) { - options.mAction.Construct(pShort); - } - - aResults[i]->GetReason(&pShort); - if (pShort != nsIMobileConnection::CALL_FORWARD_REASON_UNKNOWN) { - options.mReason.Construct(pShort); - } - - aResults[i]->GetNumber(pString); - options.mNumber.Construct(pString.get()); - - aResults[i]->GetTimeSeconds(&pShort); - if (pShort >= 0) { - options.mTimeSeconds.Construct(pShort); - } - - aResults[i]->GetServiceClass(&pShort); - if (pShort != nsIMobileConnection::ICC_SERVICE_CLASS_NONE) { - options.mServiceClass.Construct(pShort); - } - - additionalInformation.AppendElement(options); - } - - JS::Rooted jsAdditionalInformation(cx); - if (!ToJSValue(cx, additionalInformation, &jsAdditionalInformation)) { - JS_ClearPendingException(cx); - return NS_ERROR_TYPE_ERR; - } - - result.mAdditionalInformation.Construct().SetAsObject() = - &jsAdditionalInformation.toObject(); - - return NotifySendCancelMmiSuccess(result); -} - NS_IMETHODIMP MobileConnectionCallback::NotifyGetCallForwardingSuccess(uint32_t aCount, nsIMobileCallForwardingOptions** aResults) @@ -384,34 +236,11 @@ MobileConnectionCallback::NotifyGetRoamingPreferenceSuccess(int32_t aMode) }; NS_IMETHODIMP -MobileConnectionCallback::NotifyError(const nsAString& aName, - const nsAString& aMessage, - const nsAString& aServiceCode, - uint16_t aInfo, - uint8_t aArgc) +MobileConnectionCallback::NotifyError(const nsAString& aName) { nsCOMPtr rs = do_GetService(DOMREQUEST_SERVICE_CONTRACTID); NS_ENSURE_TRUE(rs, NS_ERROR_FAILURE); - - nsRefPtr error; - switch (aArgc) { - case 0: - return rs->FireErrorAsync(mRequest, aName); - case 1: - error = new DOMMMIError(mWindow, aName, aMessage, EmptyString(), - Nullable()); - return rs->FireDetailedError(mRequest, error); - case 2: - error = new DOMMMIError(mWindow, aName, aMessage, aServiceCode, - Nullable()); - return rs->FireDetailedError(mRequest, error); - case 3: - error = new DOMMMIError(mWindow, aName, aMessage, aServiceCode, - Nullable(int16_t(aInfo))); - return rs->FireDetailedError(mRequest, error); - } - - return NS_ERROR_FAILURE; + return rs->FireErrorAsync(mRequest, aName); } } // namespace mobileconnection diff --git a/dom/mobileconnection/MobileConnectionCallback.h b/dom/mobileconnection/MobileConnectionCallback.h index 739c04f278d9..33f5eedcfd26 100644 --- a/dom/mobileconnection/MobileConnectionCallback.h +++ b/dom/mobileconnection/MobileConnectionCallback.h @@ -41,9 +41,6 @@ private: nsresult NotifySuccessWithString(const nsAString& aResult); - nsresult - NotifySendCancelMmiSuccess(const MozMMIResult& aResult); - nsCOMPtr mWindow; nsRefPtr mRequest; }; diff --git a/dom/mobileconnection/gonk/MobileConnectionService.js b/dom/mobileconnection/gonk/MobileConnectionService.js index 726d3b898104..7e99be187e16 100644 --- a/dom/mobileconnection/gonk/MobileConnectionService.js +++ b/dom/mobileconnection/gonk/MobileConnectionService.js @@ -27,8 +27,6 @@ const MOBILECELLINFO_CID = Components.ID("{0635d9ab-997e-4cdf-84e7-c1883752dff3}"); const MOBILECALLFORWARDINGOPTIONS_CID = Components.ID("{e0cf4463-ee63-4b05-ab2e-d94bf764836c}"); -const TELEPHONYDIALCALLBACK_CID = - Components.ID("{c2af1a5d-3649-44ef-a1ff-18e9ac1dec51}"); const NEIGHBORINGCELLINFO_CID = Components.ID("{6078cbf1-f34c-44fa-96f8-11a88d4bfdd3}"); const GSMCELLINFO_CID = @@ -280,62 +278,6 @@ CdmaCellInfo.prototype = { evdoSnr: INT32_MAX }; -/** - * Wrap a MobileConnectionCallback to a TelephonyDialCallback. - */ -function TelephonyDialCallback(aCallback) { - this.callback = aCallback; -} -TelephonyDialCallback.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyDialCallback]), - classID: TELEPHONYDIALCALLBACK_CID, - - notifyDialMMI: function(mmiServiceCode) { - this.serviceCode = mmiServiceCode; - }, - - notifyDialMMISuccess: function(statusMessage) { - this.callback.notifySendCancelMmiSuccess(this.serviceCode, statusMessage); - }, - - notifyDialMMISuccessWithInteger: function(statusMessage, additionalInfo) { - this.callback.notifySendCancelMmiSuccessWithInteger(this.serviceCode, - statusMessage, - additionalInfo); - }, - - notifyDialMMISuccessWithStrings: function(statusMessage, count, additionalInfo) { - this.callback.notifySendCancelMmiSuccessWithStrings(this.serviceCode, - statusMessage, - count, - additionalInfo); - }, - - notifyDialMMISuccessWithCallForwardingOptions: function(statusMessage, count, additionalInfo) { - this.callback.notifySendCancelMmiSuccessWithCallForwardingOptions( - this.serviceCode, - statusMessage, - count, - additionalInfo); - }, - - notifyDialMMIError: function(error) { - this.callback.notifyError(error, "", this.serviceCode); - }, - - notifyDialMMIErrorWithInfo: function(error, info) { - this.callback.notifyError(error, "", this.serviceCode, info); - }, - - notifyDialError: function() { - throw Cr.NS_ERROR_UNEXPECTED; - }, - - notifyDialSuccess: function() { - throw Cr.NS_ERROR_UNEXPECTED; - }, -}; - function MobileConnectionProvider(aClientId, aRadioInterface) { this._clientId = aClientId; this._radioInterface = aRadioInterface; @@ -833,24 +775,6 @@ MobileConnectionProvider.prototype = { }).bind(this)); }, - sendMMI: function(aMmi, aCallback) { - let callback = new TelephonyDialCallback(aCallback); - gGonkTelephonyService.dialMMI(this._clientId, aMmi, callback); - }, - - cancelMMI: function(aCallback) { - this._radioInterface.sendWorkerMessage("cancelUSSD", null, - (function(aResponse) { - if (aResponse.errorMsg) { - aCallback.notifyError(aResponse.errorMsg); - return false; - } - - aCallback.notifySuccess(); - return false; - }).bind(this)); - }, - setCallForwarding: function(aAction, aReason, aNumber, aTimeSeconds, aServiceClass, aCallback) { let options = { @@ -1190,18 +1114,6 @@ MobileConnectionService.prototype = { this.getItemByServiceId(aClientId).updateDataInfo(aDataInfo); }, - notifyUssdReceived: function(aClientId, aMessage, aSessionEnded) { - if (DEBUG) { - debug("notifyUssdReceived for " + aClientId + ": " + - aMessage + " (sessionEnded : " + aSessionEnded + ")"); - } - - gMobileConnectionMessenger.notifyUssdReceived(aClientId, aMessage, aSessionEnded); - - this.getItemByServiceId(aClientId) - .deliverListenerEvent("notifyUssdReceived", [aMessage, aSessionEnded]); - }, - notifyDataError: function(aClientId, aMessage) { if (DEBUG) { debug("notifyDataError for " + aClientId + ": " + aMessage); diff --git a/dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl b/dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl index 017f506b4563..6bc9002523b5 100644 --- a/dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl +++ b/dom/mobileconnection/gonk/nsIGonkMobileConnectionService.idl @@ -9,7 +9,7 @@ "@mozilla.org/mobileconnection/gonkmobileconnectionservice;1" %} -[scriptable, uuid(eae40ffe-394a-4355-8e0b-07170d3e70f4)] +[scriptable, uuid(ef49b866-85a0-11e4-b023-f73e02752840)] interface nsIGonkMobileConnectionService : nsIMobileConnectionService { void notifyNetworkInfoChanged(in unsigned long clientId, in jsval networkInfo); @@ -29,10 +29,6 @@ interface nsIGonkMobileConnectionService : nsIMobileConnectionService void notifyRadioStateChanged(in unsigned long clientId, in long radioState); - void notifyUssdReceived(in unsigned long clientId, - in DOMString message, - in boolean sessionEnded); - void notifyEmergencyCallbackModeChanged(in unsigned long clientId, in boolean active, in unsigned long timeoutMs); diff --git a/dom/mobileconnection/gonk/nsIMobileConnectionMessenger.idl b/dom/mobileconnection/gonk/nsIMobileConnectionMessenger.idl index 7337771a69b2..9fb7d5a52716 100644 --- a/dom/mobileconnection/gonk/nsIMobileConnectionMessenger.idl +++ b/dom/mobileconnection/gonk/nsIMobileConnectionMessenger.idl @@ -4,23 +4,9 @@ #include "nsISupports.idl" -[scriptable, uuid(fd2fa95c-5b54-11e4-bc6b-6f3bffb681cd)] +[scriptable, uuid(b9ec941e-8504-11e4-810e-7b62c60e8261)] interface nsIMobileConnectionMessenger : nsISupports { - /** - * 'ussd-received' system message - * - * @param aServiceId - * The ID of Service where this info is notified from. - * @param aMessage - * USSD Message to be displayed. - * @param aSessionEnded - * True if USSD session is ended. - */ - void notifyUssdReceived(in unsigned long aServiceId, - in DOMString aMessage, - in boolean aSessionEnded); - /** * 'cdma-info-rec-received' system message with Display Info * diff --git a/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl b/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl index 1fc169404974..31cd17c0e890 100644 --- a/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl +++ b/dom/mobileconnection/interfaces/nsIMobileConnectionService.idl @@ -12,7 +12,7 @@ interface nsIMobileNetworkInfo; interface nsINeighboringCellIdsCallback; interface nsIVariant; -[scriptable, uuid(c00abd30-5b2e-11e4-8ed6-0800200c9a66)] +[scriptable, uuid(6e6468a4-84fb-11e4-9b66-17dbe13c059e)] interface nsIMobileConnectionListener : nsISupports { /** @@ -25,17 +25,6 @@ interface nsIMobileConnectionListener : nsISupports */ void notifyDataChanged(); - /** - * Notify when ussd is received. - * - * @param message - * The ussd request in string format. - * @param sessionEnded - * Indicates whether the session is ended. - */ - void notifyUssdReceived(in DOMString message, - in boolean sessionEnded); - /** * Notify when data call is failed to establish. * @@ -124,7 +113,7 @@ interface nsIMobileConnectionListener : nsISupports #define NO_ADDITIONAL_INFORMATION 0 %} -[scriptable, builtinclass, uuid(16e77f19-0298-46de-ae49-9b2fb92a28c0)] +[scriptable, builtinclass, uuid(14d66926-8434-11e4-8c3f-f724194bb5f1)] interface nsIMobileConnectionCallback : nsISupports { /** @@ -137,27 +126,9 @@ interface nsIMobileConnectionCallback : nsISupports void notifyGetNetworksSuccess(in uint32_t count, [array, size_is(count)] in nsIMobileNetworkInfo networks); - void notifySendCancelMmiSuccess(in DOMString aServiceCode, - in DOMString aStatusMessage); - - void notifySendCancelMmiSuccessWithInteger(in DOMString aServiceCode, - in DOMString aStatusMessage, - in unsigned short aAdditionalInformation); - - void notifySendCancelMmiSuccessWithStrings(in DOMString aServiceCode, - in DOMString aStatusMessage, - in unsigned long aLength, - [array, size_is(aLength)] in wstring aAdditionalInformation); - - void notifySendCancelMmiSuccessWithCallForwardingOptions(in DOMString aServiceCode, - in DOMString aStatusMessage, - in unsigned long aLength, - [array, size_is(aLength)] in nsIMobileCallForwardingOptions aAdditionalInformation); - void notifyGetCallForwardingSuccess(in uint32_t count, [array, size_is(count)] in nsIMobileCallForwardingOptions results); - void notifyGetCallBarringSuccess(in unsigned short program, in boolean enabled, in unsigned short serviceClass); @@ -171,45 +142,7 @@ interface nsIMobileConnectionCallback : nsISupports /** * notifyError() will be called, when request is failed. */ - [optional_argc] - void notifyError(in DOMString name, - [optional] in DOMString message, - [optional] in DOMString serviceCode, - [optional] in unsigned short additionalInformation); - -%{C++ - // non-virtual so it won't affect the vtable - inline nsresult NotifyError(const nsAString& aName) - { - return NotifyError(aName, EmptyString(), EmptyString(), - NO_ADDITIONAL_INFORMATION, 0 /* ARGC = 0 */); - } - // non-virtual so it won't affect the vtable - inline nsresult NotifyError(const nsAString& aName, - const nsAString& aMessage) - { - return NotifyError(aName, aMessage, EmptyString(), NO_ADDITIONAL_INFORMATION, - 1 /* ARGC = 1 */); - } - // non-virtual so it won't affect the vtable - inline nsresult NotifyError(const nsAString& aName, - const nsAString& aMessage, - const nsAString& aServiceCode) - { - return NotifyError(aName, aMessage, aServiceCode, NO_ADDITIONAL_INFORMATION, - 2 /* ARGC = 2 */); - } - // non-virtual so it won't affect the vtable - inline nsresult NotifyError(const nsAString& aName, - const nsAString& aMessage, - const nsAString& aServiceCode, - uint16_t aAdditionInformation) - { - return NotifyError(aName, aMessage, aServiceCode, aAdditionInformation, - 3 /* ARGC = 3 */); - } -%} - + void notifyError(in DOMString name); }; %{C++ @@ -235,7 +168,7 @@ already_AddRefed NS_CreateMobileConnectionService(); %} -[scriptable, uuid(99e43353-5fc4-497e-88a2-5fa6862ee64c)] +[scriptable, uuid(2b3d0122-8054-11e4-964e-c727f38fd7e6)] interface nsIMobileConnection : nsISupports { /* @@ -549,36 +482,6 @@ interface nsIMobileConnection : nsISupports */ void getVoicePrivacyMode(in nsIMobileConnectionCallback requestCallback); - /** - * Send a MMI message. - * - * @param mmi - * DOMString containing an MMI string that can be associated to a - * USSD request or other RIL functionality. - * @param requestCallback - * Called when request is finished. - * - * If successful, the notifySendCancelMmiSuccess*() will be called. And the - * result will contain the information about the mmi operation. - * - * Otherwise, the notifyError() will be called. - */ - void sendMMI(in DOMString mmi, - in nsIMobileConnectionCallback requestCallback); - - /** - * Cancel the current MMI request if one exists. - * - * @param requestCallback - * Called when request is finished. - * - * If successful, the notifySendCancelMmiSuccess*() will be called. And the - * result will contain the information about the mmi operation. - * - * Otherwise, the notifyError() will be called. - */ - void cancelMMI(in nsIMobileConnectionCallback requestCallback); - /** * Queries current call forwarding options. * diff --git a/dom/mobileconnection/ipc/MobileConnectionChild.cpp b/dom/mobileconnection/ipc/MobileConnectionChild.cpp index a970079ac83c..d052e36c2896 100644 --- a/dom/mobileconnection/ipc/MobileConnectionChild.cpp +++ b/dom/mobileconnection/ipc/MobileConnectionChild.cpp @@ -222,20 +222,6 @@ MobileConnectionChild::GetVoicePrivacyMode(nsIMobileConnectionCallback* aCallbac ? NS_OK : NS_ERROR_FAILURE; } -NS_IMETHODIMP -MobileConnectionChild::SendMMI(const nsAString& aMmi, - nsIMobileConnectionCallback* aCallback) -{ - return SendRequest(SendMmiRequest(nsAutoString(aMmi)), aCallback) - ? NS_OK : NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -MobileConnectionChild::CancelMMI(nsIMobileConnectionCallback* aCallback) -{ - return SendRequest(CancelMmiRequest(), aCallback) ? NS_OK : NS_ERROR_FAILURE; -} - NS_IMETHODIMP MobileConnectionChild::SetCallForwarding(uint16_t aAction, uint16_t aReason, const nsAString& aNumber, @@ -415,17 +401,6 @@ MobileConnectionChild::RecvNotifyDataInfoChanged(nsIMobileConnectionInfo* const& return true; } -bool -MobileConnectionChild::RecvNotifyUssdReceived(const nsString& aMessage, - const bool& aSessionEnd) -{ - for (int32_t i = 0; i < mListeners.Count(); i++) { - mListeners[i]->NotifyUssdReceived(aMessage, aSessionEnd); - } - - return true; -} - bool MobileConnectionChild::RecvNotifyDataError(const nsString& aMessage) { @@ -568,60 +543,6 @@ MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessNetworks const_cast(aReply.results().Elements()))); } -bool -MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessMmi& aReply) -{ - nsAutoString serviceCode(aReply.serviceCode()); - nsAutoString statusMessage(aReply.statusMessage()); - AdditionalInformation info(aReply.additionalInformation()); - - // Handle union types - switch (info.type()) { - case AdditionalInformation::Tvoid_t: - return NS_SUCCEEDED(mRequestCallback->NotifySendCancelMmiSuccess(serviceCode, - statusMessage)); - - case AdditionalInformation::Tuint16_t: - return NS_SUCCEEDED(mRequestCallback->NotifySendCancelMmiSuccessWithInteger( - serviceCode, statusMessage, info.get_uint16_t())); - - case AdditionalInformation::TArrayOfnsString: { - uint32_t count = info.get_ArrayOfnsString().Length(); - const nsTArray& additionalInformation = info.get_ArrayOfnsString(); - - nsAutoArrayPtr additionalInfoPtrs(new const char16_t*[count]); - for (size_t i = 0; i < count; ++i) { - additionalInfoPtrs[i] = additionalInformation[i].get(); - } - - return NS_SUCCEEDED(mRequestCallback->NotifySendCancelMmiSuccessWithStrings( - serviceCode, statusMessage, count, additionalInfoPtrs)); - } - - case AdditionalInformation::TArrayOfnsMobileCallForwardingOptions: { - uint32_t count = info.get_ArrayOfnsMobileCallForwardingOptions().Length(); - - nsTArray> results; - for (uint32_t i = 0; i < count; i++) { - // Use dont_AddRef here because these instances are already AddRef-ed in - // MobileConnectionIPCSerializer.h - nsCOMPtr item = dont_AddRef( - info.get_ArrayOfnsMobileCallForwardingOptions()[i]); - results.AppendElement(item); - } - - return NS_SUCCEEDED(mRequestCallback->NotifySendCancelMmiSuccessWithCallForwardingOptions( - serviceCode, statusMessage, count, - const_cast(info.get_ArrayOfnsMobileCallForwardingOptions().Elements()))); - } - - default: - MOZ_CRASH("Received invalid type!"); - } - - return false; -} - bool MobileConnectionRequestChild::DoReply(const MobileConnectionReplySuccessCallForwarding& aReply) { @@ -671,32 +592,6 @@ MobileConnectionRequestChild::DoReply(const MobileConnectionReplyError& aReply) return NS_SUCCEEDED(mRequestCallback->NotifyError(aReply.message())); } -bool -MobileConnectionRequestChild::DoReply(const MobileConnectionReplyErrorMmi& aReply) -{ - nsAutoString name(aReply.name()); - nsAutoString message(aReply.message()); - nsAutoString serviceCode(aReply.serviceCode()); - AdditionalInformation info(aReply.additionalInformation()); - - // Handle union types - switch (info.type()) { - case AdditionalInformation::Tuint16_t: - return NS_SUCCEEDED(mRequestCallback->NotifyError(name, - message, - serviceCode, - info.get_uint16_t())); - case AdditionalInformation::Tvoid_t: - default: - // If additionInfomation is not uint16_t, handle it as void_t. - return NS_SUCCEEDED(mRequestCallback->NotifyError(name, - message, - serviceCode)); - } - - return false; -} - bool MobileConnectionRequestChild::Recv__delete__(const MobileConnectionReply& aReply) { @@ -709,8 +604,6 @@ MobileConnectionRequestChild::Recv__delete__(const MobileConnectionReply& aReply return DoReply(aReply.get_MobileConnectionReplySuccessBoolean()); case MobileConnectionReply::TMobileConnectionReplySuccessNetworks: return DoReply(aReply.get_MobileConnectionReplySuccessNetworks()); - case MobileConnectionReply::TMobileConnectionReplySuccessMmi: - return DoReply(aReply.get_MobileConnectionReplySuccessMmi()); case MobileConnectionReply::TMobileConnectionReplySuccessCallForwarding: return DoReply(aReply.get_MobileConnectionReplySuccessCallForwarding()); case MobileConnectionReply::TMobileConnectionReplySuccessCallBarring: @@ -723,8 +616,6 @@ MobileConnectionRequestChild::Recv__delete__(const MobileConnectionReply& aReply return DoReply(aReply.get_MobileConnectionReplySuccessRoamingPreference()); case MobileConnectionReply::TMobileConnectionReplyError: return DoReply(aReply.get_MobileConnectionReplyError()); - case MobileConnectionReply::TMobileConnectionReplyErrorMmi: - return DoReply(aReply.get_MobileConnectionReplyErrorMmi()); default: MOZ_CRASH("Received invalid response type!"); } diff --git a/dom/mobileconnection/ipc/MobileConnectionChild.h b/dom/mobileconnection/ipc/MobileConnectionChild.h index 5a01f16d4107..d861ea6d4ac8 100644 --- a/dom/mobileconnection/ipc/MobileConnectionChild.h +++ b/dom/mobileconnection/ipc/MobileConnectionChild.h @@ -69,10 +69,6 @@ protected: virtual bool RecvNotifyDataInfoChanged(nsIMobileConnectionInfo* const& aInfo) MOZ_OVERRIDE; - virtual bool - RecvNotifyUssdReceived(const nsString& aMessage, - const bool& aSessionEnd) MOZ_OVERRIDE; - virtual bool RecvNotifyDataError(const nsString& aMessage) MOZ_OVERRIDE; @@ -148,9 +144,6 @@ public: bool DoReply(const MobileConnectionReplySuccessNetworks& aReply); - bool - DoReply(const MobileConnectionReplySuccessMmi& aReply); - bool DoReply(const MobileConnectionReplySuccessCallForwarding& aReply); @@ -169,9 +162,6 @@ public: bool DoReply(const MobileConnectionReplyError& aReply); - bool - DoReply(const MobileConnectionReplyErrorMmi& aReply); - protected: virtual ~MobileConnectionRequestChild() diff --git a/dom/mobileconnection/ipc/MobileConnectionParent.cpp b/dom/mobileconnection/ipc/MobileConnectionParent.cpp index cb2416883f99..83c3ff34208c 100644 --- a/dom/mobileconnection/ipc/MobileConnectionParent.cpp +++ b/dom/mobileconnection/ipc/MobileConnectionParent.cpp @@ -67,10 +67,6 @@ MobileConnectionParent::RecvPMobileConnectionRequestConstructor(PMobileConnectio return actor->DoRequest(aRequest.get_SetVoicePrivacyModeRequest()); case MobileConnectionRequest::TGetVoicePrivacyModeRequest: return actor->DoRequest(aRequest.get_GetVoicePrivacyModeRequest()); - case MobileConnectionRequest::TSendMmiRequest: - return actor->DoRequest(aRequest.get_SendMmiRequest()); - case MobileConnectionRequest::TCancelMmiRequest: - return actor->DoRequest(aRequest.get_CancelMmiRequest()); case MobileConnectionRequest::TSetCallForwardingRequest: return actor->DoRequest(aRequest.get_SetCallForwardingRequest()); case MobileConnectionRequest::TGetCallForwardingRequest: @@ -192,16 +188,6 @@ MobileConnectionParent::NotifyDataChanged() return SendNotifyDataInfoChanged(info.forget().take()) ? NS_OK : NS_ERROR_FAILURE; } -NS_IMETHODIMP -MobileConnectionParent::NotifyUssdReceived(const nsAString& aMessage, - bool aSessionEnded) -{ - NS_ENSURE_TRUE(mLive, NS_ERROR_FAILURE); - - return SendNotifyUssdReceived(nsAutoString(aMessage), aSessionEnded) - ? NS_OK : NS_ERROR_FAILURE; -} - NS_IMETHODIMP MobileConnectionParent::NotifyDataError(const nsAString& aMessage) { @@ -399,22 +385,6 @@ MobileConnectionRequestParent::DoRequest(const GetVoicePrivacyModeRequest& aRequ return NS_SUCCEEDED(mMobileConnection->GetVoicePrivacyMode(this)); } -bool -MobileConnectionRequestParent::DoRequest(const SendMmiRequest& aRequest) -{ - NS_ENSURE_TRUE(mMobileConnection, false); - - return NS_SUCCEEDED(mMobileConnection->SendMMI(aRequest.mmi(), this)); -} - -bool -MobileConnectionRequestParent::DoRequest(const CancelMmiRequest& aRequest) -{ - NS_ENSURE_TRUE(mMobileConnection, false); - - return NS_SUCCEEDED(mMobileConnection->CancelMMI(this)); -} - bool MobileConnectionRequestParent::DoRequest(const SetCallForwardingRequest& aRequest) { @@ -556,57 +526,6 @@ MobileConnectionRequestParent::NotifyGetNetworksSuccess(uint32_t aCount, return SendReply(MobileConnectionReplySuccessNetworks(networks)); } -NS_IMETHODIMP -MobileConnectionRequestParent::NotifySendCancelMmiSuccess(const nsAString& aServiceCode, - const nsAString& aStatusMessage) -{ - return SendReply(MobileConnectionReplySuccessMmi(nsString(aServiceCode), - nsString(aStatusMessage), - AdditionalInformation(mozilla::void_t()))); -} - -NS_IMETHODIMP -MobileConnectionRequestParent::NotifySendCancelMmiSuccessWithInteger(const nsAString& aServiceCode, - const nsAString& aStatusMessage, - uint16_t aAdditionalInformation) -{ - return SendReply(MobileConnectionReplySuccessMmi(nsString(aServiceCode), - nsString(aStatusMessage), - AdditionalInformation(aAdditionalInformation))); -} - -NS_IMETHODIMP -MobileConnectionRequestParent::NotifySendCancelMmiSuccessWithStrings(const nsAString& aServiceCode, - const nsAString& aStatusMessage, - uint32_t aCount, - const char16_t** aAdditionalInformation) -{ - nsTArray additionalInformation; - for (uint32_t i = 0; i < aCount; i++) { - additionalInformation.AppendElement(nsDependentString(aAdditionalInformation[i])); - } - - return SendReply(MobileConnectionReplySuccessMmi(nsString(aServiceCode), - nsString(aStatusMessage), - AdditionalInformation(additionalInformation))); -} - -NS_IMETHODIMP -MobileConnectionRequestParent::NotifySendCancelMmiSuccessWithCallForwardingOptions(const nsAString& aServiceCode, - const nsAString& aStatusMessage, - uint32_t aCount, - nsIMobileCallForwardingOptions** aAdditionalInformation) -{ - nsTArray additionalInformation; - for (uint32_t i = 0; i < aCount; i++) { - additionalInformation.AppendElement(aAdditionalInformation[i]); - } - - return SendReply(MobileConnectionReplySuccessMmi(nsString(aServiceCode), - nsString(aStatusMessage), - AdditionalInformation(additionalInformation))); -} - NS_IMETHODIMP MobileConnectionRequestParent::NotifyGetCallForwardingSuccess(uint32_t aCount, nsIMobileCallForwardingOptions** aResults) @@ -648,26 +567,8 @@ MobileConnectionRequestParent::NotifyGetRoamingPreferenceSuccess(int32_t aMode) } NS_IMETHODIMP -MobileConnectionRequestParent::NotifyError(const nsAString& aName, - const nsAString& aMessage, - const nsAString& aServiceCode, - uint16_t aInfo, - uint8_t aArgc) +MobileConnectionRequestParent::NotifyError(const nsAString& aName) { - if (aArgc == 0) { - nsAutoString error(aName); - return SendReply(MobileConnectionReplyError(error)); - } - - nsAutoString name(aName); - nsAutoString message(aMessage); - nsAutoString serviceCode(aServiceCode); - - if (aArgc < 3) { - return SendReply(MobileConnectionReplyErrorMmi(name, message, serviceCode, - AdditionalInformation(mozilla::void_t()))); - } - - return SendReply(MobileConnectionReplyErrorMmi(name, message, serviceCode, - AdditionalInformation(aInfo))); + nsAutoString error(aName); + return SendReply(MobileConnectionReplyError(error)); } diff --git a/dom/mobileconnection/ipc/MobileConnectionParent.h b/dom/mobileconnection/ipc/MobileConnectionParent.h index 7f9a7c09dd7e..5e78d35d29d7 100644 --- a/dom/mobileconnection/ipc/MobileConnectionParent.h +++ b/dom/mobileconnection/ipc/MobileConnectionParent.h @@ -111,12 +111,6 @@ public: bool DoRequest(const GetVoicePrivacyModeRequest& aRequest); - bool - DoRequest(const SendMmiRequest& aRequest); - - bool - DoRequest(const CancelMmiRequest& aRequest); - bool DoRequest(const SetCallForwardingRequest& aRequest); diff --git a/dom/mobileconnection/ipc/PMobileConnection.ipdl b/dom/mobileconnection/ipc/PMobileConnection.ipdl index 41feaafd93d3..7281931d31c7 100644 --- a/dom/mobileconnection/ipc/PMobileConnection.ipdl +++ b/dom/mobileconnection/ipc/PMobileConnection.ipdl @@ -20,7 +20,6 @@ sync protocol PMobileConnection child: NotifyVoiceInfoChanged(nsMobileConnectionInfo aInfo); NotifyDataInfoChanged(nsMobileConnectionInfo aInfo); - NotifyUssdReceived(nsString aMessage, bool aSessionEnd); NotifyDataError(nsString aMessage); NotifyCFStateChanged(uint16_t aAction, uint16_t aReason, nsString aNumber, uint16_t aTimeSeconds, uint16_t aServiceClass); @@ -97,15 +96,6 @@ struct GetVoicePrivacyModeRequest { }; -struct SendMmiRequest -{ - nsString mmi; -}; - -struct CancelMmiRequest -{ -}; - struct SetCallForwardingRequest { uint16_t action; @@ -179,8 +169,6 @@ union MobileConnectionRequest GetRoamingPreferenceRequest; SetVoicePrivacyModeRequest; GetVoicePrivacyModeRequest; - SendMmiRequest; - CancelMmiRequest; SetCallForwardingRequest; GetCallForwardingRequest; SetCallBarringRequest; diff --git a/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl b/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl index 2c54373e82dd..ce7ed401bf78 100644 --- a/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl +++ b/dom/mobileconnection/ipc/PMobileConnectionRequest.ipdl @@ -40,13 +40,6 @@ struct MobileConnectionReplySuccessNetworks nsMobileNetworkInfo[] results; }; -struct MobileConnectionReplySuccessMmi -{ - nsString serviceCode; - nsString statusMessage; - AdditionalInformation additionalInformation; -}; - struct MobileConnectionReplySuccessCallForwarding { nsMobileCallForwardingOptions[] results; @@ -81,21 +74,12 @@ struct MobileConnectionReplyError nsString message; }; -struct MobileConnectionReplyErrorMmi -{ - nsString name; - nsString message; - nsString serviceCode; - AdditionalInformation additionalInformation; -}; - union MobileConnectionReply { // Success MobileConnectionReplySuccess; MobileConnectionReplySuccessBoolean; MobileConnectionReplySuccessNetworks; - MobileConnectionReplySuccessMmi; MobileConnectionReplySuccessCallForwarding; MobileConnectionReplySuccessCallBarring; MobileConnectionReplySuccessClirStatus; @@ -103,7 +87,6 @@ union MobileConnectionReply MobileConnectionReplySuccessRoamingPreference; // Error MobileConnectionReplyError; - MobileConnectionReplyErrorMmi; }; } // namespace mobileconnection diff --git a/dom/mobileconnection/tests/marionette/manifest.ini b/dom/mobileconnection/tests/marionette/manifest.ini index dea9c104d0ae..ba36f1acf242 100644 --- a/dom/mobileconnection/tests/marionette/manifest.ini +++ b/dom/mobileconnection/tests/marionette/manifest.ini @@ -14,10 +14,6 @@ qemu = true [test_mobile_data_connection.js] [test_mobile_data_location.js] [test_mobile_data_state.js] -[test_mobile_mmi.js] -[test_mobile_mmi_change_pin.js] -[test_mobile_mmi_call_forwarding.js] -[test_mobile_mmi_unlock_puk.js] [test_mobile_roaming_preference.js] [test_call_barring_get_option.js] [test_call_barring_set_error.js] diff --git a/dom/mobileconnection/tests/marionette/test_mobile_mmi.js b/dom/mobileconnection/tests/marionette/test_mobile_mmi.js deleted file mode 100644 index c1424d0e1c93..000000000000 --- a/dom/mobileconnection/tests/marionette/test_mobile_mmi.js +++ /dev/null @@ -1,46 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = "head.js"; - -function testGettingIMEI() { - log("Test *#06# ..."); - - let MMI_CODE = "*#06#"; - return sendMMI(MMI_CODE) - .then(function resolve(aResult) { - ok(true, MMI_CODE + " success"); - is(aResult.serviceCode, "scImei", "Service code IMEI"); - // IMEI is hardcoded as "000000000000000". - // See it here {B2G_HOME}/external/qemu/telephony/android_modem.c - // (The result of +CGSN). - is(aResult.statusMessage, "000000000000000", "Emulator IMEI"); - is(aResult.additionalInformation, undefined, "No additional information"); - }, function reject() { - ok(false, MMI_CODE + " should not fail"); - }); -} - -function testInvalidMMICode() { - log("Test invalid MMI code ..."); - - let MMI_CODE = "InvalidMMICode"; - return sendMMI(MMI_CODE) - .then(function resolve() { - ok(false, MMI_CODE + " should not success"); - }, function reject(aError) { - ok(true, MMI_CODE + " fail"); - is(aError.name, "emMmiError", "MMI error name"); - is(aError.message, "", "No message"); - is(aError.serviceCode, "scUssd", "Service code USSD"); - is(aError.additionalInformation, null, "No additional information"); - }); -} - -// Start test -startTestCommon(function() { - return Promise.resolve() - .then(() => testGettingIMEI()) - .then(() => testInvalidMMICode()); -}); diff --git a/dom/mobileconnection/tests/marionette/test_mobile_mmi_call_forwarding.js b/dom/mobileconnection/tests/marionette/test_mobile_mmi_call_forwarding.js deleted file mode 100644 index 594fcc3a05a5..000000000000 --- a/dom/mobileconnection/tests/marionette/test_mobile_mmi_call_forwarding.js +++ /dev/null @@ -1,144 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = "head.js"; - -const TEST_DATA = [ - { - reason: MozMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL, - number: "+886912345678", - serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE, - timeSeconds: 5 - }, { - reason: MozMobileConnection.CALL_FORWARD_REASON_MOBILE_BUSY, - number: "0912345678", - serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE, - timeSeconds: 10 - }, { - reason: MozMobileConnection.CALL_FORWARD_REASON_NO_REPLY, - number: "+886987654321", - serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE, - timeSeconds: 15 - }, { - reason: MozMobileConnection.CALL_FORWARD_REASON_NOT_REACHABLE, - number: "+0987654321", - serviceClass: MozMobileConnection.ICC_SERVICE_CLASS_VOICE, - timeSeconds: 20 - } -]; - -// Please see TS 22.030 Annex B -const CF_REASON_TO_MMI = { - /* CALL_FORWARD_REASON_UNCONDITIONAL */ - 0: "21", - /* CALL_FORWARD_REASON_MOBILE_BUSY */ - 1: "67", - /* CALL_FORWARD_REASON_NO_REPLY */ - 2: "61", - /* CALL_FORWARD_REASON_NOT_REACHABLE */ - 3: "62", - /* CALL_FORWARD_REASON_ALL_CALL_FORWARDING */ - 4: "002", - /* CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING */ - 5: "004" -}; - -// Please see TS 22.030 Annex C -const SERVICE_CLASS_TO_MMI = { - /* ICC_SERVICE_CLASS_VOICE */ - 1: "11" -}; - -function testSetCallForwarding(aData) { - // Registration: **SC*SIA*SIB*SIC# - let MMI_CODE = "**" + CF_REASON_TO_MMI[aData.reason] + "*" + aData.number + - "*" + SERVICE_CLASS_TO_MMI[aData.serviceClass] + - "*" + aData.timeSeconds + "#"; - log("Test " + MMI_CODE); - - let promises = []; - // Check cfstatechange event. - promises.push(waitForManagerEvent("cfstatechange").then(function(aEvent) { - is(aEvent.action, MozMobileConnection.CALL_FORWARD_ACTION_REGISTRATION, - "check action"); - is(aEvent.reason, aData.reason, "check reason"); - is(aEvent.number, aData.number, "check number"); - is(aEvent.timeSeconds, aData.timeSeconds, "check timeSeconds"); - is(aEvent.serviceClass, aData.serviceClass, "check serviceClass"); - })); - // Check DOMRequest's result. - promises.push(sendMMI(MMI_CODE) - .then(function resolve(aResult) { - is(aResult.serviceCode, "scCallForwarding", "Check service code"); - is(aResult.statusMessage, "smServiceRegistered", "Check status message"); - is(aResult.additionalInformation, undefined, "Check additional information"); - }, function reject(aError) { - ok(false, "got '" + aError.name + "' error"); - })); - - return Promise.all(promises); -} - -function testGetCallForwarding(aExpectedData) { - // Interrogation: *#SC# - let MMI_CODE = "*#" + CF_REASON_TO_MMI[aExpectedData.reason] + "#"; - log("Test " + MMI_CODE); - - return sendMMI(MMI_CODE) - .then(function resolve(aResult) { - is(aResult.serviceCode, "scCallForwarding", "Check service code"); - is(aResult.statusMessage, "smServiceInterrogated", "Check status message"); - is(Array.isArray(aResult.additionalInformation), true, - "additionalInformation should be an array"); - - for (let i = 0; i < aResult.additionalInformation.length; i++) { - let result = aResult.additionalInformation[i]; - - // Only need to check the result containing the serviceClass that we are - // interested in. - if (!(result.serviceClass & aExpectedData.serviceClass)) { - continue; - } - - is(result.active, true, "check active"); - is(result.reason, aExpectedData.reason, "check reason"); - is(result.number, aExpectedData.number, "check number"); - is(result.timeSeconds, aExpectedData.timeSeconds, "check timeSeconds"); - } - }, function reject(aError) { - ok(false, MMI_CODE + " got error: " + aError.name); - }); -} - -function clearAllCallForwardingSettings() { - log("Clear all call forwarding settings"); - - let promise = Promise.resolve(); - for (let reason = MozMobileConnection.CALL_FORWARD_REASON_UNCONDITIONAL; - reason <= MozMobileConnection.CALL_FORWARD_REASON_ALL_CONDITIONAL_CALL_FORWARDING; - reason++) { - let options = { - reason: reason, - action: MozMobileConnection.CALL_FORWARD_ACTION_ERASURE - }; - // Emulator doesn't support CALL_FORWARD_REASON_ALL_* yet, we catch the - // reject here in order to avoid impact the test result. - promise = - promise.then(() => setCallForwardingOption(options).then(null, () => {})); - } - return promise; -} - -// Start tests -startTestCommon(function() { - let promise = Promise.resolve(); - for (let i = 0; i < TEST_DATA.length; i++) { - let data = TEST_DATA[i]; - promise = promise.then(() => testSetCallForwarding(data)) - .then(() => testGetCallForwarding(data)); - } - // reset call forwarding settings. - return promise.then(null, () => { ok(false, "promise reject during test"); }) - .then(() => clearAllCallForwardingSettings()); -}); diff --git a/dom/mobileconnection/tests/marionette/test_mobile_mmi_change_pin.js b/dom/mobileconnection/tests/marionette/test_mobile_mmi_change_pin.js deleted file mode 100644 index 13fe3e19e158..000000000000 --- a/dom/mobileconnection/tests/marionette/test_mobile_mmi_change_pin.js +++ /dev/null @@ -1,111 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 60000; -MARIONETTE_HEAD_JS = "head.js"; - -// PIN is hardcoded as "0000" by default. -// See it here {B2G_HOME}/external/qemu/telephony/sim_card.c, -// in asimcard_create(). -const TEST_DATA = [ - // Test passing no pin. - { - pin: "", - newPin: "0000", - newPinAgain: "1111", - expectedError: { - name: "emMmiError", - additionalInformation: null - } - }, - // Test passing no newPin. - { - pin: "0000", - newPin: "", - newPinAgain: "", - expectedError: { - name: "emMmiError", - additionalInformation: null - } - }, - // Test passing mismatched newPin. - { - pin: "0000", - newPin: "0000", - newPinAgain: "1111", - expectedError: { - name: "emMmiErrorMismatchPin", - additionalInformation: null - } - }, - // Test passing invalid pin (< 4 digit). - { - pin: "000", - newPin: "0000", - newPinAgain: "0000", - expectedError: { - name: "emMmiErrorInvalidPin", - additionalInformation: null - } - }, - // Test passing invalid newPin (> 8 digit). - { - pin: "0000", - newPin: "000000000", - newPinAgain: "000000000", - expectedError: { - name: "emMmiErrorInvalidPin", - additionalInformation: null - } - }, - // Test passing incorrect pin. - { - pin: "1234", - newPin: "0000", - newPinAgain: "0000", - expectedError: { - name: "emMmiErrorBadPin", - // The default pin retries is 3, failed once becomes to 2 - additionalInformation: 2 - } - }, - // Test changing pin successfully (Reset the retries). - { - pin: "0000", - newPin: "0000", - newPinAgain: "0000" - } -]; - -function testChangePin(aPin, aNewPin, aNewPinAgain, aExpectedError) { - let MMI_CODE = "**04*" + aPin + "*" + aNewPin + "*" + aNewPinAgain + "#"; - log("Test " + MMI_CODE); - - return sendMMI(MMI_CODE) - .then(function resolve(aResult) { - ok(!aExpectedError, MMI_CODE + " success"); - is(aResult.serviceCode, "scPin", "Check service code"); - is(aResult.statusMessage, "smPinChanged", "Check status message"); - is(aResult.additionalInformation, undefined, "Check additional information"); - }, function reject(aError) { - ok(aExpectedError, MMI_CODE + " fail"); - is(aError.name, aExpectedError.name, "Check name"); - is(aError.message, "", "Check message"); - is(aError.serviceCode, "scPin", "Check service code"); - is(aError.additionalInformation, aExpectedError.additionalInformation, - "Chech additional information"); - }); -} - -// Start test -startTestCommon(function() { - let promise = Promise.resolve(); - for (let i = 0; i < TEST_DATA.length; i++) { - let data = TEST_DATA[i]; - promise = promise.then(() => testChangePin(data.pin, - data.newPin, - data.newPinAgain, - data.expectedError)); - } - return promise; -}); diff --git a/dom/mobileconnection/tests/mochitest/test_mobileconnection_permission.html b/dom/mobileconnection/tests/mochitest/test_mobileconnection_permission.html index 89b055982a86..ba69674b65f7 100644 --- a/dom/mobileconnection/tests/mochitest/test_mobileconnection_permission.html +++ b/dom/mobileconnection/tests/mochitest/test_mobileconnection_permission.html @@ -49,8 +49,6 @@ if (!SpecialPowers.hasPermission("mobileconnection", document)) { is("getRoamingPreference" in mobileConnection, true, "getRoamingPreference"); is("setVoicePrivacyMode" in mobileConnection, true, "setVoicePrivacyMode"); is("getVoicePrivacyMode" in mobileConnection, true, "getVoicePrivacyMode"); - is("sendMMI" in mobileConnection, true, "sendMMI"); - is("cancelMMI" in mobileConnection, true, "cancelMMI"); is("setCallForwardingOption" in mobileConnection, true, "setCallForwardingOption"); is("getCallForwardingOption" in mobileConnection, true, "getCallForwardingOption"); is("setCallBarringOption" in mobileConnection, true, "setCallBarringOption"); diff --git a/dom/mobileconnection/tests/mochitest/test_mobilenetwork_permission.html b/dom/mobileconnection/tests/mochitest/test_mobilenetwork_permission.html index 0e8b94be6009..386d1ce93f8c 100644 --- a/dom/mobileconnection/tests/mochitest/test_mobilenetwork_permission.html +++ b/dom/mobileconnection/tests/mochitest/test_mobilenetwork_permission.html @@ -49,8 +49,6 @@ if (!SpecialPowers.hasPermission("mobilenetwork", document)) { is("getRoamingPreference" in mobileConnection, false, "getRoamingPreference"); is("setVoicePrivacyMode" in mobileConnection, false, "setVoicePrivacyMode"); is("getVoicePrivacyMode" in mobileConnection, false, "getVoicePrivacyMode"); - is("sendMMI" in mobileConnection, false, "sendMMI"); - is("cancelMMI" in mobileConnection, false, "cancelMMI"); is("setCallForwardingOption" in mobileConnection, false, "setCallForwardingOption"); is("getCallForwardingOption" in mobileConnection, false, "getCallForwardingOption"); is("setCallBarringOption" in mobileConnection, false, "setCallBarringOption"); diff --git a/dom/settings/tests/file_loadserver.js b/dom/settings/tests/file_loadserver.js new file mode 100644 index 000000000000..64c2104a92ce --- /dev/null +++ b/dom/settings/tests/file_loadserver.js @@ -0,0 +1,17 @@ +let Ci = Components.interfaces; +let Cc = Components.classes; +let Cu = Components.utils; + +// Stolen from SpecialPowers, since at this point we don't know we're in a test. +let isMainProcess = function() { + try { + return Cc["@mozilla.org/xre/app-info;1"]. + getService(Ci.nsIXULRuntime). + processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; + } catch (e) { } + return true; +}; + +if (isMainProcess()) { + Components.utils.import("resource://gre/modules/SettingsRequestManager.jsm"); +} diff --git a/dom/settings/tests/mochitest.ini b/dom/settings/tests/mochitest.ini index 90f21987ece6..a5740d2062c5 100644 --- a/dom/settings/tests/mochitest.ini +++ b/dom/settings/tests/mochitest.ini @@ -1,5 +1,7 @@ [DEFAULT] -skip-if = (toolkit == 'gonk' && debug) || e10s #debug-only failure, bug 932878 +skip-if = (toolkit == 'gonk' && debug) #debug-only failure, bug 932878 +support-files = + file_loadserver.js [test_settings_basics.html] [test_settings_permissions.html] diff --git a/dom/settings/tests/test_settings_basics.html b/dom/settings/tests/test_settings_basics.html index 22a35e369a8f..d9fce696447f 100644 --- a/dom/settings/tests/test_settings_basics.html +++ b/dom/settings/tests/test_settings_basics.html @@ -21,10 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={678695} "use strict"; -if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm"); -} - +var url = SimpleTest.getTestFileURL("file_loadserver.js"); +var script = SpecialPowers.loadChromeScript(url); SpecialPowers.addPermission("settings-api-read", true, document); SpecialPowers.addPermission("settings-api-write", true, document); SpecialPowers.addPermission("settings-read", true, document); @@ -99,12 +97,10 @@ function check(o1, o2) { var req, req2, req3, req4, req5, req6; var index = 0; -var mozSettings = navigator.mozSettings; - var steps = [ function () { ok(true, "Deleting database"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); @@ -113,14 +109,14 @@ var steps = [ }, function () { ok(true, "Setting wifi"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wifi); req.onsuccess = function () { ok(true, "set done"); } req.onerror = onFailure; - var lock2 = mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); req2 = lock2.get("net3g.apn"); req2.onsuccess = function () { is(Object.keys(req2.result).length, 1, "length 1"); @@ -132,7 +128,7 @@ var steps = [ }, function () { ok(true, "Change wifi1"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wifi2); req.onsuccess = function () { ok(true, "Set Done"); @@ -150,7 +146,7 @@ var steps = [ }, function () { ok(true, "Set Combination"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req3 = lock.set(combination); req3.onsuccess = function () { ok(true, "set done"); @@ -164,7 +160,7 @@ var steps = [ req3.onerror = onFailure; }, function() { - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req4 = lock.get("net3g.apn2"); req4.onsuccess = function() { ok(true, "Done"); @@ -175,7 +171,7 @@ var steps = [ }, function() { ok(true, "Get unknown key"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("abc.def"); req.onsuccess = function() { is(req.result["abc.def"], undefined, "no result"); @@ -186,7 +182,7 @@ var steps = [ function() { ok(true, "adding onsettingchange"); navigator.mozSettings.onsettingchange = onsettingschangeWithNext; - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req2 = lock.get("screen.brightness"); req2.onsuccess = function() { ok(true, "end adding onsettingchange"); @@ -196,7 +192,7 @@ var steps = [ }, function() { ok(true, "Test onsettingchange"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(screenBright); req.onsuccess = function () { ok(true, "set done, observer has to call next"); @@ -205,7 +201,7 @@ var steps = [ }, function() { ok(true, "delete onsettingschange"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); navigator.mozSettings.onsettingchange = null; req = lock.set(screenBright); req.onsuccess = function () { @@ -216,7 +212,7 @@ var steps = [ }, function () { ok(true, "Waiting for all set callbacks"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("screen.brightness"); req.onsuccess = function() { ok(true, "Done"); @@ -230,7 +226,7 @@ var steps = [ navigator.mozSettings.addObserver("screen.brightness", observer1); navigator.mozSettings.addObserver("screen.brightness", observer2); navigator.mozSettings.addObserver("screen.brightness", observerWithNext); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req2 = lock.get("screen.brightness"); req2.onsuccess = function() { ok(true, "set observeSetting done!"); @@ -240,7 +236,7 @@ var steps = [ }, function() { ok(true, "test observers"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(screenBright); req.onsuccess = function () { ok(true, "set done"); @@ -249,7 +245,7 @@ var steps = [ }, function() { ok(true, "removing Event Listener"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(screenBright); req.onsuccess = function () { ok(true, "set done"); @@ -260,7 +256,7 @@ var steps = [ }, function() { ok(true, "test Event Listener"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(screenBright); req.onsuccess = function () { ok(true, "set done"); @@ -269,7 +265,7 @@ var steps = [ }, function() { ok(true, "removing Event Listener"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); navigator.mozSettings.removeObserver("screen.brightness", observerWithNext); req = lock.set(screenBright); req.onsuccess = function () { @@ -282,7 +278,7 @@ var steps = [ }, function() { ok(true, "removing Event Listener"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("screen.brightness"); req.onsuccess = function () { ok(true, "get done"); @@ -292,7 +288,7 @@ var steps = [ }, function () { ok(true, "Nested test"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("screen.brightness"); req.onsuccess = function () { req3 = lock.set({"screen.brightness": req.result["screen.brightness"] + 1}) @@ -313,7 +309,7 @@ var steps = [ } req2.onerror = onFailure; - var lock2 = mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); req5 = lock2.get("screen.brightness"); req5.onsuccess = function () { is(req5.result["screen.brightness"], 1.7, "same Value"); @@ -323,7 +319,7 @@ var steps = [ }, function () { ok(true, "Deleting database"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); @@ -331,7 +327,7 @@ var steps = [ }; }, function () { - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req2 = lock.set(wifi); req2.onsuccess = function () { ok(true, "set done"); @@ -339,7 +335,7 @@ var steps = [ req2.onerror = onFailure; ok(true, "Get all settings"); - var lock2 = mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); req3 = lock2.get("*"); req3.onsuccess = function () { is(Object.keys(req3.result).length, 1, "length 1"); @@ -360,7 +356,7 @@ var steps = [ }, function () { ok(true, "Change wifi1"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wifi2); req.onsuccess = function () { ok(true, "Set Done"); @@ -379,8 +375,8 @@ var steps = [ }, function () { ok(true, "Test locking"); - var lock = mozSettings.createLock(); - var lock2 = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); req = lock.set(wifiEnabled); req.onsuccess = function () { ok(true, "Test Locking Done"); @@ -396,7 +392,7 @@ var steps = [ }, function () { ok(true, "Test locking result"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("wifi.enabled"); req.onsuccess = function() { check(req.result, wifiDisabled); @@ -409,7 +405,7 @@ var steps = [ ok(true, "Test locking heavy"); for (var i=0; i<30; i++) { // only new locks! - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); var obj = {}; obj["wifi.enabled" + i] = true; req = lock.set( obj ); @@ -419,7 +415,7 @@ var steps = [ req.onerror = onFailure; }; { - var lock2 = mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); req2 = lock2.get("*"); req2.onsuccess = function () { is(Object.keys(req2.result).length, 32, "length 12"); @@ -428,7 +424,7 @@ var steps = [ }; req2.onerror = onFailure; } - var lock2 = mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); var obj = {}; obj["wifi.enabled" + 30] = true; req3 = lock2.set( obj ); @@ -437,7 +433,7 @@ var steps = [ }; req3.onerror = onFailure; - var lock3 = mozSettings.createLock(); + var lock3 = navigator.mozSettings.createLock(); // with one lock for (var i = 0; i < 30; i++) { req4 = lock3.get("wifi.enabled" + i); @@ -451,7 +447,7 @@ var steps = [ } ok(true, "start next2!"); - var lock4 = mozSettings.createLock(); + var lock4 = navigator.mozSettings.createLock(); for (var i=0; i<30; i++) { var obj = {}; obj["wifi.enabled" + i] = false; @@ -461,7 +457,7 @@ var steps = [ }; req4.onerror = onFailure; } - var lock5 = mozSettings.createLock(); + var lock5 = navigator.mozSettings.createLock(); for (var i=0; i<30; i++) { req5 = lock5.get("wifi.enabled" + i); var testObj = {}; @@ -473,7 +469,7 @@ var steps = [ req5.onerror = onFailure; } - var lock6 = mozSettings.createLock(); + var lock6 = navigator.mozSettings.createLock(); req6 = lock6.clear(); req6.onsuccess = function () { ok(true, "Deleted the database"); @@ -483,8 +479,8 @@ var steps = [ }, function () { ok(true, "reverse Test locking"); - var lock2 = mozSettings.createLock(); - var lock = mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wifiEnabled); req.onsuccess = function () { @@ -501,7 +497,7 @@ var steps = [ }, function () { ok(true, "Test locking result"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("wifi.enabled"); req.onsuccess = function() { @@ -510,14 +506,14 @@ var steps = [ } req.onerror = onFailure; - var lock2 = mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); req2 = lock2.clear(); req2.onsuccess = function () { ok(true, "Deleted the database"); }; req2.onerror = onFailure; - var lock3 = mozSettings.createLock(); + var lock3 = navigator.mozSettings.createLock(); req3 = lock3.set(wifi); req3.onsuccess = function () { ok(true, "set done"); @@ -528,7 +524,7 @@ var steps = [ }, function () { ok(true, "Get all settings"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("*"); req.onsuccess = function () { is(Object.keys(req.result).length, 1, "length 1"); @@ -540,7 +536,7 @@ var steps = [ }, function () { ok(true, "Get net3g.apn"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("net3g.apn"); req.onsuccess = function () { is(Object.keys(req.result).length, 1, "length 1"); @@ -552,7 +548,7 @@ var steps = [ }, function () { ok(true, "Change wifi2"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wifi2); req.onsuccess = function () { ok(true, "Set Done"); @@ -562,7 +558,7 @@ var steps = [ }, function () { ok(true, "Get net3g.apn"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("net3g.apn"); req.onsuccess = function () { is(Object.keys(req.result).length, 1, "length 1"); @@ -574,7 +570,7 @@ var steps = [ }, function () { ok(true, "Add wifi.enabled"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wifiEnabled); req.onsuccess = function () { ok(true, "Set Done"); @@ -584,7 +580,7 @@ var steps = [ }, function () { ok(true, "Get Wifi Enabled"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("wifi.enabled"); req.onsuccess = function () { is(Object.keys(req.result).length, 1, "length 1"); @@ -596,7 +592,7 @@ var steps = [ }, function () { ok(true, "Get all"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("*"); req.onsuccess = function () { is(Object.keys(req.result).length, 2, "length 2"); @@ -609,7 +605,7 @@ var steps = [ }, function () { ok(true, "Add wifiNetworks"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wifiNetworks0); req.onsuccess = function () { ok(true, "Set Done"); @@ -625,7 +621,7 @@ var steps = [ }, function () { ok(true, "Get Wifi Networks"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("wifi.networks[0]"); req.onsuccess = function () { is(Object.keys(req.result).length, 1, "length 1"); @@ -637,12 +633,12 @@ var steps = [ }, function() { ok(true, "Clear DB, multiple locks"); - var lock4 = mozSettings.createLock(); - var lock3 = mozSettings.createLock(); - var lock2 = mozSettings.createLock(); - var lock = mozSettings.createLock(); - var lock6 = mozSettings.createLock(); - var lock7 = mozSettings.createLock(); + var lock4 = navigator.mozSettings.createLock(); + var lock3 = navigator.mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); + var lock6 = navigator.mozSettings.createLock(); + var lock7 = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); @@ -652,7 +648,7 @@ var steps = [ }, function () { ok(true, "Add wifiNetworks"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wifiNetworks0); req.onsuccess = function () { ok(true, "Set Done"); @@ -662,7 +658,7 @@ var steps = [ }, function () { ok(true, "Test set after lock closed"); - var lockx = mozSettings.createLock(); + var lockx = navigator.mozSettings.createLock(); var cb = function() { var reqx = null; try { @@ -678,7 +674,7 @@ var steps = [ }, function() { ok(true, "Clear DB"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); @@ -688,7 +684,7 @@ var steps = [ }, function() { ok(true, "Set with multiple arguments"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(combination); req.onsuccess = function () { ok(true, "Set Done"); @@ -698,7 +694,7 @@ var steps = [ }, function() { ok(true, "request argument from multiple set"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("screen.brightness"); req.onsuccess = function () { check(req.result["screen.brightness"], 0.7, "get done"); @@ -708,7 +704,7 @@ var steps = [ }, function() { ok(true, "Test closed attribute on a valid lock"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); is(lock.closed, false, "closed attribute is false on creation"); req = lock.get("screen.brightness"); req.onsuccess = function () { @@ -719,7 +715,7 @@ var steps = [ }, function () { ok(true, "Test closed attribute on invalid lock"); - var lockx = mozSettings.createLock(); + var lockx = navigator.mozSettings.createLock(); var cb = function() { var reqx = null; try { @@ -735,7 +731,7 @@ var steps = [ }, function() { ok(true, "Clear DB"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); @@ -745,7 +741,7 @@ var steps = [ }, function() { ok(true, "Set object value"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set({"setting-obj": {foo: {bar: 23}}}); req.onsuccess = function() { req2 = lock.get("setting-obj"); @@ -761,7 +757,7 @@ var steps = [ }, function() { ok(true, "Clear DB"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); @@ -771,7 +767,7 @@ var steps = [ }, function () { ok(true, "Call success callback when transaction commits"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); lock.onsettingstransactionsuccess = function () { next(); }; @@ -789,7 +785,7 @@ var steps = [ }, function() { ok(true, "Clear DB"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); diff --git a/dom/settings/tests/test_settings_blobs.html b/dom/settings/tests/test_settings_blobs.html index 35986fa6fcbe..69ac21fb6d88 100644 --- a/dom/settings/tests/test_settings_blobs.html +++ b/dom/settings/tests/test_settings_blobs.html @@ -21,10 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=821630 "use strict"; -if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm"); -} - +var url = SimpleTest.getTestFileURL("file_loadserver.js"); +var script = SpecialPowers.loadChromeScript(url); SpecialPowers.addPermission("settings-read", true, document); SpecialPowers.addPermission("settings-write", true, document); SpecialPowers.addPermission("settings-api-read", true, document); @@ -45,7 +43,6 @@ function onFailure() { } } -let mozSettings = window.navigator.mozSettings; let req; let storedBlob = new Blob(['12345'], {"type": "text/plain"}); @@ -61,7 +58,7 @@ function checkBlob(blob) { let steps = [ function() { - let lock = mozSettings.createLock(); + let lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = next; req.onerror = onFailure("Deleting database"); @@ -69,19 +66,19 @@ let steps = [ function() { function obs(e) { checkBlob(e.settingValue); - mozSettings.removeObserver("test1", obs); + navigator.mozSettings.removeObserver("test1", obs); next(); } - mozSettings.addObserver("test1", obs); + navigator.mozSettings.addObserver("test1", obs); next(); }, function() { // next is called by the observer above - let req = mozSettings.createLock().set({"test1": storedBlob}); + let req = navigator.mozSettings.createLock().set({"test1": storedBlob}); req.onerror = onFailure("Saving blob"); }, function() { - let req = mozSettings.createLock().get("test1"); + let req = navigator.mozSettings.createLock().get("test1"); req.onsuccess = function(event) { checkBlob(event.target.result["test1"]); next(); @@ -89,12 +86,12 @@ let steps = [ req.onerror = onFailure("Getting blob"); }, function() { - let req = mozSettings.createLock().set({"test2": [1, 2, storedBlob, 4]}); + let req = navigator.mozSettings.createLock().set({"test2": [1, 2, storedBlob, 4]}); req.onsuccess = next; req.onerror = onFailure("Saving array"); }, function() { - let req = mozSettings.createLock().get("test2"); + let req = navigator.mozSettings.createLock().get("test2"); req.onsuccess = function(event) { let val = event.target.result["test2"]; ok(Array.isArray(val), "Result is an array"); @@ -105,12 +102,12 @@ let steps = [ req.onerror = onFailure("Getting array"); }, function() { - let req = mozSettings.createLock().set({"test3": {foo: "bar", baz: {number: 1, arr: [storedBlob]}}}); + let req = navigator.mozSettings.createLock().set({"test3": {foo: "bar", baz: {number: 1, arr: [storedBlob]}}}); req.onsuccess = next(); req.onerror = onFailure("Saving object"); }, function() { - let req = mozSettings.createLock().get("test3"); + let req = navigator.mozSettings.createLock().get("test3"); req.onsuccess = function(event) { let val = event.target.result["test3"]; ok(typeof(val) == "object", "Result is an object"); @@ -125,7 +122,7 @@ let steps = [ req.onerror = onFailure("Getting object"); }, function() { - let req = mozSettings.createLock().clear(); + let req = navigator.mozSettings.createLock().clear(); req.onsuccess = function() { next(); }; diff --git a/dom/settings/tests/test_settings_data_uris.html b/dom/settings/tests/test_settings_data_uris.html index 9b4ef51a5df1..9650dff6d4d9 100644 --- a/dom/settings/tests/test_settings_data_uris.html +++ b/dom/settings/tests/test_settings_data_uris.html @@ -21,10 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=806374 "use strict"; -if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm"); -} - +var url = SimpleTest.getTestFileURL("file_loadserver.js"); +var script = SpecialPowers.loadChromeScript(url); SpecialPowers.addPermission("settings-read", true, document); SpecialPowers.addPermission("settings-write", true, document); SpecialPowers.addPermission("settings-api-read", true, document); @@ -45,7 +43,6 @@ function onFailure() { } } -let mozSettings = window.navigator.mozSettings; let req; // A simple data URI that will be converted to a blob. @@ -62,7 +59,7 @@ function checkBlob(blob) { let steps = [ function() { - let lock = mozSettings.createLock(); + let lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = next; req.onerror = onFailure("Deleting database"); @@ -70,19 +67,19 @@ let steps = [ function() { function obs(e) { checkBlob(e.settingValue); - mozSettings.removeObserver("test1", obs); + navigator.mozSettings.removeObserver("test1", obs); next(); } - mozSettings.addObserver("test1", obs); + navigator.mozSettings.addObserver("test1", obs); next(); }, function() { // next is called by the observer above - let req = mozSettings.createLock().set({"test1": dataURI}); + let req = navigator.mozSettings.createLock().set({"test1": dataURI}); req.onerror = onFailure("Saving blob"); }, function() { - let req = mozSettings.createLock().get("test1"); + let req = navigator.mozSettings.createLock().get("test1"); req.onsuccess = function(event) { checkBlob(event.target.result["test1"]); next(); @@ -90,12 +87,12 @@ let steps = [ req.onerror = onFailure("Getting blob"); }, function() { - let req = mozSettings.createLock().set({"test2": [1, 2, dataURI, 4]}); + let req = navigator.mozSettings.createLock().set({"test2": [1, 2, dataURI, 4]}); req.onsuccess = next; req.onerror = onFailure("Saving array"); }, function() { - let req = mozSettings.createLock().get("test2"); + let req = navigator.mozSettings.createLock().get("test2"); req.onsuccess = function(event) { let val = event.target.result["test2"]; ok(Array.isArray(val), "Result is an array"); @@ -106,12 +103,12 @@ let steps = [ req.onerror = onFailure("Getting array"); }, function() { - let req = mozSettings.createLock().set({"test3": {foo: "bar", baz: {number: 1, arr: [dataURI]}}}); + let req = navigator.mozSettings.createLock().set({"test3": {foo: "bar", baz: {number: 1, arr: [dataURI]}}}); req.onsuccess = next(); req.onerror = onFailure("Saving object"); }, function() { - let req = mozSettings.createLock().get("test3"); + let req = navigator.mozSettings.createLock().get("test3"); req.onsuccess = function(event) { let val = event.target.result["test3"]; ok(typeof(val) == "object", "Result is an object"); @@ -126,7 +123,7 @@ let steps = [ req.onerror = onFailure("Getting object"); }, function() { - let req = mozSettings.createLock().clear(); + let req = navigator.mozSettings.createLock().clear(); req.onsuccess = function() { next(); }; diff --git a/dom/settings/tests/test_settings_onsettingchange.html b/dom/settings/tests/test_settings_onsettingchange.html index 311e4f7142bb..da00103d7c8d 100644 --- a/dom/settings/tests/test_settings_onsettingchange.html +++ b/dom/settings/tests/test_settings_onsettingchange.html @@ -21,10 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=678695 "use strict"; -if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm"); -} - +var url = SimpleTest.getTestFileURL("file_loadserver.js"); +var script = SpecialPowers.loadChromeScript(url); SpecialPowers.addPermission("settings-write", true, document); SpecialPowers.addPermission("settings-read", true, document); SpecialPowers.addPermission("settings-api-read", true, document); @@ -83,12 +81,10 @@ function onComplexSettingschangeWithNext(event) { var req, req2; var index = 0; -var mozSettings = window.navigator.mozSettings; - var steps = [ function () { ok(true, "Deleting database"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); @@ -97,7 +93,7 @@ var steps = [ req.onerror = onFailure; }, function () { - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req2 = lock.set(screenBright); req2.onsuccess = function () { ok(true, "set done"); @@ -108,7 +104,7 @@ var steps = [ }, function() { ok(true, "testing"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req2 = lock.set(screenBright); req2.onsuccess = function() { ok(true, "end adding onsettingchange"); @@ -117,7 +113,7 @@ var steps = [ }, function() { ok(true, "test observers"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("screen.brightness"); req.onsuccess = function () { ok(true, "get done"); @@ -131,7 +127,7 @@ var steps = [ navigator.mozSettings.addObserver("screen.brightness", observer1); navigator.mozSettings.addObserver("screen.brightness", observer2); navigator.mozSettings.addObserver("screen.brightness", observerOnlyCalledOnce); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req2 = lock.get("screen.brightness"); req2.onsuccess = function() { ok(true, "set observeSetting done!"); @@ -141,7 +137,7 @@ var steps = [ }, function() { ok(true, "test observers"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(screenBright); req.onsuccess = function () { ok(true, "set1 done"); @@ -150,7 +146,7 @@ var steps = [ }, function() { ok(true, "test observers"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("screen.brightness"); navigator.mozSettings.removeObserver("screen.brightness", observerOnlyCalledOnce); req.onsuccess = function () { @@ -160,7 +156,7 @@ var steps = [ }, function() { ok(true, "removing Event Listener"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(screenBright); req.onsuccess = function () { ok(true, "set2 done"); @@ -173,7 +169,7 @@ var steps = [ function() { ok(true, "delete onsettingschange"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); navigator.mozSettings.onsettingchange = null; req = lock.set(screenBright); req.onsuccess = function () { @@ -184,7 +180,7 @@ var steps = [ }, function () { ok(true, "Waiting for all set callbacks"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("screen.brightness"); req.onsuccess = function() { ok(true, "Done"); @@ -198,7 +194,7 @@ var steps = [ navigator.mozSettings.addObserver("screen.brightness", observer1); navigator.mozSettings.addObserver("screen.brightness", observer2); navigator.mozSettings.addObserver("screen.brightness", observerWithNext); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req2 = lock.get("screen.brightness"); req2.onsuccess = function() { ok(true, "set observeSetting done!"); @@ -208,7 +204,7 @@ var steps = [ }, function() { ok(true, "test observers"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(screenBright); req.onsuccess = function () { ok(true, "set1 done"); @@ -217,7 +213,7 @@ var steps = [ }, function() { ok(true, "removing Event Listener"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(screenBright); req.onsuccess = function () { ok(true, "set2 done"); @@ -228,7 +224,7 @@ var steps = [ }, function() { ok(true, "test Event Listener"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(screenBright); req.onsuccess = function () { ok(true, "set3 done"); @@ -237,7 +233,7 @@ var steps = [ }, function() { ok(true, "removing Event Listener"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); navigator.mozSettings.removeObserver("screen.brightness", observerWithNext); req = lock.set(screenBright); req.onsuccess = function () { @@ -250,7 +246,7 @@ var steps = [ }, function() { ok(true, "removing Event Listener"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("screen.brightness"); req.onsuccess = function () { ok(true, "get5 done"); @@ -260,7 +256,7 @@ var steps = [ }, function() { ok(true, "Clear DB"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); @@ -270,7 +266,7 @@ var steps = [ }, function () { ok(true, "Deleting database"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.clear(); req.onsuccess = function () { ok(true, "Deleted the database"); @@ -279,7 +275,7 @@ var steps = [ req.onerror = onFailure; }, function () { - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); navigator.mozSettings.onsettingchange = onComplexSettingschangeWithNext; req2 = navigator.mozSettings.createLock().set({'test.key': cset}); req2.onsuccess = function () { diff --git a/dom/settings/tests/test_settings_permissions.html b/dom/settings/tests/test_settings_permissions.html index 69114f886bb6..26b5d295fcf3 100644 --- a/dom/settings/tests/test_settings_permissions.html +++ b/dom/settings/tests/test_settings_permissions.html @@ -21,10 +21,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id={678695} "use strict"; -if (SpecialPowers.isMainProcess()) { - SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm"); -} - +var url = SimpleTest.getTestFileURL("file_loadserver.js"); +var script = SpecialPowers.loadChromeScript(url); SpecialPowers.removePermission("settings-read", document); SpecialPowers.removePermission("settings-write", document); SpecialPowers.addPermission("settings-api-read", true, document); @@ -80,21 +78,19 @@ function check(o1, o2) { var req, req2, req3, req4, req5, req6; var index = 0; -var mozSettings = navigator.mozSettings; - var steps = [ // Can't delete database here since that requires permissions we don't want // to give the page. function () { ok(true, "Setting wallpaper"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wallpaper); req.onsuccess = function () { ok(true, "set done"); } req.onerror = onFailure; - var lock2 = mozSettings.createLock(); + var lock2 = navigator.mozSettings.createLock(); req2 = lock2.get("wallpaper.image"); req2.onsuccess = function () { is(Object.keys(req2.result).length, 1, "length 1"); @@ -106,7 +102,7 @@ var steps = [ }, function () { ok(true, "Get Wifi"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.get("wifi.enabled"); req.onerror = function () { ok(true, "get failed (expected)"); @@ -116,7 +112,7 @@ var steps = [ }, function () { ok(true, "Set Wifi"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(wifi); req.onerror = function () { ok(true, "set failed (expected)"); @@ -126,7 +122,7 @@ var steps = [ }, function () { ok(true, "Set combination (1 valid 1 not valid)"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(combination); req.onerror = function () { ok(true, "set failed (expected)"); @@ -136,7 +132,7 @@ var steps = [ }, function () { ok(true, "All requests on a failed lock should fail"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); lock.onsettingstransactionfailure = function (evt) { ok(evt.error == "Lock failed a permissions check, all requests now failing.", "transaction failure on permissions error message correct."); ok(true, "transaction failed (expected) "); @@ -157,7 +153,7 @@ var steps = [ }, function () { ok(true, "Set combination (1 valid 1 not valid)"); - var lock = mozSettings.createLock(); + var lock = navigator.mozSettings.createLock(); req = lock.set(combination); req.onerror = function () { ok(true, "set failed (expected)"); diff --git a/dom/system/gonk/RILSystemMessengerHelper.js b/dom/system/gonk/RILSystemMessengerHelper.js index 7411fb6e8c87..89b4006663b6 100644 --- a/dom/system/gonk/RILSystemMessengerHelper.js +++ b/dom/system/gonk/RILSystemMessengerHelper.js @@ -73,6 +73,10 @@ RILSystemMessengerHelper.prototype = { aDuration, aOutgoing, aHangUpLocal); }, + notifyUssdReceived: function(aServiceId, aMessage, aSessionEnded) { + this.messenger.notifyUssdReceived(aServiceId, aMessage, aSessionEnded); + }, + /** * nsISmsMessenger API */ @@ -100,10 +104,6 @@ RILSystemMessengerHelper.prototype = { /** * nsIMobileConnectionMessenger API */ - notifyUssdReceived: function(aServiceId, aMessage, aSessionEnded) { - this.messenger.notifyUssdReceived(aServiceId, aMessage, aSessionEnded); - }, - notifyCdmaInfoRecDisplay: function(aServiceId, aDisplay) { this.messenger.notifyCdmaInfoRecDisplay(aServiceId, aDisplay); }, diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index 86227fef5bb3..2d2503eae61c 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -1993,20 +1993,20 @@ RadioInterface.prototype = { matchMvno: function(target, message) { if (DEBUG) this.debug("matchMvno: " + JSON.stringify(message)); - if (!message || !message.mvnoType || !message.mvnoData) { + if (!message || !message.mvnoData) { message.errorMsg = RIL.GECKO_ERROR_INVALID_PARAMETER; } if (!message.errorMsg) { switch (message.mvnoType) { - case "imsi": + case RIL.GECKO_CARDMVNO_TYPE_IMSI: if (!this.rilContext.imsi) { message.errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE; break; } message.result = this.isImsiMatches(message.mvnoData); break; - case "spn": + case RIL.GECKO_CARDMVNO_TYPE_SPN: let spn = this.rilContext.iccInfo && this.rilContext.iccInfo.spn; if (!spn) { message.errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE; @@ -2014,7 +2014,7 @@ RadioInterface.prototype = { } message.result = spn == message.mvnoData; break; - case "gid": + case RIL.GECKO_CARDMVNO_TYPE_GID: this.workerMessenger.send("getGID1", null, (function(response) { let gid = response.gid1; let mvnoDataLength = message.mvnoData.length; diff --git a/dom/system/gonk/ril_consts.js b/dom/system/gonk/ril_consts.js index 8eb042953bbd..5851e939bf0a 100644 --- a/dom/system/gonk/ril_consts.js +++ b/dom/system/gonk/ril_consts.js @@ -2645,6 +2645,11 @@ this.GECKO_CARDCONTACT_TYPE_ADN = 0; this.GECKO_CARDCONTACT_TYPE_FDN = 1; this.GECKO_CARDCONTACT_TYPE_SDN = 2; +// See nsIIccProvider::CARD_MVNO_TYPE_* +this.GECKO_CARDMVNO_TYPE_IMSI = 0; +this.GECKO_CARDMVNO_TYPE_SPN = 1; +this.GECKO_CARDMVNO_TYPE_GID = 2; + // See ril.h RIL_PersoSubstate this.PERSONSUBSTATE = {}; PERSONSUBSTATE[CARD_PERSOSUBSTATE_UNKNOWN] = GECKO_CARDSTATE_UNKNOWN; @@ -2999,6 +3004,10 @@ this.MMI_SC_CLIR = "31"; // MMI call waiting service code this.MMI_SC_CALL_WAITING = "43"; +// MMI service code for registration new password as defined in TS 22.030 6.5.4 +this.MMI_SC_CHANGE_PASSWORD = "03"; +this.MMI_ZZ_BARRING_SERVICE = "330"; + // MMI call barring service codes this.MMI_SC_BAOC = "33"; this.MMI_SC_BAOIC = "331"; @@ -3031,6 +3040,7 @@ this.MMI_KS_SC_PIN = "scPin"; this.MMI_KS_SC_PIN2 = "scPin2"; this.MMI_KS_SC_PUK = "scPuk"; this.MMI_KS_SC_PUK2 = "scPuk2"; +this.MMI_KS_SC_CHANGE_PASSWORD = "scChangePassword" this.MMI_KS_SC_IMEI = "scImei"; this.MMI_KS_SC_USSD = "scUssd"; this.MMI_KS_SC_CALL = "scCall"; @@ -3040,13 +3050,16 @@ this.MMI_ERROR_KS_ERROR = "emMmiError"; this.MMI_ERROR_KS_NOT_SUPPORTED = "emMmiErrorNotSupported"; this.MMI_ERROR_KS_INVALID_ACTION = "emMmiErrorInvalidAction"; this.MMI_ERROR_KS_MISMATCH_PIN = "emMmiErrorMismatchPin"; +this.MMI_ERROR_KS_MISMATCH_PASSWORD = "emMmiErrorMismatchPassword"; this.MMI_ERROR_KS_BAD_PIN = "emMmiErrorBadPin"; this.MMI_ERROR_KS_BAD_PUK = "emMmiErrorBadPuk"; this.MMI_ERROR_KS_INVALID_PIN = "emMmiErrorInvalidPin"; +this.MMI_ERROR_KS_INVALID_PASSWORD = "emMmiErrorInvalidPassword"; this.MMI_ERROR_KS_NEEDS_PUK = "emMmiErrorNeedsPuk"; this.MMI_ERROR_KS_SIM_BLOCKED = "emMmiErrorSimBlocked"; // MMI status message. +this.MMI_SM_KS_PASSWORD_CHANGED = "smPasswordChanged"; this.MMI_SM_KS_PIN_CHANGED = "smPinChanged"; this.MMI_SM_KS_PIN2_CHANGED = "smPin2Changed"; this.MMI_SM_KS_PIN_UNBLOCKED = "smPinUnblocked"; diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index a09fbe2ea630..a25ef11be526 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -2409,6 +2409,33 @@ RilObject.prototype = { return true; } + function _isValidChangePasswordRequest() { + if (mmi.procedure !== MMI_PROCEDURE_REGISTRATION && + mmi.procedure !== MMI_PROCEDURE_ACTIVATION) { + _sendMMIError(MMI_ERROR_KS_INVALID_ACTION); + return false; + } + + if (mmi.sia !== "" && mmi.sia !== MMI_ZZ_BARRING_SERVICE) { + _sendMMIError(MMI_ERROR_KS_NOT_SUPPORTED); + return false; + } + + let validPassword = si => /^[0-9]{4}$/.test(si); + if (!validPassword(mmi.sib) || !validPassword(mmi.sic) || + !validPassword(mmi.pwd)) { + _sendMMIError(MMI_ERROR_KS_INVALID_PASSWORD); + return false; + } + + if (mmi.sic != mmi.pwd) { + _sendMMIError(MMI_ERROR_KS_MISMATCH_PASSWORD); + return false; + } + + return true; + } + let _isRadioAvailable = (function() { if (this.radioState !== GECKO_RADIOSTATE_ENABLED) { _sendMMIError(GECKO_ERROR_RADIO_NOT_AVAILABLE); @@ -2556,6 +2583,17 @@ RilObject.prototype = { this.setCLIR(options); return; + // Change call barring password + case MMI_SC_CHANGE_PASSWORD: + if (!_isRadioAvailable() || !_isValidChangePasswordRequest()) { + return; + } + + options.pin = mmi.sib; + options.newPin = mmi.sic; + this.changeCallBarringPassword(options); + return; + // Call barring case MMI_SC_BAOC: case MMI_SC_BAOIC: @@ -2617,7 +2655,7 @@ RilObject.prototype = { options.ussd = mmi.fullMMI; - if (options.startNewSession && this._ussdSession) { + if (this._ussdSession) { if (DEBUG) this.context.debug("Cancel existing ussd session."); this.cachedUSSDRequest = options; this.cancelUSSD({}); @@ -5986,6 +6024,13 @@ RilObject.prototype[REQUEST_CHANGE_BARRING_PASSWORD] = if (options.rilRequestError) { options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError]; } + + if (options.rilMessageType != "sendMMI") { + this.sendChromeMessage(options); + return; + } + + options.statusMessage = MMI_SM_KS_PASSWORD_CHANGED; this.sendChromeMessage(options); }; RilObject.prototype[REQUEST_SIM_OPEN_CHANNEL] = function REQUEST_SIM_OPEN_CHANNEL(length, options) { diff --git a/dom/telephony/gonk/TelephonyService.js b/dom/telephony/gonk/TelephonyService.js index 20cfe648a1a4..ac5040fc7c76 100644 --- a/dom/telephony/gonk/TelephonyService.js +++ b/dom/telephony/gonk/TelephonyService.js @@ -566,7 +566,7 @@ TelephonyService.prototype = { return; } - this._dialMMI(aClientId, mmi, aCallback, true); + this._dialMMI(aClientId, mmi, aCallback); } } }, @@ -664,15 +664,14 @@ TelephonyService.prototype = { * @param aStartNewSession * True to start a new session for ussd request. */ - _dialMMI: function(aClientId, aMmi, aCallback, aStartNewSession) { + _dialMMI: function(aClientId, aMmi, aCallback) { let mmiServiceCode = aMmi ? this._serviceCodeToKeyString(aMmi.serviceCode) : RIL.MMI_KS_SC_USSD; aCallback.notifyDialMMI(mmiServiceCode); this._sendToRilWorker(aClientId, "sendMMI", - { mmi: aMmi, - startNewSession: aStartNewSession }, response => { + { mmi: aMmi }, response => { if (DEBUG) debug("MMI response: " + JSON.stringify(response)); if (!response.success) { @@ -907,6 +906,8 @@ TelephonyService.prototype = { return RIL.MMI_KS_SC_CALL_BARRING; case RIL.MMI_SC_CALL_WAITING: return RIL.MMI_KS_SC_CALL_WAITING; + case RIL.MMI_SC_CHANGE_PASSWORD: + return RIL.MMI_KS_SC_CHANGE_PASSWORD; default: return RIL.MMI_KS_SC_USSD; } @@ -1302,13 +1303,7 @@ TelephonyService.prototype = { aMessage + " (sessionEnded : " + aSessionEnded + ")"); } - gGonkMobileConnectionService.notifyUssdReceived(aClientId, aMessage, - aSessionEnded); - }, - - dialMMI: function(aClientId, aMmiString, aCallback) { - let mmi = this._parseMMI(aMmiString, this._hasCalls(aClientId)); - this._dialMMI(aClientId, mmi, aCallback, false); + gTelephonyMessenger.notifyUssdReceived(aClientId, aMessage, aSessionEnded); }, /** @@ -1355,7 +1350,6 @@ USSDReceivedWrapper.prototype = { let event = new aWindow.USSDReceivedEvent("ussdreceived", { serviceId: aMessage.serviceId, message: aMessage.message, - sessionEnded: aMessage.sessionEnded, session: session }); diff --git a/dom/telephony/nsIGonkTelephonyService.idl b/dom/telephony/nsIGonkTelephonyService.idl index 7dd72d481647..269f8304ed11 100644 --- a/dom/telephony/nsIGonkTelephonyService.idl +++ b/dom/telephony/nsIGonkTelephonyService.idl @@ -10,7 +10,7 @@ "@mozilla.org/telephony/gonktelephonyservice;1" %} -[scriptable, uuid(068d7bf2-1773-48ef-95f8-bd835115fed7)] +[scriptable, uuid(cbbe66d8-865b-11e4-94f1-ab441e55905b)] interface nsIGonkTelephonyService : nsITelephonyService { void notifyAudioStateChanged(in unsigned long clientId, in short state); @@ -31,7 +31,4 @@ interface nsIGonkTelephonyService : nsITelephonyService void notifyUssdReceived(in unsigned long clientId, in DOMString message, in boolean sessionEnded); - - void dialMMI(in unsigned long clientId, in AString mmiString, - in nsITelephonyDialCallback callback); }; diff --git a/dom/telephony/nsITelephonyMessenger.idl b/dom/telephony/nsITelephonyMessenger.idl index 1ce9d2440415..a7b269af4402 100644 --- a/dom/telephony/nsITelephonyMessenger.idl +++ b/dom/telephony/nsITelephonyMessenger.idl @@ -4,7 +4,7 @@ #include "nsISupports.idl" -[scriptable, uuid(998a48b2-5b54-11e4-833e-6b17c1427d49)] +[scriptable, uuid(84045b7e-84fb-11e4-a94c-5ba58d0d5932)] interface nsITelephonyMessenger : nsISupports { /** @@ -37,4 +37,18 @@ interface nsITelephonyMessenger : nsISupports in unsigned long aDuration, in boolean aOutgoing, in boolean aHangUpLocal); + + /** + * 'ussd-received' system message + * + * @param aServiceId + * The ID of Service where this info is notified from. + * @param aMessage + * USSD Message to be displayed. + * @param aSessionEnded + * True if USSD session is ended. + */ + void notifyUssdReceived(in unsigned long aServiceId, + in DOMString aMessage, + in boolean aSessionEnded); }; \ No newline at end of file diff --git a/dom/telephony/test/marionette/manifest.ini b/dom/telephony/test/marionette/manifest.ini index afba3a7744ba..370115850ae4 100644 --- a/dom/telephony/test/marionette/manifest.ini +++ b/dom/telephony/test/marionette/manifest.ini @@ -33,6 +33,7 @@ qemu = true [test_incoming_onstatechange.js] [test_mmi.js] [test_mmi_call_forwarding.js] +[test_mmi_change_barring_password.js] [test_mmi_change_pin.js] [test_mmi_unlock_puk.js] [test_multiple_hold.js] diff --git a/dom/telephony/test/marionette/test_mmi_change_barring_password.js b/dom/telephony/test/marionette/test_mmi_change_barring_password.js new file mode 100644 index 000000000000..688cca936941 --- /dev/null +++ b/dom/telephony/test/marionette/test_mmi_change_barring_password.js @@ -0,0 +1,99 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = "head.js"; + +const TEST_DATA = [ + // Test passing no password. + { + password: "", + newPassword: "0000", + newPasswordAgain: "1111", + expectedError: { + name: "emMmiErrorInvalidPassword" + } + }, + // Test passing no newPassword. + { + password: "0000", + newPassword: "", + newPasswordAgain: "", + expectedError: { + name: "emMmiErrorInvalidPassword" + } + }, + // Test passing mismatched newPassword. + { + password: "0000", + newPassword: "0000", + newPasswordAgain: "1111", + expectedError: { + name: "emMmiErrorMismatchPassword" + } + }, + // Test passing invalid password (not 4 digits). + { + password: "000", + newPassword: "0000", + newPasswordAgain: "0000", + expectedError: { + name: "emMmiErrorInvalidPassword" + } + }, + // TODO: Bug 906603 - B2G RIL: Support Change Call Barring Password on Emulator. + // Currently emulator doesn't support REQUEST_CHANGE_BARRING_PASSWORD, so we + // expect to get a 'RequestNotSupported' error here. + { + password: "0000", + newPassword: "1234", + newPasswordAgain: "1234", + expectedError: { + name: "RequestNotSupported" + } + } +]; + +let MMI_PREFIX = [ + "*03*330*", + "**03*330*", + "*03**", + "**03**", +]; + +function testChangeCallBarringPassword(aMMIPrefix, aPassword, aNewPassword, + aNewPasswordAgain, aExpectedError) { + let MMI_CODE = aMMIPrefix + aPassword + "*" + aNewPassword + "*" + aNewPasswordAgain + "#"; + log("Test " + MMI_CODE); + + return gSendMMI(MMI_CODE).then(aResult => { + is(aResult.success, !aExpectedError, "check success"); + is(aResult.serviceCode, "scChangePassword", "Check service code"); + + if (aResult.success) { + is(aResult.statusMessage, "smPasswordChanged", "Check status message"); + } else { + is(aResult.statusMessage, aExpectedError.name, "Check name"); + } + }); +} + +// Start test +startTest(function() { + let promise = Promise.resolve(); + + for (let prefix of MMI_PREFIX) { + for (let i = 0; i < TEST_DATA.length; i++) { + let data = TEST_DATA[i]; + promise = promise.then(() => testChangeCallBarringPassword(prefix, + data.password, + data.newPassword, + data.newPasswordAgain, + data.expectedError)); + } + } + + return promise + .catch(error => ok(false, "Promise reject: " + error)) + .then(finish); +}); diff --git a/dom/webidl/HTMLMenuElement.webidl b/dom/webidl/HTMLMenuElement.webidl index 5ee2e66e3790..ff81a7c80d23 100644 --- a/dom/webidl/HTMLMenuElement.webidl +++ b/dom/webidl/HTMLMenuElement.webidl @@ -40,11 +40,11 @@ partial interface HTMLMenuElement { /** * Creates a native menu builder. The builder type is dependent on menu type. - * Currently, it returns nsXULContextMenuBuilder for context menus. - * Toolbar menus are not yet supported (the method returns null). + * Currently, it returns the @mozilla.org/content/html-menu-builder;1 + * component. Toolbar menus are not yet supported (the method returns null). */ [ChromeOnly] - MenuBuilder createBuilder(); + MenuBuilder? createBuilder(); /* * Builds a menu by iterating over menu children. diff --git a/dom/webidl/MozIcc.webidl b/dom/webidl/MozIcc.webidl index f21ac6730b05..309b52249f19 100644 --- a/dom/webidl/MozIcc.webidl +++ b/dom/webidl/MozIcc.webidl @@ -82,6 +82,13 @@ enum IccContactType "sdn" // Service Dialling Number. }; +enum IccMvnoType +{ + "imsi", + "spn", + "gid" +}; + dictionary IccUnlockCardLockOptions { required IccLockType lockType; @@ -315,17 +322,13 @@ interface MozIcc : EventTarget * * @param mvnoType * Mvno type to use to compare the match data. - * Currently, we only support 'imsi'. * @param matchData * Data to be compared with ICC's field. * * @return a DOMRequest. * The request's result will be a boolean indicating the matching * result. - * - * TODO: change param mvnoType to WebIDL enum after Bug 864489 - - * B2G RIL: use ipdl as IPC in MozIccManager */ [Throws] - DOMRequest matchMvno(DOMString mvnoType, DOMString matchData); + DOMRequest matchMvno(IccMvnoType mvnoType, DOMString matchData); }; diff --git a/dom/webidl/MozMobileConnection.webidl b/dom/webidl/MozMobileConnection.webidl index 5c16958ad07f..c9ea38eb74d9 100644 --- a/dom/webidl/MozMobileConnection.webidl +++ b/dom/webidl/MozMobileConnection.webidl @@ -263,49 +263,6 @@ interface MozMobileConnection : EventTarget [Throws, CheckPermissions="mobileconnection"] DOMRequest getVoicePrivacyMode(); - /** - * Send a MMI message. - * - * @param mmi - * DOMString containing an MMI string that can be associated to a - * USSD request or other RIL functionality. - * - * @return a DOMRequest. - * - * If successful, the request's onsuccess will be called. And the request's - * result will be an object containing information about the operation. - * @see MozMMIResult for the detail of result. - * - * Otherwise, the request's onerror will be called, and the request's error - * will be a DOMMMIError. - * @see DOMMMIError for the detail of error. - * - * Note: In case that the MMI code requires sending an USSD request, the - * DOMrequest 'success' event means that the RIL has successfully processed - * and sent the USSD request to the network. The network reply will be - * reported via 'onussdreceived' event. If the MMI code is not associated to - * a USSD but to other RIL request its result, if one is needed, will be - * notified via the returned DOMRequest 'success' or 'error' event. - */ - [Throws, CheckPermissions="mobileconnection"] - DOMRequest sendMMI(DOMString mmi); - - /** - * Cancel the current MMI request if one exists. - * - * @return a DOMRequest. - * - * If successful, the request's onsuccess will be called. And the request's - * result will be an object containing information about the operation. - * @see MozMMIResult for the detail of result. - * - * Otherwise, the request's onerror will be called, and the request's error - * will be a DOMMMIError. - * @see DOMMMIError for the detail of error. - */ - [Throws, CheckPermissions="mobileconnection"] - DOMRequest cancelMMI(); - /** * Configures call forward options. * @@ -522,12 +479,6 @@ interface MozMobileConnection : EventTarget */ attribute EventHandler ondatachange; - /** - * The 'ussdreceived' event is notified whenever a new USSD message is - * received. - */ - attribute EventHandler onussdreceived; - /** * The 'dataerror' event is notified whenever the data connection object * receives an error from the RIL. diff --git a/dom/webidl/USSDReceivedEvent.webidl b/dom/webidl/USSDReceivedEvent.webidl index 627beb3712d4..5583ec795ee8 100644 --- a/dom/webidl/USSDReceivedEvent.webidl +++ b/dom/webidl/USSDReceivedEvent.webidl @@ -5,7 +5,7 @@ */ [Pref="dom.telephony.enabled", - CheckPermissions="telephony mobileconnection", + CheckPermissions="telephony", AvailableIn="CertifiedApps", Constructor(DOMString type, optional USSDReceivedEventInit eventInitDict)] interface USSDReceivedEvent : Event @@ -13,7 +13,6 @@ interface USSDReceivedEvent : Event readonly attribute unsigned long serviceId; readonly attribute DOMString? message; readonly attribute USSDSession? session; // null if session is ended. - readonly attribute boolean sessionEnded; // deprecated. Bug 1070831 }; dictionary USSDReceivedEventInit : EventInit @@ -21,5 +20,4 @@ dictionary USSDReceivedEventInit : EventInit unsigned long serviceId = 0; DOMString? message = null; USSDSession? session = null; - boolean sessionEnded = false; }; diff --git a/dom/xul/moz.build b/dom/xul/moz.build index 62315cd7cfc5..a316ca9397ec 100644 --- a/dom/xul/moz.build +++ b/dom/xul/moz.build @@ -12,7 +12,6 @@ if CONFIG['MOZ_XUL']: DIRS += ['templates'] XPIDL_SOURCES += [ - 'nsIXULContextMenuBuilder.idl', 'nsIXULOverlayProvider.idl', ] @@ -23,7 +22,6 @@ if CONFIG['MOZ_XUL']: UNIFIED_SOURCES += [ 'nsXULCommandDispatcher.cpp', 'nsXULContentSink.cpp', - 'nsXULContextMenuBuilder.cpp', 'nsXULElement.cpp', 'nsXULPopupListener.cpp', 'nsXULPrototypeCache.cpp', diff --git a/dom/xul/nsIXULContextMenuBuilder.idl b/dom/xul/nsIXULContextMenuBuilder.idl deleted file mode 100644 index 1f5070393f6f..000000000000 --- a/dom/xul/nsIXULContextMenuBuilder.idl +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -#include "nsISupports.idl" - -interface nsIDOMDocumentFragment; - -/** - * An interface for initialization of XUL context menu builder - * and for triggering of menuitem actions with assigned identifiers. - */ - -[scriptable, uuid(eb6b42c0-2f1c-4760-b5ca-bdc9b3ec77d4)] -interface nsIXULContextMenuBuilder : nsISupports -{ - - /** - * Initialize builder before building. - * - * @param aDocumentFragment the fragment that will be used to append top - * level elements - * - * @param aGeneratedItemIdAttrName the name of the attribute that will be - * used to mark elements as generated and for menuitem identification - */ - void init(in nsIDOMDocumentFragment aDocumentFragment, - in AString aGeneratedItemIdAttrName); - - /** - * Invoke the action of the menuitem with assigned id aGeneratedItemId. - * - * @param aGeneratedItemId the menuitem id - */ - void click(in DOMString aGeneratedItemId); - -}; diff --git a/dom/xul/nsXULContextMenuBuilder.cpp b/dom/xul/nsXULContextMenuBuilder.cpp deleted file mode 100644 index e6d9d5602c9e..000000000000 --- a/dom/xul/nsXULContextMenuBuilder.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -#include "nsContentCreatorFunctions.h" -#include "nsIContent.h" -#include "nsIDOMDocumentFragment.h" -#include "nsIDOMHTMLElement.h" -#include "nsIDOMHTMLMenuItemElement.h" -#include "nsXULContextMenuBuilder.h" -#include "nsIDocument.h" -#include "mozilla/dom/Element.h" - -using namespace mozilla; -using namespace mozilla::dom; - -nsXULContextMenuBuilder::nsXULContextMenuBuilder() - : mCurrentGeneratedItemId(0) -{ -} - -nsXULContextMenuBuilder::~nsXULContextMenuBuilder() -{ -} - -NS_IMPL_CYCLE_COLLECTION(nsXULContextMenuBuilder, mFragment, mDocument, - mCurrentNode, mElements) - -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULContextMenuBuilder) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULContextMenuBuilder) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULContextMenuBuilder) - NS_INTERFACE_MAP_ENTRY(nsIMenuBuilder) - NS_INTERFACE_MAP_ENTRY(nsIXULContextMenuBuilder) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMenuBuilder) -NS_INTERFACE_MAP_END - - -NS_IMETHODIMP -nsXULContextMenuBuilder::OpenContainer(const nsAString& aLabel) -{ - if (!mFragment) { - return NS_ERROR_NOT_INITIALIZED; - } - - if (!mCurrentNode) { - mCurrentNode = mFragment; - } else { - nsCOMPtr menu; - nsresult rv = CreateElement(nsGkAtoms::menu, nullptr, getter_AddRefs(menu)); - NS_ENSURE_SUCCESS(rv, rv); - - menu->SetAttr(kNameSpaceID_None, nsGkAtoms::label, aLabel, false); - - nsCOMPtr menuPopup; - rv = CreateElement(nsGkAtoms::menupopup, nullptr, - getter_AddRefs(menuPopup)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = menu->AppendChildTo(menuPopup, false); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mCurrentNode->AppendChildTo(menu, false); - NS_ENSURE_SUCCESS(rv, rv); - - mCurrentNode = menuPopup; - } - - return NS_OK; -} - -NS_IMETHODIMP -nsXULContextMenuBuilder::AddItemFor(nsIDOMHTMLMenuItemElement* aElement, - bool aCanLoadIcon) -{ - if (!mFragment) { - return NS_ERROR_NOT_INITIALIZED; - } - - nsCOMPtr menuitem; - nsCOMPtr element = do_QueryInterface(aElement); - nsresult rv = CreateElement(nsGkAtoms::menuitem, element, - getter_AddRefs(menuitem)); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString type; - aElement->GetType(type); - if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) { - // The menu is only temporary, so we don't need to handle - // the radio type precisely. - menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::type, - NS_LITERAL_STRING("checkbox"), false); - bool checked; - aElement->GetChecked(&checked); - if (checked) { - menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::checked, - NS_LITERAL_STRING("true"), false); - } - } - - nsAutoString label; - aElement->GetLabel(label); - menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::label, label, false); - - nsAutoString icon; - aElement->GetIcon(icon); - if (!icon.IsEmpty()) { - menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, - NS_LITERAL_STRING("menuitem-iconic"), false); - if (aCanLoadIcon) { - menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::image, icon, false); - } - } - - bool disabled; - aElement->GetDisabled(&disabled); - if (disabled) { - menuitem->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, - NS_LITERAL_STRING("true"), false); - } - - return mCurrentNode->AppendChildTo(menuitem, false); -} - -NS_IMETHODIMP -nsXULContextMenuBuilder::AddSeparator() -{ - if (!mFragment) { - return NS_ERROR_NOT_INITIALIZED; - } - - nsCOMPtr menuseparator; - nsresult rv = CreateElement(nsGkAtoms::menuseparator, nullptr, - getter_AddRefs(menuseparator)); - NS_ENSURE_SUCCESS(rv, rv); - - return mCurrentNode->AppendChildTo(menuseparator, false); -} - -NS_IMETHODIMP -nsXULContextMenuBuilder::UndoAddSeparator() -{ - if (!mFragment) { - return NS_ERROR_NOT_INITIALIZED; - } - - uint32_t count = mCurrentNode->GetChildCount(); - if (!count || - mCurrentNode->GetChildAt(count - 1)->Tag() != nsGkAtoms::menuseparator) { - return NS_OK; - } - - mCurrentNode->RemoveChildAt(count - 1, false); - return NS_OK; -} - -NS_IMETHODIMP -nsXULContextMenuBuilder::CloseContainer() -{ - if (!mFragment) { - return NS_ERROR_NOT_INITIALIZED; - } - - if (mCurrentNode == mFragment) { - mCurrentNode = nullptr; - } else { - nsIContent* parent = mCurrentNode->GetParent(); - mCurrentNode = parent->GetParent(); - } - - return NS_OK; -} - - -NS_IMETHODIMP -nsXULContextMenuBuilder::Init(nsIDOMDocumentFragment* aDocumentFragment, - const nsAString& aGeneratedItemIdAttrName) -{ - NS_ENSURE_ARG_POINTER(aDocumentFragment); - - mFragment = do_QueryInterface(aDocumentFragment); - mDocument = mFragment->GetOwnerDocument(); - mGeneratedItemIdAttr = do_GetAtom(aGeneratedItemIdAttrName); - - return NS_OK; -} - -NS_IMETHODIMP -nsXULContextMenuBuilder::Click(const nsAString& aGeneratedItemId) -{ - nsresult rv; - int32_t idx = nsString(aGeneratedItemId).ToInteger(&rv); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr element = mElements.SafeObjectAt(idx); - if (element) { - element->DOMClick(); - } - } - - return NS_OK; -} - -nsresult -nsXULContextMenuBuilder::CreateElement(nsIAtom* aTag, - nsIDOMHTMLElement* aHTMLElement, - Element** aResult) -{ - *aResult = nullptr; - - nsRefPtr nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo( - aTag, nullptr, kNameSpaceID_XUL, nsIDOMNode::ELEMENT_NODE); - - nsresult rv = NS_NewElement(aResult, nodeInfo.forget(), NOT_FROM_PARSER); - if (NS_FAILED(rv)) { - return rv; - } - - nsAutoString generateditemid; - - if (aHTMLElement) { - mElements.AppendObject(aHTMLElement); - generateditemid.AppendInt(mCurrentGeneratedItemId++); - } - - (*aResult)->SetAttr(kNameSpaceID_None, mGeneratedItemIdAttr, generateditemid, - false); - - return NS_OK; -} diff --git a/dom/xul/nsXULContextMenuBuilder.h b/dom/xul/nsXULContextMenuBuilder.h deleted file mode 100644 index 83e19cb99dab..000000000000 --- a/dom/xul/nsXULContextMenuBuilder.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -#include "nsCOMPtr.h" -#include "nsCOMArray.h" -#include "nsIMenuBuilder.h" -#include "nsIXULContextMenuBuilder.h" -#include "nsCycleCollectionParticipant.h" - -class nsIAtom; -class nsIContent; -class nsIDocument; -class nsIDOMHTMLElement; - -namespace mozilla { -namespace dom { -class Element; -} // namespace dom -} // namespace mozilla - -class nsXULContextMenuBuilder : public nsIMenuBuilder, - public nsIXULContextMenuBuilder -{ -public: - nsXULContextMenuBuilder(); - - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULContextMenuBuilder, - nsIMenuBuilder) - NS_DECL_NSIMENUBUILDER - - NS_DECL_NSIXULCONTEXTMENUBUILDER - -protected: - virtual ~nsXULContextMenuBuilder(); - - nsresult CreateElement(nsIAtom* aTag, - nsIDOMHTMLElement* aHTMLElement, - mozilla::dom::Element** aResult); - - nsCOMPtr mFragment; - nsCOMPtr mDocument; - nsCOMPtr mGeneratedItemIdAttr; - - nsCOMPtr mCurrentNode; - int32_t mCurrentGeneratedItemId; - - nsCOMArray mElements; -}; diff --git a/gfx/layers/client/TiledContentClient.cpp b/gfx/layers/client/TiledContentClient.cpp index 2795f2d7a5d9..0b4cf6ed6042 100644 --- a/gfx/layers/client/TiledContentClient.cpp +++ b/gfx/layers/client/TiledContentClient.cpp @@ -216,7 +216,7 @@ SharedFrameMetricsHelper::UpdateFromCompositorFrameMetrics( fabsf(contentMetrics.mDisplayPort.x - compositorMetrics.mDisplayPort.x) <= 2 && fabsf(contentMetrics.mDisplayPort.y - compositorMetrics.mDisplayPort.y) <= 2 && fabsf(contentMetrics.mDisplayPort.width - compositorMetrics.mDisplayPort.width) <= 2 && - fabsf(contentMetrics.mDisplayPort.height - compositorMetrics.mDisplayPort.height)) { + fabsf(contentMetrics.mDisplayPort.height - compositorMetrics.mDisplayPort.height) <= 2) { return false; } diff --git a/gfx/layers/opengl/GrallocTextureHost.cpp b/gfx/layers/opengl/GrallocTextureHost.cpp index 3df82b18a710..ee1f60822f9c 100644 --- a/gfx/layers/opengl/GrallocTextureHost.cpp +++ b/gfx/layers/opengl/GrallocTextureHost.cpp @@ -96,150 +96,6 @@ TextureTargetForAndroidPixelFormat(android::PixelFormat aFormat) } } -GrallocTextureSourceOGL::GrallocTextureSourceOGL(CompositorOGL* aCompositor, - GrallocTextureHostOGL* aTextureHost, - android::GraphicBuffer* aGraphicBuffer, - gfx::SurfaceFormat aFormat) - : mCompositor(aCompositor) - , mTextureHost(aTextureHost) - , mGraphicBuffer(aGraphicBuffer) - , mEGLImage(0) - , mFormat(aFormat) - , mNeedsReset(true) -{ - MOZ_ASSERT(mGraphicBuffer.get()); -} - -GrallocTextureSourceOGL::~GrallocTextureSourceOGL() -{ - DeallocateDeviceData(); - mCompositor = nullptr; -} - -void -GrallocTextureSourceOGL::BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) -{ - /* - * The job of this function is to ensure that the texture is tied to the - * android::GraphicBuffer, so that texturing will source the GraphicBuffer. - * - * To this effect we create an EGLImage wrapping this GraphicBuffer, - * using EGLImageCreateFromNativeBuffer, and then we tie this EGLImage to our - * texture using fEGLImageTargetTexture2D. - */ - MOZ_ASSERT(gl()); - if (!IsValid() || !gl()->MakeCurrent()) { - return; - } - - GLuint tex = GetGLTexture(); - GLuint textureTarget = GetTextureTarget(); - - gl()->fActiveTexture(aTextureUnit); - gl()->fBindTexture(textureTarget, tex); - - ApplyFilterToBoundTexture(gl(), aFilter, textureTarget); - -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 - if (mTextureHost) { - // Wait until it's ready. - mTextureHost->WaitAcquireFenceSyncComplete(); - } -#endif -} - -bool GrallocTextureSourceOGL::Lock() -{ - MOZ_ASSERT(IsValid()); - if (!IsValid()) { - return false; - } - if (!gl()->MakeCurrent()) { - NS_WARNING("Failed to make the gl context current"); - return false; - } - - mTexture = mCompositor->GetTemporaryTexture(GetTextureTarget(), LOCAL_GL_TEXTURE0); - - GLuint textureTarget = GetTextureTarget(); - - gl()->fActiveTexture(LOCAL_GL_TEXTURE0); - gl()->fBindTexture(textureTarget, mTexture); - if (!mEGLImage) { - mEGLImage = EGLImageCreateFromNativeBuffer(gl(), mGraphicBuffer->getNativeBuffer()); - } - gl()->fEGLImageTargetTexture2D(textureTarget, mEGLImage); - return true; -} - -bool -GrallocTextureSourceOGL::IsValid() const -{ - return !!gl() && !!mGraphicBuffer.get() && !!mCompositor; -} - -gl::GLContext* -GrallocTextureSourceOGL::gl() const -{ - return mCompositor ? mCompositor->gl() : nullptr; -} - -void -GrallocTextureSourceOGL::SetCompositor(Compositor* aCompositor) -{ - if (mCompositor && !aCompositor) { - DeallocateDeviceData(); - } - mCompositor = static_cast(aCompositor); -} - - -GLenum -GrallocTextureSourceOGL::GetTextureTarget() const -{ - MOZ_ASSERT(gl()); - MOZ_ASSERT(mGraphicBuffer.get()); - - if (!gl() || !mGraphicBuffer.get()) { - return LOCAL_GL_TEXTURE_EXTERNAL; - } - - // SGX has a quirk that only TEXTURE_EXTERNAL works and any other value will - // result in black pixels when trying to draw from bound textures. - // Unfortunately, using TEXTURE_EXTERNAL on Adreno has a terrible effect on - // performance. - // See Bug 950050. - if (gl()->Renderer() == gl::GLRenderer::SGX530 || - gl()->Renderer() == gl::GLRenderer::SGX540) { - return LOCAL_GL_TEXTURE_EXTERNAL; - } - - return TextureTargetForAndroidPixelFormat(mGraphicBuffer->getPixelFormat()); -} - -gfx::IntSize -GrallocTextureSourceOGL::GetSize() const -{ - if (!IsValid()) { - NS_WARNING("Trying to access the size of an invalid GrallocTextureSourceOGL"); - return gfx::IntSize(0, 0); - } - return gfx::IntSize(mGraphicBuffer->getWidth(), mGraphicBuffer->getHeight()); -} - -void -GrallocTextureSourceOGL::DeallocateDeviceData() -{ - if (mEGLImage) { - MOZ_ASSERT(mCompositor); - if (!gl() || !gl()->MakeCurrent()) { - return; - } - EGLImageDestroy(gl(), mEGLImage); - mEGLImage = EGL_NO_IMAGE; - } -} - GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags, const NewSurfaceDescriptorGralloc& aDescriptor) : TextureHost(aFlags) @@ -272,9 +128,6 @@ void GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor) { mCompositor = static_cast(aCompositor); - if (mTilingTextureSource) { - mTilingTextureSource->SetCompositor(mCompositor); - } if (mGLTextureSource) { mGLTextureSource->SetCompositor(mCompositor); } @@ -312,10 +165,6 @@ GrallocTextureHostOGL::GetFormat() const void GrallocTextureHostOGL::DeallocateSharedData() { - if (mTilingTextureSource) { - mTilingTextureSource->ForgetBuffer(); - mTilingTextureSource = nullptr; - } if (mGLTextureSource) { mGLTextureSource = nullptr; } @@ -339,10 +188,6 @@ GrallocTextureHostOGL::DeallocateSharedData() void GrallocTextureHostOGL::ForgetSharedData() { - if (mTilingTextureSource) { - mTilingTextureSource->ForgetBuffer(); - mTilingTextureSource = nullptr; - } if (mGLTextureSource) { mGLTextureSource = nullptr; } @@ -351,9 +196,6 @@ GrallocTextureHostOGL::ForgetSharedData() void GrallocTextureHostOGL::DeallocateDeviceData() { - if (mTilingTextureSource) { - mTilingTextureSource->DeallocateDeviceData(); - } if (mGLTextureSource) { mGLTextureSource = nullptr; } @@ -387,77 +229,24 @@ GrallocTextureHostOGL::GetRenderState() TemporaryRef GrallocTextureHostOGL::GetAsSurface() { - if (mTilingTextureSource) { - return mTilingTextureSource->GetAsSurface(); - } else { - android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); - uint8_t* grallocData; - int32_t rv = graphicBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast(&grallocData)); - RefPtr grallocTempSurf = - gfx::Factory::CreateWrappingDataSourceSurface(grallocData, - graphicBuffer->getStride() * android::bytesPerPixel(graphicBuffer->getPixelFormat()), - GetSize(), GetFormat()); - RefPtr surf = CreateDataSourceSurfaceByCloning(grallocTempSurf); - - graphicBuffer->unlock(); - - return surf.forget(); - } -} - -TemporaryRef -GrallocTextureSourceOGL::GetAsSurface() { - if (!IsValid()) { - return nullptr; - } - + android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); uint8_t* grallocData; - int32_t rv = mGraphicBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast(&grallocData)); - if (rv) { - return nullptr; - } - + int32_t rv = graphicBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast(&grallocData)); RefPtr grallocTempSurf = gfx::Factory::CreateWrappingDataSourceSurface(grallocData, - mGraphicBuffer->getStride() * android::bytesPerPixel(mGraphicBuffer->getPixelFormat()), + graphicBuffer->getStride() * android::bytesPerPixel(graphicBuffer->getPixelFormat()), GetSize(), GetFormat()); - RefPtr surf = CreateDataSourceSurfaceByCloning(grallocTempSurf); - mGraphicBuffer->unlock(); + graphicBuffer->unlock(); return surf.forget(); } -GLuint -GrallocTextureSourceOGL::GetGLTexture() -{ - return mTexture; -} - -void -GrallocTextureSourceOGL::BindEGLImage() -{ - gl()->fEGLImageTargetTexture2D(GetTextureTarget(), mEGLImage); -} - TextureSource* GrallocTextureHostOGL::GetTextureSources() { - // This is now only used with tiled layers, and will eventually be removed. - // Other layer types use BindTextureSource instead. - MOZ_ASSERT(!mGLTextureSource); - if (!mTilingTextureSource) { - android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); - MOZ_ASSERT(graphicBuffer); - if (!graphicBuffer) { - return nullptr; - } - mTilingTextureSource = new GrallocTextureSourceOGL(mCompositor, this, - graphicBuffer, mFormat); - } - mTilingTextureSource->Lock(); - return mTilingTextureSource; + return nullptr; } void @@ -529,8 +318,6 @@ GrallocTextureHostOGL::PrepareTextureSource(CompositableTextureSourceRef& aTextu // because otherwise we would be modifying the content of every layer that uses // the TextureSource in question, even thoug they don't use this TextureHost. - MOZ_ASSERT(!mTilingTextureSource); - android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get(); MOZ_ASSERT(graphicBuffer); diff --git a/gfx/layers/opengl/GrallocTextureHost.h b/gfx/layers/opengl/GrallocTextureHost.h index 56290658f8d3..24565ff7d816 100644 --- a/gfx/layers/opengl/GrallocTextureHost.h +++ b/gfx/layers/opengl/GrallocTextureHost.h @@ -15,74 +15,6 @@ namespace mozilla { namespace layers { -class GrallocTextureHostOGL; - -// Progressively getting replaced by GLTextureSource -class GrallocTextureSourceOGL : public TextureSource - , public TextureSourceOGL -{ -public: - friend class GrallocTextureHostOGL; - - GrallocTextureSourceOGL(CompositorOGL* aCompositor, - GrallocTextureHostOGL* aTextureHost, - android::GraphicBuffer* aGraphicBuffer, - gfx::SurfaceFormat aFormat); - - virtual ~GrallocTextureSourceOGL(); - - virtual bool IsValid() const MOZ_OVERRIDE; - - virtual void BindTexture(GLenum aTextureUnit, gfx::Filter aFilter) MOZ_OVERRIDE; - - virtual gfx::IntSize GetSize() const MOZ_OVERRIDE; - - virtual TextureSourceOGL* AsSourceOGL() MOZ_OVERRIDE { return this; } - - virtual GLenum GetTextureTarget() const MOZ_OVERRIDE; - - virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; } - - virtual GLenum GetWrapMode() const MOZ_OVERRIDE - { - return LOCAL_GL_CLAMP_TO_EDGE; - } - - void DeallocateDeviceData(); - - gl::GLContext* gl() const; - - virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE; - - void ForgetBuffer() - { - mGraphicBuffer = nullptr; - mTextureHost = nullptr; - } - - TemporaryRef GetAsSurface(); - - GLuint GetGLTexture(); - - void BindEGLImage(); - - EGLImage GetEGLImage() - { - return mEGLImage; - } - - bool Lock(); - -protected: - RefPtr mCompositor; - GrallocTextureHostOGL* mTextureHost; - android::sp mGraphicBuffer; - EGLImage mEGLImage; - GLuint mTexture; - gfx::SurfaceFormat mFormat; - bool mNeedsReset; -}; - class GrallocTextureHostOGL : public TextureHost #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 , public TextureHostOGL @@ -144,8 +76,6 @@ private: NewSurfaceDescriptorGralloc mGrallocHandle; RefPtr mGLTextureSource; RefPtr mCompositor; - // only used for tiling, will be removed. - RefPtr mTilingTextureSource; // Size reported by the GraphicBuffer gfx::IntSize mSize; // Size reported by TextureClient, can be different in some cases (video?), diff --git a/hal/gonk/GonkHal.cpp b/hal/gonk/GonkHal.cpp index 69b40766f289..15a442da0847 100644 --- a/hal/gonk/GonkHal.cpp +++ b/hal/gonk/GonkHal.cpp @@ -27,11 +27,7 @@ #include #include #include -#if ANDROID_VERSION >= 21 -#include -#else -#include -#endif +#include #include "mozilla/DebugOnly.h" @@ -1321,6 +1317,8 @@ EnsureKernelLowMemKillerParamsSet() int32_t lowerBoundOfNextKillUnderKB = 0; int32_t countOfLowmemorykillerParametersSets = 0; + long page_size = sysconf(_SC_PAGESIZE); + for (int i = NUM_PROCESS_PRIORITY - 1; i >= 0; i--) { // The system doesn't function correctly if we're missing these prefs, so // crash loudly. @@ -1358,7 +1356,7 @@ EnsureKernelLowMemKillerParamsSet() adjParams.AppendPrintf("%d,", OomAdjOfOomScoreAdj(oomScoreAdj)); // minfree is in pages. - minfreeParams.AppendPrintf("%d,", killUnderKB * 1024 / PAGE_SIZE); + minfreeParams.AppendPrintf("%ld,", killUnderKB * 1024 / page_size); lowerBoundOfNextOomScoreAdj = oomScoreAdj; lowerBoundOfNextKillUnderKB = killUnderKB; @@ -1381,7 +1379,7 @@ EnsureKernelLowMemKillerParamsSet() // notify_trigger is in pages. WriteToFile("/sys/module/lowmemorykiller/parameters/notify_trigger", - nsPrintfCString("%d", lowMemNotifyThresholdKB * 1024 / PAGE_SIZE).get()); + nsPrintfCString("%ld", lowMemNotifyThresholdKB * 1024 / page_size).get()); } // Ensure OOM events appear in logcat diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 1d724cf4fc9b..5ef521d52188 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2242,7 +2242,7 @@ LIRGenerator::visitLoadSlot(MLoadSlot *ins) MOZ_CRASH("typed load must have a payload"); default: - define(new(alloc()) LLoadSlotT(useRegisterAtStart(ins->slots())), ins); + define(new(alloc()) LLoadSlotT(useRegisterForTypedLoad(ins->slots(), ins->type())), ins); break; } } @@ -3088,13 +3088,16 @@ LIRGenerator::visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins) void LIRGenerator::visitLoadFixedSlot(MLoadFixedSlot *ins) { - MOZ_ASSERT(ins->object()->type() == MIRType_Object); + MDefinition *obj = ins->object(); + MOZ_ASSERT(obj->type() == MIRType_Object); - if (ins->type() == MIRType_Value) { - LLoadFixedSlotV *lir = new(alloc()) LLoadFixedSlotV(useRegisterAtStart(ins->object())); + MIRType type = ins->type(); + + if (type == MIRType_Value) { + LLoadFixedSlotV *lir = new(alloc()) LLoadFixedSlotV(useRegisterAtStart(obj)); defineBox(lir, ins); } else { - LLoadFixedSlotT *lir = new(alloc()) LLoadFixedSlotT(useRegisterAtStart(ins->object())); + LLoadFixedSlotT *lir = new(alloc()) LLoadFixedSlotT(useRegisterForTypedLoad(obj, type)); define(lir, ins); } } diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index 38aa8c398d8f..e2ba7f10cc1c 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -482,6 +482,23 @@ LIRGeneratorShared::fillBoxUses(LInstruction *lir, size_t n, MDefinition *mir) } #endif +LUse +LIRGeneratorShared::useRegisterForTypedLoad(MDefinition *mir, MIRType type) +{ + MOZ_ASSERT(type != MIRType_Value && type != MIRType_None); + MOZ_ASSERT(mir->type() == MIRType_Object || mir->type() == MIRType_Slots); + +#ifdef JS_PUNBOX64 + // On x64, masm.loadUnboxedValue emits slightly less efficient code when + // the input and output use the same register and we're not loading an + // int32/bool/double, so we just call useRegister in this case. + if (type != MIRType_Int32 && type != MIRType_Boolean && type != MIRType_Double) + return useRegister(mir); +#endif + + return useRegisterAtStart(mir); +} + } // namespace jit } // namespace js diff --git a/js/src/jit/shared/Lowering-shared.h b/js/src/jit/shared/Lowering-shared.h index f22b81864f51..98e86fda0834 100644 --- a/js/src/jit/shared/Lowering-shared.h +++ b/js/src/jit/shared/Lowering-shared.h @@ -111,6 +111,8 @@ class LIRGeneratorShared : public MDefinitionVisitor inline LAllocation useRegisterOrNonNegativeConstantAtStart(MDefinition *mir); inline LAllocation useRegisterOrNonDoubleConstant(MDefinition *mir); + inline LUse useRegisterForTypedLoad(MDefinition *mir, MIRType type); + #ifdef JS_NUNBOX32 inline LUse useType(MDefinition *mir, LUse::Policy policy); inline LUse usePayload(MDefinition *mir, LUse::Policy policy); diff --git a/js/xpconnect/tests/idl/Makefile.in b/js/xpconnect/tests/idl/Makefile.in deleted file mode 100644 index 5603df2a1320..000000000000 --- a/js/xpconnect/tests/idl/Makefile.in +++ /dev/null @@ -1,11 +0,0 @@ -# -# 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/. - -include $(topsrcdir)/config/rules.mk - -componentdir = js/xpconnect/tests/components -libs:: $(DEPTH)/config/makefiles/xpidl/xpt/$(XPT_NAME) - $(INSTALL) $^ $(testxpcobjdir)/$(componentdir)/native - $(INSTALL) $^ $(testxpcobjdir)/$(componentdir)/js diff --git a/js/xpconnect/tests/idl/moz.build b/js/xpconnect/tests/idl/moz.build index 1b55e03ae1e3..71aee0d47b0e 100644 --- a/js/xpconnect/tests/idl/moz.build +++ b/js/xpconnect/tests/idl/moz.build @@ -14,3 +14,11 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'xpctest' +# XXX: This relies on xpctest.xpt being created in dist/bin/components/ during +# the export tier AND TEST_HARNESS_FILES being processed after that. +TEST_HARNESS_FILES.xpcshell.js.xpconnect.tests.components.native += [ + '!/dist/bin/components/xpctest.xpt', +] +TEST_HARNESS_FILES.xpcshell.js.xpconnect.tests.components.js += [ + '!/dist/bin/components/xpctest.xpt', +] diff --git a/mfbt/MathAlgorithms.h b/mfbt/MathAlgorithms.h index abfc2184847e..41be25960fb3 100644 --- a/mfbt/MathAlgorithms.h +++ b/mfbt/MathAlgorithms.h @@ -146,7 +146,7 @@ Abs(const long double aLongDouble) } // namespace mozilla -#if defined(_WIN32) && \ +#if defined(_MSC_VER) && \ (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64)) # define MOZ_BITSCAN_WINDOWS diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index bccdc4c56b66..60f807f6f8da 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -408,6 +408,8 @@ @BINPATH@/components/Webapps.manifest @BINPATH@/components/AppsService.js @BINPATH@/components/AppsService.manifest +@BINPATH@/components/htmlMenuBuilder.js +@BINPATH@/components/htmlMenuBuilder.manifest @BINPATH@/components/Activities.manifest @BINPATH@/components/ActivitiesGlue.js diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat index 857110554315..06944edb5841 100644 --- a/netwerk/dns/effective_tld_names.dat +++ b/netwerk/dns/effective_tld_names.dat @@ -9233,7 +9233,7 @@ githubusercontent.com ro.com // Google, Inc. -// Submitted by Eduardo Vela 2012-10-24 +// Submitted by Eduardo Vela 2014-12-19 appspot.com blogspot.ae blogspot.be @@ -9281,6 +9281,7 @@ blogspot.tw codespot.com googleapis.com googlecode.com +pagespeedmobilizer.com withgoogle.com // Heroku : https://www.heroku.com/ diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 7a46a22448ef..750dfafea8ba 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -2377,13 +2377,15 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, void *param) LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param)); nsresult closeCode = static_cast(reason); - nsHttpTransaction *trans = (nsHttpTransaction *) param; + nsRefPtr trans = + dont_AddRef(static_cast(param)); + // // if the transaction owns a connection and the transaction is not done, // then ask the connection to close the transaction. otherwise, close the // transaction directly (removing it from the pending queue first). // - nsAHttpConnection *conn = trans->Connection(); + nsRefPtr conn(trans->Connection()); if (conn && !trans->IsDone()) { conn->CloseTransaction(trans, closeCode); } else { @@ -2394,7 +2396,7 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, void *param) int32_t index = ent->mPendingQ.IndexOf(trans); if (index >= 0) { LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]" - " found in pending queue\n", trans)); + " found in pending queue\n", trans.get())); ent->mPendingQ.RemoveElementAt(index); nsHttpTransaction *temp = trans; NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument! @@ -2429,12 +2431,11 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, void *param) if (liveTransaction && liveTransaction->IsNullTransaction()) { LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p] " "also canceling Null Transaction %p on conn %p\n", - trans, liveTransaction, activeConn)); + trans.get(), liveTransaction, activeConn)); activeConn->CloseTransaction(liveTransaction, closeCode); } } } - NS_RELEASE(trans); } void diff --git a/python/mozbuild/mozbuild/backend/common.py b/python/mozbuild/mozbuild/backend/common.py index e2be49b0b7ee..4f8d0ee50eb3 100644 --- a/python/mozbuild/mozbuild/backend/common.py +++ b/python/mozbuild/mozbuild/backend/common.py @@ -39,7 +39,7 @@ class XPIDLManager(object): self.idls = {} self.modules = {} - def register_idl(self, source, module, allow_existing=False): + def register_idl(self, source, module, install_target, allow_existing=False): """Registers an IDL file with this instance. The IDL file will be built, installed, etc. @@ -58,7 +58,8 @@ class XPIDLManager(object): raise Exception('IDL already registered: %' % entry['basename']) self.idls[entry['basename']] = entry - self.modules.setdefault(entry['module'], set()).add(entry['root']) + t = self.modules.setdefault(entry['module'], (install_target, set())) + t[1].add(entry['root']) class WebIDLCollection(object): @@ -181,7 +182,8 @@ class CommonBackend(BuildBackend): topsrcdir=obj.topsrcdir) elif isinstance(obj, XPIDLFile): - self._idl_manager.register_idl(obj.source_path, obj.module) + self._idl_manager.register_idl(obj.source_path, obj.module, + obj.install_target) elif isinstance(obj, ConfigFileSubstitution): # Do not handle ConfigFileSubstitution for Makefiles. Leave that diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index 809407badb59..39c2f7b985ad 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals +import errno import itertools import json import logging @@ -15,11 +16,11 @@ from collections import ( defaultdict, namedtuple, ) +from StringIO import StringIO import mozwebidlcodegen from reftest import ReftestManifest -import mozbuild.makeutil as mozmakeutil from mozpack.copier import FilePurger from mozpack.manifests import ( InstallManifest, @@ -751,7 +752,7 @@ class RecursiveMakeBackend(CommonBackend): # Write out a master list of all IPDL source files. ipdl_dir = mozpath.join(self.environment.topobjdir, 'ipc', 'ipdl') - mk = mozmakeutil.Makefile() + mk = Makefile() sorted_ipdl_sources = list(sorted(self._ipdl_sources)) mk.add_statement('ALL_IPDLSRCS := %s' % ' '.join(sorted_ipdl_sources)) @@ -983,8 +984,7 @@ INSTALL_TARGETS += %(prefix)s def _handle_idl_manager(self, manager): build_files = self._install_manifests['xpidl'] - for p in ('Makefile', 'backend.mk', '.deps/.mkdir.done', - 'xpt/.mkdir.done'): + for p in ('Makefile', 'backend.mk', '.deps/.mkdir.done'): build_files.add_optional_exists(p) for idl in manager.idls.values(): @@ -994,34 +994,44 @@ INSTALL_TARGETS += %(prefix)s % idl['root']) for module in manager.modules: - build_files.add_optional_exists(mozpath.join('xpt', - '%s.xpt' % module)) build_files.add_optional_exists(mozpath.join('.deps', '%s.pp' % module)) modules = manager.modules xpt_modules = sorted(modules.keys()) - rules = [] + xpt_files = set() + + mk = Makefile() for module in xpt_modules: - deps = sorted(modules[module]) - idl_deps = ['$(dist_idl_dir)/%s.idl' % dep for dep in deps] - rules.extend([ - # It may seem strange to have the .idl files listed as - # prerequisites both here and in the auto-generated .pp files. - # It is necessary to list them here to handle the case where a - # new .idl is added to an xpt. If we add a new .idl and nothing - # else has changed, the new .idl won't be referenced anywhere - # except in the command invocation. Therefore, the .xpt won't - # be rebuilt because the dependencies say it is up to date. By - # listing the .idls here, we ensure the make file has a - # reference to the new .idl. Since the new .idl presumably has - # an mtime newer than the .xpt, it will trigger xpt generation. - '$(idl_xpt_dir)/%s.xpt: %s' % (module, ' '.join(idl_deps)), - '\t@echo "$(notdir $@)"', - '\t$(idlprocess) $(basename $(notdir $@)) %s' % ' '.join(deps), - '', - ]) + install_target, sources = modules[module] + deps = sorted(sources) + + # It may seem strange to have the .idl files listed as + # prerequisites both here and in the auto-generated .pp files. + # It is necessary to list them here to handle the case where a + # new .idl is added to an xpt. If we add a new .idl and nothing + # else has changed, the new .idl won't be referenced anywhere + # except in the command invocation. Therefore, the .xpt won't + # be rebuilt because the dependencies say it is up to date. By + # listing the .idls here, we ensure the make file has a + # reference to the new .idl. Since the new .idl presumably has + # an mtime newer than the .xpt, it will trigger xpt generation. + xpt_path = '$(DEPTH)/%s/components/%s.xpt' % (install_target, module) + xpt_files.add(xpt_path) + rule = mk.create_rule([xpt_path]) + rule.add_dependencies(['$(call mkdir_deps,%s)' % mozpath.dirname(xpt_path)]) + rule.add_dependencies(manager.idls['%s.idl' % dep]['source'] for dep in deps) + + if install_target.startswith('dist/'): + path = mozpath.relpath(xpt_path, '$(DEPTH)/dist') + prefix, subpath = path.split('/', 1) + key = 'dist_%s' % prefix + + self._install_manifests[key].add_optional_exists(subpath) + + rules = StringIO() + mk.dump(rules, removal_guard=False) # Create dependency for output header so we force regeneration if the # header was deleted. This ideally should not be necessary. However, @@ -1037,8 +1047,9 @@ INSTALL_TARGETS += %(prefix)s obj.topobjdir = self.environment.topobjdir obj.config = self.environment self._create_makefile(obj, extra=dict( - xpidl_rules='\n'.join(rules), + xpidl_rules=rules.getvalue(), xpidl_modules=' '.join(xpt_modules), + xpt_files=' '.join(sorted(xpt_files)), )) def _process_program(self, program, backend_file): diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py index e0cd58fb3acf..9b99dbba20ed 100644 --- a/python/mozbuild/mozbuild/frontend/context.py +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -1161,7 +1161,7 @@ VARIABLES = { Files from topsrcdir and the objdir can also be installed by prefixing the path(s) with a '/' character and a '!' character, respectively:: TEST_HARNESS_FILES.path += ['/build/bar.py', '!quux.py'] - """, None), + """, 'libs'), } # Sanity check: we don't want any variable above to have a list as storage type. diff --git a/python/mozbuild/mozbuild/frontend/data.py b/python/mozbuild/mozbuild/frontend/data.py index ddb17920e548..3c88785c1127 100644 --- a/python/mozbuild/mozbuild/frontend/data.py +++ b/python/mozbuild/mozbuild/frontend/data.py @@ -157,6 +157,7 @@ class XPIDLFile(ContextDerived): __slots__ = ( 'basename', + 'install_target', 'source_path', ) @@ -167,6 +168,8 @@ class XPIDLFile(ContextDerived): self.basename = mozpath.basename(source) self.module = module + self.install_target = context['FINAL_TARGET'] + class Defines(ContextDerived): """Context derived container object for DEFINES, which is an OrderedDict. """ diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py index f3f087e31dcc..6a7604c10278 100644 --- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -345,6 +345,14 @@ class TreeMetadataEmitter(LoggingMixin): This is a generator of mozbuild.frontend.data.ContextDerived instances. """ + + # We only want to emit an InstallationTarget if one of the consulted + # variables is defined. Later on, we look up FINAL_TARGET, which has + # the side-effect of populating it. So, we need to do this lookup + # early. + if any(k in context for k in ('FINAL_TARGET', 'XPI_NAME', 'DIST_SUBDIR')): + yield InstallationTarget(context) + # We always emit a directory traversal descriptor. This is needed by # the recursive make backend. for o in self._emit_directory_traversal_from_context(context): yield o @@ -523,9 +531,9 @@ class TreeMetadataEmitter(LoggingMixin): for s in strings: if context.is_objdir_path(s): if s.startswith('!/'): - raise SandboxValidationError( - 'Topobjdir-relative file not allowed in TEST_HARNESS_FILES: %s' % s, context) - objdir_files[path].append(s[1:]) + objdir_files[path].append('$(DEPTH)/%s' % s[2:]) + else: + objdir_files[path].append(s[1:]) else: resolved = context.resolve_path(s) if '*' in s: @@ -616,10 +624,6 @@ class TreeMetadataEmitter(LoggingMixin): 'does not exist: %s (resolved to %s)' % (local_include, actual_include), context) yield LocalInclude(context, local_include) - if context.get('FINAL_TARGET') or context.get('XPI_NAME') or \ - context.get('DIST_SUBDIR'): - yield InstallationTarget(context) - final_target_files = context.get('FINAL_TARGET_FILES') if final_target_files: yield FinalTargetFiles(context, final_target_files, context['FINAL_TARGET']) diff --git a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py index 08dada9a8803..8d045c85bfdc 100644 --- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py +++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py @@ -443,7 +443,9 @@ class TestRecursiveMakeBackend(BackendTester): m = InstallManifest(path=mozpath.join(install_dir, 'xpidl')) self.assertIn('.deps/my_module.pp', m) - self.assertIn('xpt/my_module.xpt', m) + + m = InstallManifest(path=os.path.join(install_dir, 'dist_bin')) + self.assertIn('components/my_module.xpt', m) m = InstallManifest(path=mozpath.join(install_dir, 'dist_include')) self.assertIn('foo.h', m) diff --git a/toolkit/modules/PageMenu.jsm b/toolkit/modules/PageMenu.jsm index 7d5823f8557b..d36120b6b0fe 100644 --- a/toolkit/modules/PageMenu.jsm +++ b/toolkit/modules/PageMenu.jsm @@ -2,7 +2,7 @@ * 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/. */ -this.EXPORTED_SYMBOLS = ["PageMenu"]; +this.EXPORTED_SYMBOLS = ["PageMenuParent", "PageMenuChild"]; this.PageMenu = function PageMenu() { } @@ -11,46 +11,71 @@ PageMenu.prototype = { PAGEMENU_ATTR: "pagemenu", GENERATEDITEMID_ATTR: "generateditemid", - popup: null, - builder: null, + _popup: null, - maybeBuildAndAttachMenu: function(aTarget, aPopup) { - var pageMenu = null; - var target = aTarget; + // Only one of builder or browser will end up getting set. + _builder: null, + _browser: null, + + // Given a target node, get the context menu for it or its ancestor. + getContextMenu: function(aTarget) { + let pageMenu = null; + let target = aTarget; while (target) { - var contextMenu = target.contextMenu; + let contextMenu = target.contextMenu; if (contextMenu) { - pageMenu = contextMenu; - break; + return contextMenu; } target = target.parentNode; } - if (!pageMenu) { - return false; - } + return null; + }, - var insertionPoint = this.getInsertionPoint(aPopup); - if (!insertionPoint) { - return false; + // Given a target node, generate a JSON object for any context menu + // associated with it, or null if there is no context menu. + maybeBuild: function(aTarget) { + let pageMenu = this.getContextMenu(aTarget); + if (!pageMenu) { + return null; } pageMenu.QueryInterface(Components.interfaces.nsIHTMLMenu); pageMenu.sendShowEvent(); // the show event is not cancelable, so no need to check a result here - var fragment = aPopup.ownerDocument.createDocumentFragment(); + this._builder = pageMenu.createBuilder(); + if (!this._builder) { + return null; + } - var builder = pageMenu.createBuilder(); - if (!builder) { + pageMenu.build(this._builder); + + // This serializes then parses again, however this could be avoided in + // the single-process case with further improvement. + let menuString = this._builder.toJSONString(); + if (!menuString) { + return null; + } + + return JSON.parse(menuString); + }, + + // Given a JSON menu object and popup, add the context menu to the popup. + buildAndAttachMenuWithObject: function(aMenu, aBrowser, aPopup) { + if (!aMenu) { return false; } - builder.QueryInterface(Components.interfaces.nsIXULContextMenuBuilder); - builder.init(fragment, this.GENERATEDITEMID_ATTR); - pageMenu.build(builder); + let insertionPoint = this.getInsertionPoint(aPopup); + if (!insertionPoint) { + return false; + } - var pos = insertionPoint.getAttribute(this.PAGEMENU_ATTR); + let fragment = aPopup.ownerDocument.createDocumentFragment(); + this.buildXULMenu(aMenu, fragment); + + let pos = insertionPoint.getAttribute(this.PAGEMENU_ATTR); if (pos == "start") { insertionPoint.insertBefore(fragment, insertionPoint.firstChild); @@ -60,33 +85,101 @@ PageMenu.prototype = { insertionPoint.appendChild(fragment); } - this.builder = builder; - this.popup = aPopup; + this._browser = aBrowser; + this._popup = aPopup; - this.popup.addEventListener("command", this); - this.popup.addEventListener("popuphidden", this); + this._popup.addEventListener("command", this); + this._popup.addEventListener("popuphidden", this); return true; }, - handleEvent: function(event) { - var type = event.type; - var target = event.target; - if (type == "command" && target.hasAttribute(this.GENERATEDITEMID_ATTR)) { - this.builder.click(target.getAttribute(this.GENERATEDITEMID_ATTR)); - } else if (type == "popuphidden" && this.popup == target) { - this.removeGeneratedContent(this.popup); + // Construct the XUL menu structure for a given JSON object. + buildXULMenu: function(aNode, aElementForAppending) { + let document = aElementForAppending.ownerDocument; - this.popup.removeEventListener("popuphidden", this); - this.popup.removeEventListener("command", this); + let children = aNode.children; + for (let child of children) { + let menuitem; + switch (child.type) { + case "menuitem": + if (!child.id) { + continue; // Ignore children without ids + } - this.popup = null; - this.builder = null; + menuitem = document.createElement("menuitem"); + if (child.checkbox) { + menuitem.setAttribute("type", "checkbox"); + if (child.checked) { + menuitem.setAttribute("checked", "true"); + } + } + + if (child.label) { + menuitem.setAttribute("label", child.label); + } + if (child.icon) { + menuitem.setAttribute("image", child.icon); + menuitem.className = "menuitem-iconic"; + } + if (child.disabled) { + menuitem.setAttribute("disabled", true); + } + + break; + + case "separator": + menuitem = document.createElement("menuseparator"); + break; + + case "menu": + menuitem = document.createElement("menu"); + if (child.label) { + menuitem.setAttribute("label", child.label); + } + + let menupopup = document.createElement("menupopup"); + menuitem.appendChild(menupopup); + + this.buildXULMenu(child, menupopup); + break; + } + + menuitem.setAttribute(this.GENERATEDITEMID_ATTR, child.id ? child.id : 0); + aElementForAppending.appendChild(menuitem); } }, + // Called when the generated menuitem is executed. + handleEvent: function(event) { + let type = event.type; + let target = event.target; + if (type == "command" && target.hasAttribute(this.GENERATEDITEMID_ATTR)) { + // If a builder is assigned, call click on it directly. Otherwise, this is + // likely a menu with data from another process, so send a message to the + // browser to execute the menuitem. + if (this._builder) { + this._builder.click(target.getAttribute(this.GENERATEDITEMID_ATTR)); + } + else if (this._browser) { + this._browser.messageManager.sendAsyncMessage("ContextMenu:DoCustomCommand", + target.getAttribute(this.GENERATEDITEMID_ATTR)); + } + } else if (type == "popuphidden" && this._popup == target) { + this.removeGeneratedContent(this._popup); + + this._popup.removeEventListener("popuphidden", this); + this._popup.removeEventListener("command", this); + + this._popup = null; + this._builder = null; + this._browser = null; + } + }, + + // Get the first child of the given element with the given tag name. getImmediateChild: function(element, tag) { - var child = element.firstChild; + let child = element.firstChild; while (child) { if (child.localName == tag) { return child; @@ -96,16 +189,19 @@ PageMenu.prototype = { return null; }, + // Return the location where the generated items should be inserted into the + // given popup. They should be inserted as the next sibling of the returned + // element. getInsertionPoint: function(aPopup) { if (aPopup.hasAttribute(this.PAGEMENU_ATTR)) return aPopup; - var element = aPopup.firstChild; + let element = aPopup.firstChild; while (element) { if (element.localName == "menu") { - var popup = this.getImmediateChild(element, "menupopup"); + let popup = this.getImmediateChild(element, "menupopup"); if (popup) { - var result = this.getInsertionPoint(popup); + let result = this.getInsertionPoint(popup); if (result) { return result; } @@ -117,19 +213,20 @@ PageMenu.prototype = { return null; }, + // Remove the generated content from the given popup. removeGeneratedContent: function(aPopup) { - var ungenerated = []; + let ungenerated = []; ungenerated.push(aPopup); - var count; + let count; while (0 != (count = ungenerated.length)) { - var last = count - 1; - var element = ungenerated[last]; + let last = count - 1; + let element = ungenerated[last]; ungenerated.splice(last, 1); - var i = element.childNodes.length; + let i = element.childNodes.length; while (i-- > 0) { - var child = element.childNodes[i]; + let child = element.childNodes[i]; if (!child.hasAttribute(this.GENERATEDITEMID_ATTR)) { ungenerated.push(child); continue; @@ -139,3 +236,78 @@ PageMenu.prototype = { } } } + +// This object is expected to be used from a parent process. +this.PageMenuParent = function PageMenuParent() { +} + +PageMenuParent.prototype = { + __proto__ : PageMenu.prototype, + + /* + * Given a target node and popup, add the context menu to the popup. This is + * intended to be called when a single process is used. This is equivalent to + * calling PageMenuChild.build and PageMenuParent.addToPopup in sequence. + * + * Returns true if custom menu items were present. + */ + buildAndAddToPopup: function(aTarget, aPopup) { + let menuObject = this.maybeBuild(aTarget); + if (!menuObject) { + return false; + } + + return this.buildAndAttachMenuWithObject(menuObject, null, aPopup); + }, + + /* + * Given a JSON menu object and popup, add the context menu to the popup. This + * is intended to be called when the child page is in a different process. + * aBrowser should be the browser containing the page the context menu is + * displayed for, which may be null. + * + * Returns true if custom menu items were present. + */ + addToPopup: function(aMenu, aBrowser, aPopup) { + return this.buildAndAttachMenuWithObject(aMenu, aBrowser, aPopup); + } +} + +// This object is expected to be used from a child process. +this.PageMenuChild = function PageMenuChild() { +} + +PageMenuChild.prototype = { + __proto__ : PageMenu.prototype, + + /* + * Given a target node, return a JSON object for the custom menu commands. The + * object will consist of a hierarchical structure of menus, menuitems or + * separators. Supported properties of each are: + * Menu: children, label, type="menu" + * Menuitems: checkbox, checked, disabled, icon, label, type="menuitem" + * Separators: type="separator" + * + * In addition, the id of each item will be used to identify the item + * when it is executed. The type will either be 'menu', 'menuitem' or + * 'separator'. The toplevel node will be a menu with a children property. The + * children property of a menu is an array of zero or more other items. + * + * If there is no menu associated with aTarget, null will be returned. + */ + build: function(aTarget) { + return this.maybeBuild(aTarget); + }, + + /* + * Given the id of a menu, execute the command associated with that menu. It + * is assumed that only one command will be executed so the builder is + * cleared afterwards. + */ + executeMenu: function(aId) { + if (this._builder) { + this._builder.click(aId); + this._builder = null; + } + } +} diff --git a/widget/gonk/nativewindow/FakeSurfaceComposer.cpp b/widget/gonk/nativewindow/FakeSurfaceComposer.cpp index 82297ae9f2a6..1b34761a9d82 100644 --- a/widget/gonk/nativewindow/FakeSurfaceComposer.cpp +++ b/widget/gonk/nativewindow/FakeSurfaceComposer.cpp @@ -23,6 +23,10 @@ #include #include +#if ANDROID_VERSION >= 21 +#include +#endif + #include "FakeSurfaceComposer.h" namespace android { @@ -89,31 +93,84 @@ sp FakeSurfaceComposer::createDisplayEventConnection() return nullptr; } -status_t FakeSurfaceComposer::captureScreen(const sp& display, - const sp& producer, - uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, - bool isCpuConsumer) { +status_t +FakeSurfaceComposer::captureScreen(const sp& display + , const sp& producer +#if ANDROID_VERSION >= 21 + , Rect sourceCrop +#endif + , uint32_t reqWidth + , uint32_t reqHeight + , uint32_t minLayerZ + , uint32_t maxLayerZ +#if ANDROID_VERSION >= 21 + , bool useIdentityTransform + , Rotation rotation +#elif ANDROID_VERSION < 19 + , bool isCpuConsumer +#endif + ) +{ return INVALID_OPERATION; } -#if ANDROID_VERSION >= 19 -status_t FakeSurfaceComposer::captureScreen(const sp& display, - const sp& producer, - uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ) { +#if ANDROID_VERSION >= 21 +void +FakeSurfaceComposer::setPowerMode(const sp& display, int mode) +{ +} + +status_t +FakeSurfaceComposer::getDisplayConfigs(const sp& display, Vector* configs) +{ + return INVALID_OPERATION; +} + +status_t +FakeSurfaceComposer::getDisplayStats(const sp& display, DisplayStatInfo* stats) +{ + return INVALID_OPERATION; +} + +int +FakeSurfaceComposer::getActiveConfig(const sp& display) +{ + return INVALID_OPERATION; +} + +status_t +FakeSurfaceComposer::setActiveConfig(const sp& display, int id) +{ + return INVALID_OPERATION; +} + +status_t +FakeSurfaceComposer::clearAnimationFrameStats() +{ + return INVALID_OPERATION; +} + +status_t +FakeSurfaceComposer::getAnimationFrameStats(FrameStats* outStats) const +{ + return INVALID_OPERATION; +} +#else +void +FakeSurfaceComposer::blank(const sp& display) +{ +} + +void +FakeSurfaceComposer::unblank(const sp& display) +{ +} + +status_t +FakeSurfaceComposer::getDisplayInfo(const sp& display, DisplayInfo* info) +{ return INVALID_OPERATION; } #endif -void FakeSurfaceComposer::blank(const sp& display) { -} - -void FakeSurfaceComposer::unblank(const sp& display) { -} - -status_t FakeSurfaceComposer::getDisplayInfo(const sp& display, DisplayInfo* info) { - return INVALID_OPERATION; -} - }; // namespace android diff --git a/widget/gonk/nativewindow/FakeSurfaceComposer.h b/widget/gonk/nativewindow/FakeSurfaceComposer.h index ae47b5c0cb1d..9f6bab654bae 100644 --- a/widget/gonk/nativewindow/FakeSurfaceComposer.h +++ b/widget/gonk/nativewindow/FakeSurfaceComposer.h @@ -45,10 +45,28 @@ public: // Instantiate MediaResourceManagerService and register to service manager. // If service manager is not present, wait until service manager becomes present. static void instantiate(); + #if ANDROID_VERSION >= 19 virtual void destroyDisplay(const sp& display); - virtual status_t captureScreen(const sp& display, const sp& producer, - uint32_t reqWidth, uint32_t reqHeight, uint32_t minLayerZ, uint32_t maxLayerZ); +#endif + +#if ANDROID_VERSION >= 21 + virtual status_t captureScreen(const sp& display, + const sp& producer, + Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ, + bool useIdentityTransform, + Rotation rotation = eRotateNone); +#elif ANDROID_VERSION >= 19 + virtual status_t captureScreen(const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ); +#else + virtual status_t captureScreen(const sp& display, + const sp& producer, + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ, bool isCpuConsumer); #endif private: @@ -69,15 +87,21 @@ private: virtual bool authenticateSurfaceTexture( const sp& bufferProducer) const; virtual sp createDisplayEventConnection(); - virtual status_t captureScreen(const sp& display, - const sp& producer, - uint32_t reqWidth, uint32_t reqHeight, - uint32_t minLayerZ, uint32_t maxLayerZ, bool isCpuConsumer); +#if ANDROID_VERSION >= 21 + virtual void setPowerMode(const sp& display, int mode); + virtual status_t getDisplayConfigs(const sp& display, Vector* configs); + virtual status_t getDisplayStats(const sp& display, DisplayStatInfo* stats); + virtual int getActiveConfig(const sp& display); + virtual status_t setActiveConfig(const sp& display, int id); + virtual status_t clearAnimationFrameStats(); + virtual status_t getAnimationFrameStats(FrameStats* outStats) const; +#elif ANDROID_VERSION >= 17 // called when screen needs to turn off virtual void blank(const sp& display); // called when screen is turning back on virtual void unblank(const sp& display); virtual status_t getDisplayInfo(const sp& display, DisplayInfo* info); +#endif }; diff --git a/widget/gonk/nativewindow/GonkBufferQueue.h b/widget/gonk/nativewindow/GonkBufferQueue.h old mode 100755 new mode 100644 index 75cedbef12d3..defdb0ae2f1a --- a/widget/gonk/nativewindow/GonkBufferQueue.h +++ b/widget/gonk/nativewindow/GonkBufferQueue.h @@ -13,7 +13,9 @@ * limitations under the License. */ -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21 +# include "GonkBufferQueueLL.h" +#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 # include "GonkBufferQueueKK.h" #elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 # include "GonkBufferQueueJB.h" diff --git a/widget/gonk/nativewindow/GonkBufferQueueJB.cpp b/widget/gonk/nativewindow/GonkBufferQueueJB.cpp old mode 100755 new mode 100644 diff --git a/widget/gonk/nativewindow/GonkBufferQueueJB.h b/widget/gonk/nativewindow/GonkBufferQueueJB.h old mode 100755 new mode 100644 diff --git a/widget/gonk/nativewindow/GonkBufferQueueKK.h b/widget/gonk/nativewindow/GonkBufferQueueKK.h index 851b63f9a23c..0209de02c51f 100644 --- a/widget/gonk/nativewindow/GonkBufferQueueKK.h +++ b/widget/gonk/nativewindow/GonkBufferQueueKK.h @@ -334,7 +334,6 @@ private: // freeAllBuffersLocked frees the GraphicBuffer and sync resources for // all slots. - //void freeAllBuffersLocked(); void freeAllBuffersLocked(); // setDefaultMaxBufferCountLocked sets the maximum number of buffer slots diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.cpp new file mode 100644 index 000000000000..7df72bf68e31 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.cpp @@ -0,0 +1,193 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#include "GonkBufferItem.h" + +#include +#include + +#include + +namespace android { + +GonkBufferItem::GonkBufferItem() : + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mIsAutoTimestamp(false), + mFrameNumber(0), + mSlot(INVALID_BUFFER_SLOT), + mIsDroppable(false), + mAcquireCalled(false), + mTransformToDisplayInverse(false) { + mCrop.makeInvalid(); +} + +GonkBufferItem::operator IGonkGraphicBufferConsumer::BufferItem() const { + IGonkGraphicBufferConsumer::BufferItem bufferItem; + bufferItem.mGraphicBuffer = mGraphicBuffer; + bufferItem.mFence = mFence; + bufferItem.mCrop = mCrop; + bufferItem.mTransform = mTransform; + bufferItem.mScalingMode = mScalingMode; + bufferItem.mTimestamp = mTimestamp; + bufferItem.mIsAutoTimestamp = mIsAutoTimestamp; + bufferItem.mFrameNumber = mFrameNumber; + bufferItem.mBuf = mSlot; + bufferItem.mIsDroppable = mIsDroppable; + bufferItem.mAcquireCalled = mAcquireCalled; + bufferItem.mTransformToDisplayInverse = mTransformToDisplayInverse; + return bufferItem; +} + +size_t GonkBufferItem::getPodSize() const { + size_t c = sizeof(mCrop) + + sizeof(mTransform) + + sizeof(mScalingMode) + + sizeof(mTimestamp) + + sizeof(mIsAutoTimestamp) + + sizeof(mFrameNumber) + + sizeof(mSlot) + + sizeof(mIsDroppable) + + sizeof(mAcquireCalled) + + sizeof(mTransformToDisplayInverse); + return c; +} + +size_t GonkBufferItem::getFlattenedSize() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFlattenedSize(); + FlattenableUtils::align<4>(c); + } + if (mFence != 0) { + c += mFence->getFlattenedSize(); + FlattenableUtils::align<4>(c); + } + return sizeof(int32_t) + c + getPodSize(); +} + +size_t GonkBufferItem::getFdCount() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFdCount(); + } + if (mFence != 0) { + c += mFence->getFdCount(); + } + return c; +} + +status_t GonkBufferItem::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + + // make sure we have enough space + if (count < GonkBufferItem::getFlattenedSize()) { + return NO_MEMORY; + } + + // content flags are stored first + uint32_t& flags = *static_cast(buffer); + + // advance the pointer + FlattenableUtils::advance(buffer, size, sizeof(uint32_t)); + + flags = 0; + if (mGraphicBuffer != 0) { + status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 1; + } + if (mFence != 0) { + status_t err = mFence->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 2; + } + + // check we have enough space (in case flattening the fence/graphicbuffer lied to us) + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, mCrop); + FlattenableUtils::write(buffer, size, mTransform); + FlattenableUtils::write(buffer, size, mScalingMode); + FlattenableUtils::write(buffer, size, mTimestamp); + FlattenableUtils::write(buffer, size, mIsAutoTimestamp); + FlattenableUtils::write(buffer, size, mFrameNumber); + FlattenableUtils::write(buffer, size, mSlot); + FlattenableUtils::write(buffer, size, mIsDroppable); + FlattenableUtils::write(buffer, size, mAcquireCalled); + FlattenableUtils::write(buffer, size, mTransformToDisplayInverse); + + return NO_ERROR; +} + +status_t GonkBufferItem::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + + if (size < sizeof(uint32_t)) + return NO_MEMORY; + + uint32_t flags = 0; + FlattenableUtils::read(buffer, size, flags); + + if (flags & 1) { + mGraphicBuffer = new GraphicBuffer(); + status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + if (flags & 2) { + mFence = new Fence(); + status_t err = mFence->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + // check we have enough space + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, mCrop); + FlattenableUtils::read(buffer, size, mTransform); + FlattenableUtils::read(buffer, size, mScalingMode); + FlattenableUtils::read(buffer, size, mTimestamp); + FlattenableUtils::read(buffer, size, mIsAutoTimestamp); + FlattenableUtils::read(buffer, size, mFrameNumber); + FlattenableUtils::read(buffer, size, mSlot); + FlattenableUtils::read(buffer, size, mIsDroppable); + FlattenableUtils::read(buffer, size, mAcquireCalled); + FlattenableUtils::read(buffer, size, mTransformToDisplayInverse); + + return NO_ERROR; +} + +const char* GonkBufferItem::scalingModeName(uint32_t scalingMode) { + switch (scalingMode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE"; + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW"; + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP"; + default: return "Unknown"; + } +} + +} // namespace android diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.h new file mode 100644 index 000000000000..b2d6d3068125 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferItem.h @@ -0,0 +1,101 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_GONKBUFFERITEM_LL_H +#define NATIVEWINDOW_GONKBUFFERITEM_LL_H + +#include "IGonkGraphicBufferConsumerLL.h" + +#include + +#include +#include + +namespace android { + +class Fence; +class GraphicBuffer; + +class GonkBufferItem : public Flattenable { + friend class Flattenable; + size_t getPodSize() const; + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + public: + // The default value of mBuf, used to indicate this doesn't correspond to a slot. + enum { INVALID_BUFFER_SLOT = -1 }; + GonkBufferItem(); + operator IGonkGraphicBufferConsumer::BufferItem() const; + + static const char* scalingModeName(uint32_t scalingMode); + + // mGraphicBuffer points to the buffer allocated for this slot, or is NULL + // if the buffer in this slot has been acquired in the past (see + // BufferSlot.mAcquireCalled). + sp mGraphicBuffer; + + // mFence is a fence that will signal when the buffer is idle. + sp mFence; + + // mCrop is the current crop rectangle for this buffer slot. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. + // refer to NATIVE_WINDOW_TRANSFORM_* in + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. + // refer to NATIVE_WINDOW_SCALING_* in + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. This value + // is guaranteed to be monotonically increasing for each newly + // acquired buffer. + int64_t mTimestamp; + + // mIsAutoTimestamp indicates whether mTimestamp was generated + // automatically when the buffer was queued. + bool mIsAutoTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + + // mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT). + int mSlot; + + // mIsDroppable whether this buffer was queued with the + // property that it can be replaced by a new buffer for the purpose of + // making sure dequeueBuffer() won't block. + // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer + // was queued. + bool mIsDroppable; + + // Indicates whether this buffer has been seen by a consumer yet + bool mAcquireCalled; + + // Indicates this buffer must be transformed by the inverse transform of the screen + // it is displayed onto. This is applied after mTransform. + bool mTransformToDisplayInverse; +}; + +} // namespace android + +#endif diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.cpp new file mode 100644 index 000000000000..b9543ed48acd --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.cpp @@ -0,0 +1,557 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#include + +#define LOG_TAG "GonkBufferQueueConsumer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#include "GonkBufferItem.h" +#include "GonkBufferQueueConsumer.h" +#include "GonkBufferQueueCore.h" +#include +#include + +namespace android { + +GonkBufferQueueConsumer::GonkBufferQueueConsumer(const sp& core) : + mCore(core), + mSlots(core->mSlots), + mConsumerName() {} + +GonkBufferQueueConsumer::~GonkBufferQueueConsumer() {} + +status_t GonkBufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, + nsecs_t expectedPresent) { + ATRACE_CALL(); + Mutex::Autolock lock(mCore->mMutex); + + // Check that the consumer doesn't currently have the maximum number of + // buffers acquired. We allow the max buffer count to be exceeded by one + // buffer so that the consumer can successfully set up the newly acquired + // buffer before releasing the old one. + int numAcquiredBuffers = 0; + for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (mSlots[s].mBufferState == GonkBufferSlot::ACQUIRED) { + ++numAcquiredBuffers; + } + } + if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) { + ALOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)", + numAcquiredBuffers, mCore->mMaxAcquiredBufferCount); + return INVALID_OPERATION; + } + + // Check if the queue is empty. + // In asynchronous mode the list is guaranteed to be one buffer deep, + // while in synchronous mode we use the oldest buffer. + if (mCore->mQueue.empty()) { + return NO_BUFFER_AVAILABLE; + } + + GonkBufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); + + // If expectedPresent is specified, we may not want to return a buffer yet. + // If it's specified and there's more than one buffer queued, we may want + // to drop a buffer. + if (expectedPresent != 0) { + const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second + + // The 'expectedPresent' argument indicates when the buffer is expected + // to be presented on-screen. If the buffer's desired present time is + // earlier (less) than expectedPresent -- meaning it will be displayed + // on time or possibly late if we show it as soon as possible -- we + // acquire and return it. If we don't want to display it until after the + // expectedPresent time, we return PRESENT_LATER without acquiring it. + // + // To be safe, we don't defer acquisition if expectedPresent is more + // than one second in the future beyond the desired present time + // (i.e., we'd be holding the buffer for a long time). + // + // NOTE: Code assumes monotonic time values from the system clock + // are positive. + + // Start by checking to see if we can drop frames. We skip this check if + // the timestamps are being auto-generated by Surface. If the app isn't + // generating timestamps explicitly, it probably doesn't want frames to + // be discarded based on them. + while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) { + // If entry[1] is timely, drop entry[0] (and repeat). We apply an + // additional criterion here: we only drop the earlier buffer if our + // desiredPresent falls within +/- 1 second of the expected present. + // Otherwise, bogus desiredPresent times (e.g., 0 or a small + // relative timestamp), which normally mean "ignore the timestamp + // and acquire immediately", would cause us to drop frames. + // + // We may want to add an additional criterion: don't drop the + // earlier buffer if entry[1]'s fence hasn't signaled yet. + const BufferItem& bufferItem(mCore->mQueue[1]); + nsecs_t desiredPresent = bufferItem.mTimestamp; + if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC || + desiredPresent > expectedPresent) { + // This buffer is set to display in the near future, or + // desiredPresent is garbage. Either way we don't want to drop + // the previous buffer just to get this on the screen sooner. + ALOGV("acquireBuffer: nodrop desire=%" PRId64 " expect=%" + PRId64 " (%" PRId64 ") now=%" PRId64, + desiredPresent, expectedPresent, + desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + break; + } + + ALOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64 + " size=%zu", + desiredPresent, expectedPresent, mCore->mQueue.size()); + if (mCore->stillTracking(front)) { + // Front buffer is still in mSlots, so mark the slot as free + mSlots[front->mSlot].mBufferState = GonkBufferSlot::FREE; + } + mCore->mQueue.erase(front); + front = mCore->mQueue.begin(); + } + + // See if the front buffer is due + nsecs_t desiredPresent = front->mTimestamp; + if (desiredPresent > expectedPresent && + desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) { + ALOGV("acquireBuffer: defer desire=%" PRId64 " expect=%" PRId64 + " (%" PRId64 ") now=%" PRId64, + desiredPresent, expectedPresent, + desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + return PRESENT_LATER; + } + + ALOGV("acquireBuffer: accept desire=%" PRId64 " expect=%" PRId64 " " + "(%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent, + desiredPresent - expectedPresent, + systemTime(CLOCK_MONOTONIC)); + } + + int slot = front->mSlot; + //*outBuffer = *front; + outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer; + outBuffer->mFrameNumber = mSlots[slot].mFrameNumber; + outBuffer->mBuf = slot; + outBuffer->mFence = mSlots[slot].mFence; + + ATRACE_BUFFER_INDEX(slot); + + ALOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }", + slot, front->mFrameNumber, front->mGraphicBuffer->handle); + // If the front buffer is still being tracked, update its slot state + if (mCore->stillTracking(front)) { + mSlots[slot].mAcquireCalled = true; + mSlots[slot].mNeedsCleanupOnRelease = false; + mSlots[slot].mBufferState = GonkBufferSlot::ACQUIRED; + mSlots[slot].mFence = Fence::NO_FENCE; + } + + // If the buffer has previously been acquired by the consumer, set + // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer + // on the consumer side + //if (outBuffer->mAcquireCalled) { + // outBuffer->mGraphicBuffer = NULL; + //} + + mCore->mQueue.erase(front); + + // We might have freed a slot while dropping old buffers, or the producer + // may be blocked waiting for the number of buffers in the queue to + // decrease. + mCore->mDequeueCondition.broadcast(); + + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::detachBuffer(int slot) { + ATRACE_CALL(); + ATRACE_BUFFER_INDEX(slot); + ALOGV("detachBuffer(C): slot %d", slot); + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + ALOGE("detachBuffer(C): GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) { + ALOGE("detachBuffer(C): slot index %d out of range [0, %d)", + slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS); + return BAD_VALUE; + } else if (mSlots[slot].mBufferState != GonkBufferSlot::ACQUIRED) { + ALOGE("detachBuffer(C): slot %d is not owned by the consumer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return BAD_VALUE; + } + + mCore->freeBufferLocked(slot); + mCore->mDequeueCondition.broadcast(); + + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::attachBuffer(int* outSlot, + const sp& buffer) { + ATRACE_CALL(); + + if (outSlot == NULL) { + ALOGE("attachBuffer(P): outSlot must not be NULL"); + return BAD_VALUE; + } else if (buffer == NULL) { + ALOGE("attachBuffer(P): cannot attach NULL buffer"); + return BAD_VALUE; + } + + Mutex::Autolock lock(mCore->mMutex); + + // Make sure we don't have too many acquired buffers and find a free slot + // to put the buffer into (the oldest if there are multiple). + int numAcquiredBuffers = 0; + int found = GonkBufferQueueCore::INVALID_BUFFER_SLOT; + for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (mSlots[s].mBufferState == GonkBufferSlot::ACQUIRED) { + ++numAcquiredBuffers; + } else if (mSlots[s].mBufferState == GonkBufferSlot::FREE) { + if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT || + mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) { + found = s; + } + } + } + + if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) { + ALOGE("attachBuffer(P): max acquired buffer count reached: %d " + "(max %d)", numAcquiredBuffers, + mCore->mMaxAcquiredBufferCount); + return INVALID_OPERATION; + } + if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) { + ALOGE("attachBuffer(P): could not find free buffer slot"); + return NO_MEMORY; + } + + *outSlot = found; + ATRACE_BUFFER_INDEX(*outSlot); + ALOGV("attachBuffer(C): returning slot %d", *outSlot); + + mSlots[*outSlot].mGraphicBuffer = buffer; + mSlots[*outSlot].mBufferState = GonkBufferSlot::ACQUIRED; + mSlots[*outSlot].mAttachedByConsumer = true; + mSlots[*outSlot].mNeedsCleanupOnRelease = false; + mSlots[*outSlot].mFence = Fence::NO_FENCE; + mSlots[*outSlot].mFrameNumber = 0; + + // mAcquireCalled tells GonkBufferQueue that it doesn't need to send a valid + // GraphicBuffer pointer on the next acquireBuffer call, which decreases + // Binder traffic by not un/flattening the GraphicBuffer. However, it + // requires that the consumer maintain a cached copy of the slot <--> buffer + // mappings, which is why the consumer doesn't need the valid pointer on + // acquire. + // + // The StreamSplitter is one of the primary users of the attach/detach + // logic, and while it is running, all buffers it acquires are immediately + // detached, and all buffers it eventually releases are ones that were + // attached (as opposed to having been obtained from acquireBuffer), so it + // doesn't make sense to maintain the slot/buffer mappings, which would + // become invalid for every buffer during detach/attach. By setting this to + // false, the valid GraphicBuffer pointer will always be sent with acquire + // for attached buffers. + mSlots[*outSlot].mAcquireCalled = false; + + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, + const sp& releaseFence) { + ATRACE_CALL(); + + if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS || + releaseFence == NULL) { + return BAD_VALUE; + } + + sp listener; + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + + // If the frame number has changed because the buffer has been reallocated, + // we can ignore this releaseBuffer for the old buffer + //if (frameNumber != mSlots[slot].mFrameNumber) { + // return STALE_BUFFER_SLOT; + //} + + // Make sure this buffer hasn't been queued while acquired by the consumer + GonkBufferQueueCore::Fifo::iterator current(mCore->mQueue.begin()); + while (current != mCore->mQueue.end()) { + if (current->mSlot == slot) { + ALOGE("releaseBuffer: buffer slot %d pending release is " + "currently queued", slot); + return BAD_VALUE; + } + ++current; + } + + if (mSlots[slot].mBufferState == GonkBufferSlot::ACQUIRED) { + mSlots[slot].mFence = releaseFence; + mSlots[slot].mBufferState = GonkBufferSlot::FREE; + listener = mCore->mConnectedProducerListener; + ALOGV("releaseBuffer: releasing slot %d", slot); + } else if (mSlots[slot].mNeedsCleanupOnRelease) { + ALOGV("releaseBuffer: releasing a stale buffer slot %d " + "(state = %d)", slot, mSlots[slot].mBufferState); + mSlots[slot].mNeedsCleanupOnRelease = false; + return STALE_BUFFER_SLOT; + } else { + ALOGV("releaseBuffer: attempted to release buffer slot %d " + "but its state was %d", slot, mSlots[slot].mBufferState); + return BAD_VALUE; + } + + mCore->mDequeueCondition.broadcast(); + } // Autolock scope + + // Call back without lock held + if (listener != NULL) { + listener->onBufferReleased(); + } + + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::connect( + const sp& consumerListener, bool controlledByApp) { + ATRACE_CALL(); + + if (consumerListener == NULL) { + ALOGE("connect(C): consumerListener may not be NULL"); + return BAD_VALUE; + } + + ALOGV("connect(C): controlledByApp=%s", + controlledByApp ? "true" : "false"); + + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + ALOGE("connect(C): GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + mCore->mConsumerListener = consumerListener; + mCore->mConsumerControlledByApp = controlledByApp; + + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::disconnect() { + ATRACE_CALL(); + + ALOGV("disconnect(C)"); + + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mConsumerListener == NULL) { + ALOGE("disconnect(C): no consumer is connected"); + return BAD_VALUE; + } + + mCore->mIsAbandoned = true; + mCore->mConsumerListener = NULL; + mCore->mQueue.clear(); + mCore->freeAllBuffersLocked(); + mCore->mDequeueCondition.broadcast(); + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::getReleasedBuffers(uint64_t *outSlotMask) { + ATRACE_CALL(); + + if (outSlotMask == NULL) { + ALOGE("getReleasedBuffers: outSlotMask may not be NULL"); + return BAD_VALUE; + } + + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + ALOGE("getReleasedBuffers: GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + uint64_t mask = 0; + for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (!mSlots[s].mAcquireCalled) { + mask |= (1ULL << s); + } + } + + // Remove from the mask queued buffers for which acquire has been called, + // since the consumer will not receive their buffer addresses and so must + // retain their cached information + GonkBufferQueueCore::Fifo::iterator current(mCore->mQueue.begin()); + while (current != mCore->mQueue.end()) { + if (current->mAcquireCalled) { + mask &= ~(1ULL << current->mSlot); + } + ++current; + } + + ALOGV("getReleasedBuffers: returning mask %#" PRIx64, mask); + *outSlotMask = mask; + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::setDefaultBufferSize(uint32_t width, + uint32_t height) { + ATRACE_CALL(); + + if (width == 0 || height == 0) { + ALOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u " + "height=%u)", width, height); + return BAD_VALUE; + } + + ALOGV("setDefaultBufferSize: width=%u height=%u", width, height); + + Mutex::Autolock lock(mCore->mMutex); + mCore->mDefaultWidth = width; + mCore->mDefaultHeight = height; + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::setDefaultMaxBufferCount(int bufferCount) { + ATRACE_CALL(); + Mutex::Autolock lock(mCore->mMutex); + return mCore->setDefaultMaxBufferCountLocked(bufferCount); +} + +status_t GonkBufferQueueConsumer::disableAsyncBuffer() { + ATRACE_CALL(); + + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mConsumerListener != NULL) { + ALOGE("disableAsyncBuffer: consumer already connected"); + return INVALID_OPERATION; + } + + ALOGV("disableAsyncBuffer"); + mCore->mUseAsyncBuffer = false; + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::setMaxAcquiredBufferCount( + int maxAcquiredBuffers) { + ATRACE_CALL(); + + if (maxAcquiredBuffers < 1 || + maxAcquiredBuffers > GonkBufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) { + ALOGE("setMaxAcquiredBufferCount: invalid count %d", + maxAcquiredBuffers); + return BAD_VALUE; + } + + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mConnectedApi != GonkBufferQueueCore::NO_CONNECTED_API) { + ALOGE("setMaxAcquiredBufferCount: producer is already connected"); + return INVALID_OPERATION; + } + + ALOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers); + mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers; + return NO_ERROR; +} + +void GonkBufferQueueConsumer::setConsumerName(const String8& name) { + ATRACE_CALL(); + ALOGV("setConsumerName: '%s'", name.string()); + Mutex::Autolock lock(mCore->mMutex); + mCore->mConsumerName = name; + mConsumerName = name; +} + +status_t GonkBufferQueueConsumer::setDefaultBufferFormat(uint32_t defaultFormat) { + ATRACE_CALL(); + ALOGV("setDefaultBufferFormat: %u", defaultFormat); + Mutex::Autolock lock(mCore->mMutex); + mCore->mDefaultBufferFormat = defaultFormat; + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::setConsumerUsageBits(uint32_t usage) { + ATRACE_CALL(); + ALOGV("setConsumerUsageBits: %#x", usage); + Mutex::Autolock lock(mCore->mMutex); + mCore->mConsumerUsageBits = usage; + return NO_ERROR; +} + +status_t GonkBufferQueueConsumer::setTransformHint(uint32_t hint) { + ATRACE_CALL(); + ALOGV("setTransformHint: %#x", hint); + Mutex::Autolock lock(mCore->mMutex); + mCore->mTransformHint = hint; + return NO_ERROR; +} + +sp GonkBufferQueueConsumer::getSidebandStream() const { + return mCore->mSidebandStream; +} + +void GonkBufferQueueConsumer::dump(String8& result, const char* prefix) const { + mCore->dump(result, prefix); +} + +TemporaryRef +GonkBufferQueueConsumer::getTextureClientFromBuffer(ANativeWindowBuffer* buffer) +{ + Mutex::Autolock _l(mCore->mMutex); + if (buffer == NULL) { + ALOGE("getSlotFromBufferLocked: encountered NULL buffer"); + return nullptr; + } + + for (int i = 0; i < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; i++) { + if (mSlots[i].mGraphicBuffer != NULL && mSlots[i].mGraphicBuffer->handle == buffer->handle) { + return mSlots[i].mTextureClient; + } + } + ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); + return nullptr; +} + +int +GonkBufferQueueConsumer::getSlotFromTextureClientLocked(GonkBufferSlot::TextureClient* client) const +{ + if (client == NULL) { + ALOGE("getSlotFromBufferLocked: encountered NULL buffer"); + return BAD_VALUE; + } + + for (int i = 0; i < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; i++) { + if (mSlots[i].mTextureClient == client) { + return i; + } + } + ALOGE("getSlotFromBufferLocked: unknown TextureClient: %p", client); + return BAD_VALUE; +} +} // namespace android diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.h new file mode 100644 index 000000000000..1bd49a924672 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueConsumer.h @@ -0,0 +1,173 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_GONKBUFFERQUEUECONSUMER_LL_H +#define NATIVEWINDOW_GONKBUFFERQUEUECONSUMER_LL_H + +#include "GonkBufferQueueDefs.h" +#include "IGonkGraphicBufferConsumerLL.h" + +namespace android { + +class GonkBufferQueueCore; + +class GonkBufferQueueConsumer : public BnGonkGraphicBufferConsumer { + +public: + GonkBufferQueueConsumer(const sp& core); + virtual ~GonkBufferQueueConsumer(); + + // acquireBuffer attempts to acquire ownership of the next pending buffer in + // the GonkBufferQueue. If no buffer is pending then it returns + // NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the + // information about the buffer is returned in BufferItem. If the buffer + // returned had previously been acquired then the BufferItem::mGraphicBuffer + // field of buffer is set to NULL and it is assumed that the consumer still + // holds a reference to the buffer. + // + // If expectedPresent is nonzero, it indicates the time when the buffer + // will be displayed on screen. If the buffer's timestamp is farther in the + // future, the buffer won't be acquired, and PRESENT_LATER will be + // returned. The presentation time is in nanoseconds, and the time base + // is CLOCK_MONOTONIC. + virtual status_t acquireBuffer(BufferItem* outBuffer, + nsecs_t expectedPresent); + + // See IGonkGraphicBufferConsumer::detachBuffer + virtual status_t detachBuffer(int slot); + + // See IGonkGraphicBufferConsumer::attachBuffer + virtual status_t attachBuffer(int* slot, const sp& buffer); + + // releaseBuffer releases a buffer slot from the consumer back to the + // GonkBufferQueue. This may be done while the buffer's contents are still + // being accessed. The fence will signal when the buffer is no longer + // in use. frameNumber is used to indentify the exact buffer returned. + // + // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free + // any references to the just-released buffer that it might have, as if it + // had received a onBuffersReleased() call with a mask set for the released + // buffer. + virtual status_t releaseBuffer(int slot, uint64_t frameNumber, + const sp& releaseFence); + + // connect connects a consumer to the GonkBufferQueue. Only one + // consumer may be connected, and when that consumer disconnects the + // GonkBufferQueue is placed into the "abandoned" state, causing most + // interactions with the GonkBufferQueue by the producer to fail. + // controlledByApp indicates whether the consumer is controlled by + // the application. + // + // consumerListener may not be NULL. + virtual status_t connect(const sp& consumerListener, + bool controlledByApp); + + // disconnect disconnects a consumer from the GonkBufferQueue. All + // buffers will be freed and the GonkBufferQueue is placed in the "abandoned" + // state, causing most interactions with the GonkBufferQueue by the producer to + // fail. + virtual status_t disconnect(); + + // getReleasedBuffers sets the value pointed to by outSlotMask to a bit mask + // indicating which buffer slots have been released by the GonkBufferQueue + // but have not yet been released by the consumer. + // + // This should be called from the onBuffersReleased() callback. + virtual status_t getReleasedBuffers(uint64_t* outSlotMask); + + // setDefaultBufferSize is used to set the size of buffers returned by + // dequeueBuffer when a width and height of zero is requested. Default + // is 1x1. + virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height); + + // setDefaultMaxBufferCount sets the default value for the maximum buffer + // count (the initial default is 2). If the producer has requested a + // buffer count using setBufferCount, the default buffer count will only + // take effect if the producer sets the count back to zero. + // + // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + virtual status_t setDefaultMaxBufferCount(int bufferCount); + + // disableAsyncBuffer disables the extra buffer used in async mode + // (when both producer and consumer have set their "isControlledByApp" + // flag) and has dequeueBuffer() return WOULD_BLOCK instead. + // + // This can only be called before connect(). + virtual status_t disableAsyncBuffer(); + + // setMaxAcquiredBufferCount sets the maximum number of buffers that can + // be acquired by the consumer at one time (default 1). This call will + // fail if a producer is connected to the GonkBufferQueue. + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers); + + // setConsumerName sets the name used in logging + virtual void setConsumerName(const String8& name); + + // setDefaultBufferFormat allows the GonkBufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // in dequeueBuffer. Formats are enumerated in graphics.h; the + // initial default is HAL_PIXEL_FORMAT_RGBA_8888. + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat); + + // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. + // These are merged with the bits passed to dequeueBuffer. The values are + // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. + virtual status_t setConsumerUsageBits(uint32_t usage); + + // setTransformHint bakes in rotation to buffers so overlays can be used. + // The values are enumerated in window.h, e.g. + // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). + virtual status_t setTransformHint(uint32_t hint); + + // Retrieve the sideband buffer stream, if any. + virtual sp getSidebandStream() const; + + // dump our state in a String + virtual void dump(String8& result, const char* prefix) const; + + // Added by mozilla + virtual mozilla::TemporaryRef getTextureClientFromBuffer(ANativeWindowBuffer* buffer); + + virtual int getSlotFromTextureClientLocked(GonkBufferSlot::TextureClient* client) const; + + // Functions required for backwards compatibility. + // These will be modified/renamed in IGonkGraphicBufferConsumer and will be + // removed from this class at that time. See b/13306289. + virtual status_t consumerConnect(const sp& consumer, + bool controlledByApp) { + return connect(consumer, controlledByApp); + } + + virtual status_t consumerDisconnect() { return disconnect(); } + + // End functions required for backwards compatibility + +private: + sp mCore; + + // This references mCore->mSlots. Lock mCore->mMutex while accessing. + GonkBufferQueueDefs::SlotsType& mSlots; + + // This is a cached copy of the name stored in the GonkBufferQueueCore. + // It's updated during setConsumerName. + String8 mConsumerName; + +}; // class GonkBufferQueueConsumer + +} // namespace android + +#endif diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.cpp new file mode 100644 index 000000000000..3b539b9c4346 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.cpp @@ -0,0 +1,242 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#define LOG_TAG "GonkBufferQueueCore" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#include + +#include "GonkBufferItem.h" +#include "GonkBufferQueueCore.h" +#include +#include +#include +#include +#include + +#include +#include "mozilla/layers/GrallocTextureClient.h" +#include "mozilla/layers/ImageBridgeChild.h" + +template +static inline T max(T a, T b) { return a > b ? a : b; } + +namespace android { + +static String8 getUniqueName() { + static volatile int32_t counter = 0; + return String8::format("unnamed-%d-%d", getpid(), + android_atomic_inc(&counter)); +} + +GonkBufferQueueCore::GonkBufferQueueCore(const sp& allocator) : + mAllocator(allocator), + mMutex(), + mIsAbandoned(false), + mConsumerControlledByApp(false), + mConsumerName(getUniqueName()), + mConsumerListener(), + mConsumerUsageBits(0), + mConnectedApi(NO_CONNECTED_API), + mConnectedProducerListener(), + mSlots(), + mQueue(), + mOverrideMaxBufferCount(0), + mDequeueCondition(), + mUseAsyncBuffer(true), + mDequeueBufferCannotBlock(false), + mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888), + mDefaultWidth(1), + mDefaultHeight(1), + mDefaultMaxBufferCount(2), + mMaxAcquiredBufferCount(1), + mBufferHasBeenQueued(false), + mFrameCounter(0), + mTransformHint(0), + mIsAllocating(false), + mIsAllocatingCondition() +{ + ALOGV("GonkBufferQueueCore"); +} + +GonkBufferQueueCore::~GonkBufferQueueCore() {} + +void GonkBufferQueueCore::dump(String8& result, const char* prefix) const { + Mutex::Autolock lock(mMutex); + + String8 fifo; + Fifo::const_iterator current(mQueue.begin()); + while (current != mQueue.end()) { + fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], " + "xform=0x%02x, time=%#" PRIx64 ", scale=%s\n", + current->mSlot, current->mGraphicBuffer.get(), + current->mCrop.left, current->mCrop.top, current->mCrop.right, + current->mCrop.bottom, current->mTransform, current->mTimestamp, + GonkBufferItem::scalingModeName(current->mScalingMode)); + ++current; + } + + result.appendFormat("%s-GonkBufferQueue mMaxAcquiredBufferCount=%d, " + "mDequeueBufferCannotBlock=%d, default-size=[%dx%d], " + "default-format=%d, transform-hint=%02x, FIFO(%zu)={%s}\n", + prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock, + mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint, + mQueue.size(), fifo.string()); + + // Trim the free buffers so as to not spam the dump + int maxBufferCount = 0; + for (int s = GonkBufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) { + const GonkBufferSlot& slot(mSlots[s]); + if (slot.mBufferState != GonkBufferSlot::FREE || + slot.mGraphicBuffer != NULL) { + maxBufferCount = s + 1; + break; + } + } + + for (int s = 0; s < maxBufferCount; ++s) { + const GonkBufferSlot& slot(mSlots[s]); + const sp& buffer(slot.mGraphicBuffer); + result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix, + (slot.mBufferState == GonkBufferSlot::ACQUIRED) ? ">" : " ", + s, buffer.get(), + GonkBufferSlot::bufferStateName(slot.mBufferState)); + + if (buffer != NULL) { + result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle, + buffer->width, buffer->height, buffer->stride, + buffer->format); + } + + result.append("\n"); + } +} + +int GonkBufferQueueCore::getMinUndequeuedBufferCountLocked(bool async) const { + // If dequeueBuffer is allowed to error out, we don't have to add an + // extra buffer. + if (!mUseAsyncBuffer) { + return mMaxAcquiredBufferCount; + } + + if (mDequeueBufferCannotBlock || async) { + return mMaxAcquiredBufferCount + 1; + } + + return mMaxAcquiredBufferCount; +} + +int GonkBufferQueueCore::getMinMaxBufferCountLocked(bool async) const { + return getMinUndequeuedBufferCountLocked(async) + 1; +} + +int GonkBufferQueueCore::getMaxBufferCountLocked(bool async) const { + int minMaxBufferCount = getMinMaxBufferCountLocked(async); + + int maxBufferCount = max(mDefaultMaxBufferCount, minMaxBufferCount); + if (mOverrideMaxBufferCount != 0) { + assert(mOverrideMaxBufferCount >= minMaxBufferCount); + maxBufferCount = mOverrideMaxBufferCount; + } + + // Any buffers that are dequeued by the producer or sitting in the queue + // waiting to be consumed need to have their slots preserved. Such buffers + // will temporarily keep the max buffer count up until the slots no longer + // need to be preserved. + for (int s = maxBufferCount; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + GonkBufferSlot::BufferState state = mSlots[s].mBufferState; + if (state == GonkBufferSlot::QUEUED || state == GonkBufferSlot::DEQUEUED) { + maxBufferCount = s + 1; + } + } + + return maxBufferCount; +} + +status_t GonkBufferQueueCore::setDefaultMaxBufferCountLocked(int count) { + const int minBufferCount = 2; + if (count < minBufferCount || count > GonkBufferQueueDefs::NUM_BUFFER_SLOTS) { + ALOGV("setDefaultMaxBufferCount: invalid count %d, should be in " + "[%d, %d]", + count, minBufferCount, GonkBufferQueueDefs::NUM_BUFFER_SLOTS); + return BAD_VALUE; + } + + ALOGV("setDefaultMaxBufferCount: setting count to %d", count); + mDefaultMaxBufferCount = count; + mDequeueCondition.broadcast(); + + return NO_ERROR; +} + +void GonkBufferQueueCore::freeBufferLocked(int slot) { + ALOGV("freeBufferLocked: slot %d", slot); + + if (mSlots[slot].mTextureClient) { + mSlots[slot].mTextureClient->ClearRecycleCallback(); + // release TextureClient in ImageBridge thread + TextureClientReleaseTask* task = new TextureClientReleaseTask(mSlots[slot].mTextureClient); + mSlots[slot].mTextureClient = NULL; + ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(FROM_HERE, task); + } + mSlots[slot].mGraphicBuffer.clear(); + if (mSlots[slot].mBufferState == GonkBufferSlot::ACQUIRED) { + mSlots[slot].mNeedsCleanupOnRelease = true; + } + mSlots[slot].mBufferState = GonkBufferSlot::FREE; + mSlots[slot].mFrameNumber = UINT32_MAX; + mSlots[slot].mAcquireCalled = false; + + // Destroy fence as GonkBufferQueue now takes ownership + mSlots[slot].mFence = Fence::NO_FENCE; +} + +void GonkBufferQueueCore::freeAllBuffersLocked() { + ALOGW_IF(!mQueue.isEmpty(), + "freeAllBuffersLocked called but mQueue is not empty"); + mQueue.clear(); + mBufferHasBeenQueued = false; + for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + freeBufferLocked(s); + } +} + +bool GonkBufferQueueCore::stillTracking(const GonkBufferItem* item) const { + const GonkBufferSlot& slot = mSlots[item->mSlot]; + + ALOGV("stillTracking: item { slot=%d/%" PRIu64 " buffer=%p } " + "slot { slot=%d/%" PRIu64 " buffer=%p }", + item->mSlot, item->mFrameNumber, + (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0), + item->mSlot, slot.mFrameNumber, + (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0)); + + // Compare item with its original buffer slot. We can check the slot as + // the buffer would not be moved to a different slot by the producer. + return (slot.mGraphicBuffer != NULL) && + (item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle); +} + +void GonkBufferQueueCore::waitWhileAllocatingLocked() const { + ATRACE_CALL(); + while (mIsAllocating) { + mIsAllocatingCondition.wait(mMutex); + } +} + +} // namespace android diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.h new file mode 100644 index 000000000000..936e11686cd3 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueCore.h @@ -0,0 +1,251 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_GONKBUFFERQUEUECORE_LL_H +#define NATIVEWINDOW_GONKBUFFERQUEUECORE_LL_H + +#include "GonkBufferQueueDefs.h" +#include "GonkBufferSlot.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mozilla/layers/TextureClient.h" + +#define ATRACE_BUFFER_INDEX(index) + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::layers; + +namespace android { + +class GonkBufferItem; +class IConsumerListener; +class IGraphicBufferAlloc; +class IProducerListener; + +class GonkBufferQueueCore : public virtual RefBase { + + friend class GonkBufferQueueProducer; + friend class GonkBufferQueueConsumer; + +public: + // Used as a placeholder slot number when the value isn't pointing to an + // existing buffer. + enum { INVALID_BUFFER_SLOT = -1 }; // TODO: Extract from IGBC::BufferItem + + // We reserve two slots in order to guarantee that the producer and + // consumer can run asynchronously. + enum { MAX_MAX_ACQUIRED_BUFFERS = GonkBufferQueueDefs::NUM_BUFFER_SLOTS - 2 }; + + // The default API number used to indicate that no producer is connected + enum { NO_CONNECTED_API = 0 }; + + typedef Vector Fifo; + typedef mozilla::layers::TextureClient TextureClient; + + // GonkBufferQueueCore manages a pool of gralloc memory slots to be used by + // producers and consumers. allocator is used to allocate all the needed + // gralloc buffers. + GonkBufferQueueCore(const sp& allocator = NULL); + virtual ~GonkBufferQueueCore(); + +private: + // Dump our state in a string + void dump(String8& result, const char* prefix) const; + + int getSlotFromTextureClientLocked(TextureClient* client) const; + + // getMinUndequeuedBufferCountLocked returns the minimum number of buffers + // that must remain in a state other than DEQUEUED. The async parameter + // tells whether we're in asynchronous mode. + int getMinUndequeuedBufferCountLocked(bool async) const; + + // getMinMaxBufferCountLocked returns the minimum number of buffers allowed + // given the current GonkBufferQueue state. The async parameter tells whether + // we're in asynchonous mode. + int getMinMaxBufferCountLocked(bool async) const; + + // getMaxBufferCountLocked returns the maximum number of buffers that can be + // allocated at once. This value depends on the following member variables: + // + // mDequeueBufferCannotBlock + // mMaxAcquiredBufferCount + // mDefaultMaxBufferCount + // mOverrideMaxBufferCount + // async parameter + // + // Any time one of these member variables is changed while a producer is + // connected, mDequeueCondition must be broadcast. + int getMaxBufferCountLocked(bool async) const; + + // setDefaultMaxBufferCountLocked sets the maximum number of buffer slots + // that will be used if the producer does not override the buffer slot + // count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. The + // initial default is 2. + status_t setDefaultMaxBufferCountLocked(int count); + + // freeBufferLocked frees the GraphicBuffer and sync resources for the + // given slot. + void freeBufferLocked(int slot); + + // freeAllBuffersLocked frees the GraphicBuffer and sync resources for + // all slots. + void freeAllBuffersLocked(); + + // stillTracking returns true iff the buffer item is still being tracked + // in one of the slots. + bool stillTracking(const GonkBufferItem* item) const; + + // waitWhileAllocatingLocked blocks until mIsAllocating is false. + void waitWhileAllocatingLocked() const; + + // mAllocator is the connection to SurfaceFlinger that is used to allocate + // new GraphicBuffer objects. + sp mAllocator; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of GonkBufferQueueCore objects. It must be locked whenever any + // member variable is accessed. + mutable Mutex mMutex; + + // mIsAbandoned indicates that the GonkBufferQueue will no longer be used to + // consume image buffers pushed to it using the IGraphicBufferProducer + // interface. It is initialized to false, and set to true in the + // consumerDisconnect method. A GonkBufferQueue that is abandoned will return + // the NO_INIT error from all IGraphicBufferProducer methods capable of + // returning an error. + bool mIsAbandoned; + + // mConsumerControlledByApp indicates whether the connected consumer is + // controlled by the application. + bool mConsumerControlledByApp; + + // mConsumerName is a string used to identify the GonkBufferQueue in log + // messages. It is set by the IGraphicBufferConsumer::setConsumerName + // method. + String8 mConsumerName; + + // mConsumerListener is used to notify the connected consumer of + // asynchronous events that it may wish to react to. It is initially + // set to NULL and is written by consumerConnect and consumerDisconnect. + sp mConsumerListener; + + // mConsumerUsageBits contains flags that the consumer wants for + // GraphicBuffers. + uint32_t mConsumerUsageBits; + + // mConnectedApi indicates the producer API that is currently connected + // to this GonkBufferQueue. It defaults to NO_CONNECTED_API, and gets updated + // by the connect and disconnect methods. + int mConnectedApi; + + // mConnectedProducerToken is used to set a binder death notification on + // the producer. + sp mConnectedProducerListener; + + // mSlots is an array of buffer slots that must be mirrored on the producer + // side. This allows buffer ownership to be transferred between the producer + // and consumer without sending a GraphicBuffer over Binder. The entire + // array is initialized to NULL at construction time, and buffers are + // allocated for a slot when requestBuffer is called with that slot's index. + GonkBufferQueueDefs::SlotsType mSlots; + + // mQueue is a FIFO of queued buffers used in synchronous mode. + Fifo mQueue; + + // mOverrideMaxBufferCount is the limit on the number of buffers that will + // be allocated at one time. This value is set by the producer by calling + // setBufferCount. The default is 0, which means that the producer doesn't + // care about the number of buffers in the pool. In that case, + // mDefaultMaxBufferCount is used as the limit. + int mOverrideMaxBufferCount; + + // mDequeueCondition is a condition variable used for dequeueBuffer in + // synchronous mode. + mutable Condition mDequeueCondition; + + // mUseAsyncBuffer indicates whether an extra buffer is used in async mode + // to prevent dequeueBuffer from blocking. + bool mUseAsyncBuffer; + + // mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to + // block. This flag is set during connect when both the producer and + // consumer are controlled by the application. + bool mDequeueBufferCannotBlock; + + // mDefaultBufferFormat can be set so it will override the buffer format + // when it isn't specified in dequeueBuffer. + uint32_t mDefaultBufferFormat; + + // mDefaultWidth holds the default width of allocated buffers. It is used + // in dequeueBuffer if a width and height of 0 are specified. + int mDefaultWidth; + + // mDefaultHeight holds the default height of allocated buffers. It is used + // in dequeueBuffer if a width and height of 0 are specified. + int mDefaultHeight; + + // mDefaultMaxBufferCount is the default limit on the number of buffers that + // will be allocated at one time. This default limit is set by the consumer. + // The limit (as opposed to the default limit) may be overriden by the + // producer. + int mDefaultMaxBufferCount; + + // mMaxAcquiredBufferCount is the number of buffers that the consumer may + // acquire at one time. It defaults to 1, and can be changed by the consumer + // via setMaxAcquiredBufferCount, but this may only be done while no + // producer is connected to the GonkBufferQueue. This value is used to derive + // the value returned for the MIN_UNDEQUEUED_BUFFERS query to the producer. + int mMaxAcquiredBufferCount; + + // mBufferHasBeenQueued is true once a buffer has been queued. It is reset + // when something causes all buffers to be freed (e.g., changing the buffer + // count). + bool mBufferHasBeenQueued; + + // mFrameCounter is the free running counter, incremented on every + // successful queueBuffer call and buffer allocation. + uint64_t mFrameCounter; + + // mTransformHint is used to optimize for screen rotations. + uint32_t mTransformHint; + + // mSidebandStream is a handle to the sideband buffer stream, if any + sp mSidebandStream; + + // mIsAllocating indicates whether a producer is currently trying to allocate buffers (which + // releases mMutex while doing the allocation proper). Producers should not modify any of the + // FREE slots while this is true. mIsAllocatingCondition is signaled when this value changes to + // false. + bool mIsAllocating; + + // mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating + // becomes false. + mutable Condition mIsAllocatingCondition; +}; // class GonkBufferQueueCore + +} // namespace android + +#endif diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueDefs.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueDefs.h new file mode 100644 index 000000000000..60085706f5e6 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueDefs.h @@ -0,0 +1,36 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_BUFFERQUEUECOREDEFS_H +#define NATIVEWINDOW_BUFFERQUEUECOREDEFS_H + +#include "GonkBufferSlot.h" + +namespace android { + class GonkBufferQueueCore; + + namespace GonkBufferQueueDefs { + // GonkBufferQueue will keep track of at most this value of buffers. + // Attempts at runtime to increase the number of buffers past this + // will fail. + enum { NUM_BUFFER_SLOTS = 64 }; + + typedef GonkBufferSlot SlotsType[NUM_BUFFER_SLOTS]; + } // namespace GonkBufferQueueDefs +} // namespace android + +#endif diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.cpp new file mode 100644 index 000000000000..1056c3b0a5ee --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#define LOG_TAG "GonkBufferQueue" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#define LOG_NDEBUG 0 + +#include "GonkBufferQueue.h" +#include "GonkBufferQueueConsumer.h" +#include "GonkBufferQueueCore.h" +#include "GonkBufferQueueProducer.h" + +namespace android { + +GonkBufferQueue::ProxyConsumerListener::ProxyConsumerListener( + const wp& consumerListener): + mConsumerListener(consumerListener) {} + +GonkBufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} + +void GonkBufferQueue::ProxyConsumerListener::onFrameAvailable() { + sp listener(mConsumerListener.promote()); + if (listener != NULL) { + listener->onFrameAvailable(); + } +} + +void GonkBufferQueue::ProxyConsumerListener::onBuffersReleased() { + sp listener(mConsumerListener.promote()); + if (listener != NULL) { + listener->onBuffersReleased(); + } +} + +void GonkBufferQueue::ProxyConsumerListener::onSidebandStreamChanged() { + sp listener(mConsumerListener.promote()); + if (listener != NULL) { + listener->onSidebandStreamChanged(); + } +} + +void GonkBufferQueue::createBufferQueue(sp* outProducer, + sp* outConsumer, + const sp& allocator) { + LOG_ALWAYS_FATAL_IF(outProducer == NULL, + "GonkBufferQueue: outProducer must not be NULL"); + LOG_ALWAYS_FATAL_IF(outConsumer == NULL, + "GonkBufferQueue: outConsumer must not be NULL"); + + sp core(new GonkBufferQueueCore(allocator)); + LOG_ALWAYS_FATAL_IF(core == NULL, + "GonkBufferQueue: failed to create GonkBufferQueueCore"); + + sp producer(new GonkBufferQueueProducer(core)); + LOG_ALWAYS_FATAL_IF(producer == NULL, + "GonkBufferQueue: failed to create GonkBufferQueueProducer"); + + sp consumer(new GonkBufferQueueConsumer(core)); + LOG_ALWAYS_FATAL_IF(consumer == NULL, + "GonkBufferQueue: failed to create GonkBufferQueueConsumer"); + + *outProducer = producer; + *outConsumer = consumer; +} + +}; // namespace android diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.h new file mode 100644 index 000000000000..9fb740af5f0e --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueLL.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_GONKBUFFERQUEUE_LL_H +#define NATIVEWINDOW_GONKBUFFERQUEUE_LL_H + +#include "GonkBufferQueueDefs.h" +#include "IGonkGraphicBufferConsumerLL.h" +#include +#include + +// These are only required to keep other parts of the framework with incomplete +// dependencies building successfully +#include + +namespace android { + +class GonkBufferQueue { +public: + // GonkBufferQueue will keep track of at most this value of buffers. + // Attempts at runtime to increase the number of buffers past this will fail. + enum { NUM_BUFFER_SLOTS = GonkBufferQueueDefs::NUM_BUFFER_SLOTS }; + // Used as a placeholder slot# when the value isn't pointing to an existing buffer. + enum { INVALID_BUFFER_SLOT = IGonkGraphicBufferConsumer::BufferItem::INVALID_BUFFER_SLOT }; + // Alias to -- please scope from there in future code! + enum { + NO_BUFFER_AVAILABLE = IGonkGraphicBufferConsumer::NO_BUFFER_AVAILABLE, + PRESENT_LATER = IGonkGraphicBufferConsumer::PRESENT_LATER, + }; + + // When in async mode we reserve two slots in order to guarantee that the + // producer and consumer can run asynchronously. + enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 }; + + // for backward source compatibility + typedef ::android::ConsumerListener ConsumerListener; + typedef IGonkGraphicBufferConsumer::BufferItem BufferItem; + + // ProxyConsumerListener is a ConsumerListener implementation that keeps a weak + // reference to the actual consumer object. It forwards all calls to that + // consumer object so long as it exists. + // + // This class exists to avoid having a circular reference between the + // GonkBufferQueue object and the consumer object. The reason this can't be a weak + // reference in the GonkBufferQueue class is because we're planning to expose the + // consumer side of a GonkBufferQueue as a binder interface, which doesn't support + // weak references. + class ProxyConsumerListener : public BnConsumerListener { + public: + ProxyConsumerListener(const wp& consumerListener); + virtual ~ProxyConsumerListener(); + virtual void onFrameAvailable(); + virtual void onBuffersReleased(); + virtual void onSidebandStreamChanged(); + private: + // mConsumerListener is a weak reference to the IConsumerListener. This is + // the raison d'etre of ProxyConsumerListener. + wp mConsumerListener; + }; + + // GonkBufferQueue manages a pool of gralloc memory slots to be used by + // producers and consumers. allocator is used to allocate all the + // needed gralloc buffers. + static void createBufferQueue(sp* outProducer, + sp* outConsumer, + const sp& allocator = NULL); + +private: + GonkBufferQueue(); // Create through createBufferQueue +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // NATIVEWINDOW_GONKBUFFERQUEUE_LL_H diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.cpp new file mode 100644 index 000000000000..5d3bbedb2c12 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.cpp @@ -0,0 +1,860 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#include + +#define LOG_TAG "GonkBufferQueueProducer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#include "GonkBufferItem.h" +#include "GonkBufferQueueCore.h" +#include "GonkBufferQueueProducer.h" +#include +#include +#include + +#include +#include +#include + +#include "mozilla/layers/GrallocTextureClient.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/TextureClient.h" + +namespace android { + +GonkBufferQueueProducer::GonkBufferQueueProducer(const sp& core) : + mCore(core), + mSlots(core->mSlots), + mConsumerName(), + mStickyTransform(0) {} + +GonkBufferQueueProducer::~GonkBufferQueueProducer() {} + +status_t GonkBufferQueueProducer::requestBuffer(int slot, sp* buf) { + ATRACE_CALL(); + ALOGV("requestBuffer: slot %d", slot); + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + ALOGE("requestBuffer: GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) { + ALOGE("requestBuffer: slot index %d out of range [0, %d)", + slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS); + return BAD_VALUE; + } else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) { + ALOGE("requestBuffer: slot %d is not owned by the producer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return BAD_VALUE; + } + + mSlots[slot].mRequestBufferCalled = true; + *buf = mSlots[slot].mGraphicBuffer; + return NO_ERROR; +} + +status_t GonkBufferQueueProducer::setBufferCount(int bufferCount) { + ATRACE_CALL(); + ALOGV("setBufferCount: count = %d", bufferCount); + + sp listener; + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + mCore->waitWhileAllocatingLocked(); + + if (mCore->mIsAbandoned) { + ALOGE("setBufferCount: GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + if (bufferCount > GonkBufferQueueDefs::NUM_BUFFER_SLOTS) { + ALOGE("setBufferCount: bufferCount %d too large (max %d)", + bufferCount, GonkBufferQueueDefs::NUM_BUFFER_SLOTS); + return BAD_VALUE; + } + + // There must be no dequeued buffers when changing the buffer count. + for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (mSlots[s].mBufferState == GonkBufferSlot::DEQUEUED) { + ALOGE("setBufferCount: buffer owned by producer"); + return BAD_VALUE; + } + } + + if (bufferCount == 0) { + mCore->mOverrideMaxBufferCount = 0; + mCore->mDequeueCondition.broadcast(); + return NO_ERROR; + } + + const int minBufferSlots = mCore->getMinMaxBufferCountLocked(false); + if (bufferCount < minBufferSlots) { + ALOGE("setBufferCount: requested buffer count %d is less than " + "minimum %d", bufferCount, minBufferSlots); + return BAD_VALUE; + } + + // Here we are guaranteed that the producer doesn't have any dequeued + // buffers and will release all of its buffer references. We don't + // clear the queue, however, so that currently queued buffers still + // get displayed. + mCore->freeAllBuffersLocked(); + mCore->mOverrideMaxBufferCount = bufferCount; + mCore->mDequeueCondition.broadcast(); + listener = mCore->mConsumerListener; + } // Autolock scope + + // Call back without lock held + if (listener != NULL) { + listener->onBuffersReleased(); + } + + return NO_ERROR; +} + +status_t GonkBufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, + bool async, int* found, status_t* returnFlags) const { + bool tryAgain = true; + while (tryAgain) { + if (mCore->mIsAbandoned) { + ALOGE("%s: GonkBufferQueue has been abandoned", caller); + return NO_INIT; + } + + const int maxBufferCount = mCore->getMaxBufferCountLocked(async); + if (async && mCore->mOverrideMaxBufferCount) { + // FIXME: Some drivers are manually setting the buffer count + // (which they shouldn't), so we do this extra test here to + // handle that case. This is TEMPORARY until we get this fixed. + if (mCore->mOverrideMaxBufferCount < maxBufferCount) { + ALOGE("%s: async mode is invalid with buffer count override", + caller); + return BAD_VALUE; + } + } + + // Free up any buffers that are in slots beyond the max buffer count + //for (int s = maxBufferCount; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + // assert(mSlots[s].mBufferState == GonkBufferSlot::FREE); + // if (mSlots[s].mGraphicBuffer != NULL) { + // mCore->freeBufferLocked(s); + // *returnFlags |= RELEASE_ALL_BUFFERS; + // } + //} + + // Look for a free buffer to give to the client + *found = GonkBufferQueueCore::INVALID_BUFFER_SLOT; + int dequeuedCount = 0; + int acquiredCount = 0; + for (int s = 0; s < maxBufferCount; ++s) { + switch (mSlots[s].mBufferState) { + case GonkBufferSlot::DEQUEUED: + ++dequeuedCount; + break; + case GonkBufferSlot::ACQUIRED: + ++acquiredCount; + break; + case GonkBufferSlot::FREE: + // We return the oldest of the free buffers to avoid + // stalling the producer if possible, since the consumer + // may still have pending reads of in-flight buffers + if (*found == GonkBufferQueueCore::INVALID_BUFFER_SLOT || + mSlots[s].mFrameNumber < mSlots[*found].mFrameNumber) { + *found = s; + } + break; + default: + break; + } + } + + // Producers are not allowed to dequeue more than one buffer if they + // did not set a buffer count + if (!mCore->mOverrideMaxBufferCount && dequeuedCount) { + ALOGE("%s: can't dequeue multiple buffers without setting the " + "buffer count", caller); + return INVALID_OPERATION; + } + + // See whether a buffer has been queued since the last + // setBufferCount so we know whether to perform the min undequeued + // buffers check below + if (mCore->mBufferHasBeenQueued) { + // Make sure the producer is not trying to dequeue more buffers + // than allowed + const int newUndequeuedCount = + maxBufferCount - (dequeuedCount + 1); + const int minUndequeuedCount = + mCore->getMinUndequeuedBufferCountLocked(async); + if (newUndequeuedCount < minUndequeuedCount) { + ALOGE("%s: min undequeued buffer count (%d) exceeded " + "(dequeued=%d undequeued=%d)", + caller, minUndequeuedCount, + dequeuedCount, newUndequeuedCount); + return INVALID_OPERATION; + } + } + + // If we disconnect and reconnect quickly, we can be in a state where + // our slots are empty but we have many buffers in the queue. This can + // cause us to run out of memory if we outrun the consumer. Wait here if + // it looks like we have too many buffers queued up. + bool tooManyBuffers = mCore->mQueue.size() + > static_cast(maxBufferCount); + if (tooManyBuffers) { + ALOGV("%s: queue size is %zu, waiting", caller, + mCore->mQueue.size()); + } + + // If no buffer is found, or if the queue has too many buffers + // outstanding, wait for a buffer to be acquired or released, or for the + // max buffer count to change. + tryAgain = (*found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) || + tooManyBuffers; + if (tryAgain) { + // Return an error if we're in non-blocking mode (producer and + // consumer are controlled by the application). + // However, the consumer is allowed to briefly acquire an extra + // buffer (which could cause us to have to wait here), which is + // okay, since it is only used to implement an atomic acquire + + // release (e.g., in GLConsumer::updateTexImage()) + if (mCore->mDequeueBufferCannotBlock && + (acquiredCount <= mCore->mMaxAcquiredBufferCount)) { + return WOULD_BLOCK; + } + mCore->mDequeueCondition.wait(mCore->mMutex); + } + } // while (tryAgain) + + return NO_ERROR; +} + +status_t GonkBufferQueueProducer::dequeueBuffer(int *outSlot, + sp *outFence, bool async, + uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { + ATRACE_CALL(); + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + mConsumerName = mCore->mConsumerName; + } // Autolock scope + + ALOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x", + async ? "true" : "false", width, height, format, usage); + + if ((width && !height) || (!width && height)) { + ALOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height); + return BAD_VALUE; + } + + status_t returnFlags = NO_ERROR; + // Reset slot + *outSlot = GonkBufferQueueCore::INVALID_BUFFER_SLOT; + + bool attachedByConsumer = false; + + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + mCore->waitWhileAllocatingLocked(); + + if (format == 0) { + format = mCore->mDefaultBufferFormat; + } + + // Enable the usage bits the consumer requested + usage |= mCore->mConsumerUsageBits; + + int found; + status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async, + &found, &returnFlags); + if (status != NO_ERROR) { + return status; + } + + // This should not happen + if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) { + ALOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; + } + + *outSlot = found; + + attachedByConsumer = mSlots[found].mAttachedByConsumer; + + const bool useDefaultSize = !width && !height; + if (useDefaultSize) { + width = mCore->mDefaultWidth; + height = mCore->mDefaultHeight; + } + + mSlots[found].mBufferState = GonkBufferSlot::DEQUEUED; + + const sp& buffer(mSlots[found].mGraphicBuffer); + if ((buffer == NULL) || + (static_cast(buffer->width) != width) || + (static_cast(buffer->height) != height) || + (static_cast(buffer->format) != format) || + ((static_cast(buffer->usage) & usage) != usage)) + { + mSlots[found].mAcquireCalled = false; + mSlots[found].mGraphicBuffer = NULL; + mSlots[found].mRequestBufferCalled = false; + mSlots[found].mFence = Fence::NO_FENCE; + + if (mSlots[found].mTextureClient) { + mSlots[found].mTextureClient->ClearRecycleCallback(); + // release TextureClient in ImageBridge thread + TextureClientReleaseTask* task = new TextureClientReleaseTask(mSlots[found].mTextureClient); + mSlots[found].mTextureClient = NULL; + ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(FROM_HERE, task); + } + + returnFlags |= BUFFER_NEEDS_REALLOCATION; + } + + if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { + ALOGE("dequeueBuffer: about to return a NULL fence - " + "slot=%d w=%d h=%d format=%u", + found, buffer->width, buffer->height, buffer->format); + } + + *outFence = mSlots[found].mFence; + mSlots[found].mFence = Fence::NO_FENCE; + } // Autolock scope + + if (returnFlags & BUFFER_NEEDS_REALLOCATION) { + RefPtr textureClient = + new GrallocTextureClientOGL(ImageBridgeChild::GetSingleton(), + gfx::SurfaceFormat::UNKNOWN, + gfx::BackendType::NONE, + TextureFlags::DEALLOCATE_CLIENT); + textureClient->SetIsOpaque(true); + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + bool result = textureClient->AllocateGralloc(IntSize(width, height), format, usage); + sp graphicBuffer = textureClient->GetGraphicBuffer(); + if (!result || !graphicBuffer.get()) { + ALOGE("dequeueBuffer: failed to alloc gralloc buffer"); + return -ENOMEM; + } + + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + ALOGE("dequeueBuffer: GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + mSlots[*outSlot].mFrameNumber = UINT32_MAX; + mSlots[*outSlot].mGraphicBuffer = graphicBuffer; + mSlots[*outSlot].mTextureClient = textureClient; + } // Autolock scope + } + + if (attachedByConsumer) { + returnFlags |= BUFFER_NEEDS_REALLOCATION; + } + + ALOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x", + *outSlot, + mSlots[*outSlot].mFrameNumber, + mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); + + return returnFlags; +} + +status_t GonkBufferQueueProducer::detachBuffer(int slot) { + ATRACE_CALL(); + ATRACE_BUFFER_INDEX(slot); + ALOGV("detachBuffer(P): slot %d", slot); + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + ALOGE("detachBuffer(P): GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) { + ALOGE("detachBuffer(P): slot index %d out of range [0, %d)", + slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS); + return BAD_VALUE; + } else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) { + ALOGE("detachBuffer(P): slot %d is not owned by the producer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return BAD_VALUE; + } else if (!mSlots[slot].mRequestBufferCalled) { + ALOGE("detachBuffer(P): buffer in slot %d has not been requested", + slot); + return BAD_VALUE; + } + + mCore->freeBufferLocked(slot); + mCore->mDequeueCondition.broadcast(); + + return NO_ERROR; +} + +status_t GonkBufferQueueProducer::detachNextBuffer(sp* outBuffer, + sp* outFence) { + ATRACE_CALL(); + + if (outBuffer == NULL) { + ALOGE("detachNextBuffer: outBuffer must not be NULL"); + return BAD_VALUE; + } else if (outFence == NULL) { + ALOGE("detachNextBuffer: outFence must not be NULL"); + return BAD_VALUE; + } + + Mutex::Autolock lock(mCore->mMutex); + mCore->waitWhileAllocatingLocked(); + + if (mCore->mIsAbandoned) { + ALOGE("detachNextBuffer: GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + // Find the oldest valid slot + int found = GonkBufferQueueCore::INVALID_BUFFER_SLOT; + for (int s = 0; s < GonkBufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { + if (mSlots[s].mBufferState == GonkBufferSlot::FREE && + mSlots[s].mGraphicBuffer != NULL) { + if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT || + mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) { + found = s; + } + } + } + + if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) { + return NO_MEMORY; + } + + ALOGV("detachNextBuffer detached slot %d", found); + + *outBuffer = mSlots[found].mGraphicBuffer; + *outFence = mSlots[found].mFence; + mCore->freeBufferLocked(found); + + return NO_ERROR; +} + +status_t GonkBufferQueueProducer::attachBuffer(int* outSlot, + const sp& buffer) { + ATRACE_CALL(); + + if (outSlot == NULL) { + ALOGE("attachBuffer(P): outSlot must not be NULL"); + return BAD_VALUE; + } else if (buffer == NULL) { + ALOGE("attachBuffer(P): cannot attach NULL buffer"); + return BAD_VALUE; + } + + Mutex::Autolock lock(mCore->mMutex); + mCore->waitWhileAllocatingLocked(); + + status_t returnFlags = NO_ERROR; + int found; + // TODO: Should we provide an async flag to attachBuffer? It seems + // unlikely that buffers which we are attaching to a GonkBufferQueue will + // be asynchronous (droppable), but it may not be impossible. + status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", false, + &found, &returnFlags); + if (status != NO_ERROR) { + return status; + } + + // This should not happen + if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) { + ALOGE("attachBuffer(P): no available buffer slots"); + return -EBUSY; + } + + *outSlot = found; + ATRACE_BUFFER_INDEX(*outSlot); + ALOGV("attachBuffer(P): returning slot %d flags=%#x", + *outSlot, returnFlags); + + mSlots[*outSlot].mGraphicBuffer = buffer; + mSlots[*outSlot].mBufferState = GonkBufferSlot::DEQUEUED; + mSlots[*outSlot].mFence = Fence::NO_FENCE; + mSlots[*outSlot].mRequestBufferCalled = true; + + return returnFlags; +} + +status_t GonkBufferQueueProducer::queueBuffer(int slot, + const QueueBufferInput &input, QueueBufferOutput *output) { + ATRACE_CALL(); + + int64_t timestamp; + bool isAutoTimestamp; + Rect crop; + int scalingMode; + uint32_t transform; + uint32_t stickyTransform; + bool async; + sp fence; + input.deflate(×tamp, &isAutoTimestamp, &crop, &scalingMode, &transform, + &async, &fence, &stickyTransform); + + if (fence == NULL) { + ALOGE("queueBuffer: fence is NULL"); + // Temporary workaround for b/17946343: soldier-on instead of returning an error. This + // prevents the client from dying, at the risk of visible corruption due to hwcomposer + // reading the buffer before the producer is done rendering it. Unless the buffer is the + // last frame of an animation, the corruption will be transient. + fence = Fence::NO_FENCE; + // return BAD_VALUE; + } + + switch (scalingMode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: + case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP: + break; + default: + ALOGE("queueBuffer: unknown scaling mode %d", scalingMode); + return BAD_VALUE; + } + + sp listener; + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + ALOGE("queueBuffer: GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + const int maxBufferCount = mCore->getMaxBufferCountLocked(async); + if (async && mCore->mOverrideMaxBufferCount) { + // FIXME: Some drivers are manually setting the buffer count + // (which they shouldn't), so we do this extra test here to + // handle that case. This is TEMPORARY until we get this fixed. + if (mCore->mOverrideMaxBufferCount < maxBufferCount) { + ALOGE("queueBuffer: async mode is invalid with " + "buffer count override"); + return BAD_VALUE; + } + } + + if (slot < 0 || slot >= maxBufferCount) { + ALOGE("queueBuffer: slot index %d out of range [0, %d)", + slot, maxBufferCount); + return BAD_VALUE; + } else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) { + ALOGE("queueBuffer: slot %d is not owned by the producer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return BAD_VALUE; + } else if (!mSlots[slot].mRequestBufferCalled) { + ALOGE("queueBuffer: slot %d was queued without requesting " + "a buffer", slot); + return BAD_VALUE; + } + + ALOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 + " crop=[%d,%d,%d,%d] transform=%#x scale=%s", + slot, mCore->mFrameCounter + 1, timestamp, + crop.left, crop.top, crop.right, crop.bottom, + transform, GonkBufferItem::scalingModeName(scalingMode)); + + const sp& graphicBuffer(mSlots[slot].mGraphicBuffer); + Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight()); + Rect croppedRect; + crop.intersect(bufferRect, &croppedRect); + if (croppedRect != crop) { + ALOGE("queueBuffer: crop rect is not contained within the " + "buffer in slot %d", slot); + return BAD_VALUE; + } + + mSlots[slot].mFence = fence; + mSlots[slot].mBufferState = GonkBufferSlot::QUEUED; + ++mCore->mFrameCounter; + mSlots[slot].mFrameNumber = mCore->mFrameCounter; + + GonkBufferItem item; + item.mAcquireCalled = mSlots[slot].mAcquireCalled; + item.mGraphicBuffer = mSlots[slot].mGraphicBuffer; + item.mCrop = crop; + item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; + item.mTransformToDisplayInverse = + bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); + item.mScalingMode = scalingMode; + item.mTimestamp = timestamp; + item.mIsAutoTimestamp = isAutoTimestamp; + item.mFrameNumber = mCore->mFrameCounter; + item.mSlot = slot; + item.mFence = fence; + item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async; + + mStickyTransform = stickyTransform; + + if (mCore->mQueue.empty()) { + // When the queue is empty, we can ignore mDequeueBufferCannotBlock + // and simply queue this buffer + mCore->mQueue.push_back(item); + listener = mCore->mConsumerListener; + } else { + // When the queue is not empty, we need to look at the front buffer + // state to see if we need to replace it + GonkBufferQueueCore::Fifo::iterator front(mCore->mQueue.begin()); + if (front->mIsDroppable) { + // If the front queued buffer is still being tracked, we first + // mark it as freed + if (mCore->stillTracking(front)) { + mSlots[front->mSlot].mBufferState = GonkBufferSlot::FREE; + // Reset the frame number of the freed buffer so that it is + // the first in line to be dequeued again + mSlots[front->mSlot].mFrameNumber = 0; + } + // Overwrite the droppable buffer with the incoming one + *front = item; + } else { + mCore->mQueue.push_back(item); + listener = mCore->mConsumerListener; + } + } + + mCore->mBufferHasBeenQueued = true; + mCore->mDequeueCondition.broadcast(); + + output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, + mCore->mTransformHint, mCore->mQueue.size()); + } // Autolock scope + + // Call back without lock held + if (listener != NULL) { + listener->onFrameAvailable(); + } + + return NO_ERROR; +} + +void GonkBufferQueueProducer::cancelBuffer(int slot, const sp& fence) { + ATRACE_CALL(); + ALOGV("cancelBuffer: slot %d", slot); + Mutex::Autolock lock(mCore->mMutex); + + if (mCore->mIsAbandoned) { + ALOGE("cancelBuffer: GonkBufferQueue has been abandoned"); + return; + } + + if (slot < 0 || slot >= GonkBufferQueueDefs::NUM_BUFFER_SLOTS) { + ALOGE("cancelBuffer: slot index %d out of range [0, %d)", + slot, GonkBufferQueueDefs::NUM_BUFFER_SLOTS); + return; + } else if (mSlots[slot].mBufferState != GonkBufferSlot::DEQUEUED) { + ALOGE("cancelBuffer: slot %d is not owned by the producer " + "(state = %d)", slot, mSlots[slot].mBufferState); + return; + } else if (fence == NULL) { + ALOGE("cancelBuffer: fence is NULL"); + return; + } + + mSlots[slot].mBufferState = GonkBufferSlot::FREE; + mSlots[slot].mFrameNumber = 0; + mSlots[slot].mFence = fence; + mCore->mDequeueCondition.broadcast(); +} + +int GonkBufferQueueProducer::query(int what, int *outValue) { + ATRACE_CALL(); + Mutex::Autolock lock(mCore->mMutex); + + if (outValue == NULL) { + ALOGE("query: outValue was NULL"); + return BAD_VALUE; + } + + if (mCore->mIsAbandoned) { + ALOGE("query: GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + int value; + switch (what) { + case NATIVE_WINDOW_WIDTH: + value = mCore->mDefaultWidth; + break; + case NATIVE_WINDOW_HEIGHT: + value = mCore->mDefaultHeight; + break; + case NATIVE_WINDOW_FORMAT: + value = mCore->mDefaultBufferFormat; + break; + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + value = mCore->getMinUndequeuedBufferCountLocked(false); + break; + case NATIVE_WINDOW_STICKY_TRANSFORM: + value = static_cast(mStickyTransform); + break; + case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: + value = (mCore->mQueue.size() > 1); + break; + case NATIVE_WINDOW_CONSUMER_USAGE_BITS: + value = mCore->mConsumerUsageBits; + break; + default: + return BAD_VALUE; + } + + ALOGV("query: %d? %d", what, value); + *outValue = value; + return NO_ERROR; +} + +status_t GonkBufferQueueProducer::connect(const sp& listener, + int api, bool producerControlledByApp, QueueBufferOutput *output) { + ATRACE_CALL(); + Mutex::Autolock lock(mCore->mMutex); + mConsumerName = mCore->mConsumerName; + ALOGV("connect(P): api=%d producerControlledByApp=%s", api, + producerControlledByApp ? "true" : "false"); + + if (mCore->mIsAbandoned) { + ALOGE("connect(P): GonkBufferQueue has been abandoned"); + return NO_INIT; + } + + if (mCore->mConsumerListener == NULL) { + ALOGE("connect(P): GonkBufferQueue has no consumer"); + return NO_INIT; + } + + if (output == NULL) { + ALOGE("connect(P): output was NULL"); + return BAD_VALUE; + } + + if (mCore->mConnectedApi != GonkBufferQueueCore::NO_CONNECTED_API) { + ALOGE("connect(P): already connected (cur=%d req=%d)", mCore->mConnectedApi, + api); + return BAD_VALUE; + } + + int status = NO_ERROR; + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + mCore->mConnectedApi = api; + output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight, + mCore->mTransformHint, mCore->mQueue.size()); + + // Set up a death notification so that we can disconnect + // automatically if the remote producer dies + if (listener != NULL && + listener->asBinder()->remoteBinder() != NULL) { + status = listener->asBinder()->linkToDeath( + static_cast(this)); + if (status != NO_ERROR) { + ALOGE("connect(P): linkToDeath failed: %s (%d)", + strerror(-status), status); + } + } + mCore->mConnectedProducerListener = listener; + break; + default: + ALOGE("connect(P): unknown API %d", api); + status = BAD_VALUE; + break; + } + + mCore->mBufferHasBeenQueued = false; + mCore->mDequeueBufferCannotBlock = + mCore->mConsumerControlledByApp && producerControlledByApp; + + return status; +} + +status_t GonkBufferQueueProducer::disconnect(int api) { + ATRACE_CALL(); + ALOGV("disconnect(P): api %d", api); + + int status = NO_ERROR; + sp listener; + { // Autolock scope + Mutex::Autolock lock(mCore->mMutex); + mCore->waitWhileAllocatingLocked(); + + if (mCore->mIsAbandoned) { + // It's not really an error to disconnect after the surface has + // been abandoned; it should just be a no-op. + return NO_ERROR; + } + + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mCore->mConnectedApi == api) { + mCore->freeAllBuffersLocked(); + mCore->mConnectedApi = GonkBufferQueueCore::NO_CONNECTED_API; + mCore->mSidebandStream.clear(); + mCore->mDequeueCondition.broadcast(); + listener = mCore->mConsumerListener; + } else { + ALOGE("disconnect(P): connected to another API " + "(cur=%d req=%d)", mCore->mConnectedApi, api); + status = BAD_VALUE; + } + break; + default: + ALOGE("disconnect(P): unknown API %d", api); + status = BAD_VALUE; + break; + } + } // Autolock scope + + // Call back without lock held + if (listener != NULL) { + listener->onBuffersReleased(); + } + + return status; +} + +status_t GonkBufferQueueProducer::setSidebandStream(const sp& stream) { + return INVALID_OPERATION; +} + +void GonkBufferQueueProducer::allocateBuffers(bool async, uint32_t width, + uint32_t height, uint32_t format, uint32_t usage) { + ALOGE("allocateBuffers: no op"); +} + +void GonkBufferQueueProducer::binderDied(const wp& /* who */) { + // If we're here, it means that a producer we were connected to died. + // We're guaranteed that we are still connected to it because we remove + // this callback upon disconnect. It's therefore safe to read mConnectedApi + // without synchronization here. + int api = mCore->mConnectedApi; + disconnect(api); +} + +} // namespace android diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.h new file mode 100644 index 000000000000..ef3b70a413b6 --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferQueueProducer.h @@ -0,0 +1,205 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_GONKBUFFERQUEUEPRODUCER_LL_H +#define NATIVEWINDOW_GONKBUFFERQUEUEPRODUCER_LL_H + +#include "GonkBufferQueueDefs.h" +#include + +namespace android { + +class GonkBufferSlot; + +class GonkBufferQueueProducer : public BnGraphicBufferProducer, + private IBinder::DeathRecipient { +public: + friend class GonkBufferQueue; // Needed to access binderDied + + GonkBufferQueueProducer(const sp& core); + virtual ~GonkBufferQueueProducer(); + + // requestBuffer returns the GraphicBuffer for slot N. + // + // In normal operation, this is called the first time slot N is returned + // by dequeueBuffer. It must be called again if dequeueBuffer returns + // flags indicating that previously-returned buffers are no longer valid. + virtual status_t requestBuffer(int slot, sp* buf); + + // setBufferCount updates the number of available buffer slots. If this + // method succeeds, buffer slots will be both unallocated and owned by + // the GonkBufferQueue object (i.e. they are not owned by the producer or + // consumer). + // + // This will fail if the producer has dequeued any buffers, or if + // bufferCount is invalid. bufferCount must generally be a value + // between the minimum undequeued buffer count (exclusive) and NUM_BUFFER_SLOTS + // (inclusive). It may also be set to zero (the default) to indicate + // that the producer does not wish to set a value. The minimum value + // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + // ...). + // + // This may only be called by the producer. The consumer will be told + // to discard buffers through the onBuffersReleased callback. + virtual status_t setBufferCount(int bufferCount); + + // dequeueBuffer gets the next buffer slot index for the producer to use. + // If a buffer slot is available then that slot index is written to the + // location pointed to by the buf argument and a status of OK is returned. + // If no slot is available then a status of -EBUSY is returned and buf is + // unmodified. + // + // The outFence parameter will be updated to hold the fence associated with + // the buffer. The contents of the buffer must not be overwritten until the + // fence signals. If the fence is Fence::NO_FENCE, the buffer may be + // written immediately. + // + // The width and height parameters must be no greater than the minimum of + // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + // An error due to invalid dimensions might not be reported until + // updateTexImage() is called. If width and height are both zero, the + // default values specified by setDefaultBufferSize() are used instead. + // + // The pixel formats are enumerated in graphics.h, e.g. + // HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format + // will be used. + // + // The usage argument specifies gralloc buffer usage flags. The values + // are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER. These + // will be merged with the usage flags specified by setConsumerUsageBits. + // + // The return value may be a negative error value or a non-negative + // collection of flags. If the flags are set, the return values are + // valid, but additional actions must be performed. + // + // If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the + // producer must discard cached GraphicBuffer references for the slot + // returned in buf. + // If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer + // must discard cached GraphicBuffer references for all slots. + // + // In both cases, the producer will need to call requestBuffer to get a + // GraphicBuffer handle for the returned slot. + virtual status_t dequeueBuffer(int *outSlot, sp* outFence, bool async, + uint32_t width, uint32_t height, uint32_t format, uint32_t usage); + + // See IGraphicBufferProducer::detachBuffer + virtual status_t detachBuffer(int slot); + + // See IGraphicBufferProducer::detachNextBuffer + virtual status_t detachNextBuffer(sp* outBuffer, + sp* outFence); + + // See IGraphicBufferProducer::attachBuffer + virtual status_t attachBuffer(int* outSlot, const sp& buffer); + + // queueBuffer returns a filled buffer to the GonkBufferQueue. + // + // Additional data is provided in the QueueBufferInput struct. Notably, + // a timestamp must be provided for the buffer. The timestamp is in + // nanoseconds, and must be monotonically increasing. Its other semantics + // (zero point, etc) are producer-specific and should be documented by the + // producer. + // + // The caller may provide a fence that signals when all rendering + // operations have completed. Alternatively, NO_FENCE may be used, + // indicating that the buffer is ready immediately. + // + // Some values are returned in the output struct: the current settings + // for default width and height, the current transform hint, and the + // number of queued buffers. + virtual status_t queueBuffer(int slot, + const QueueBufferInput& input, QueueBufferOutput* output); + + // cancelBuffer returns a dequeued buffer to the GonkBufferQueue, but doesn't + // queue it for use by the consumer. + // + // The buffer will not be overwritten until the fence signals. The fence + // will usually be the one obtained from dequeueBuffer. + virtual void cancelBuffer(int slot, const sp& fence); + + // Query native window attributes. The "what" values are enumerated in + // window.h (e.g. NATIVE_WINDOW_FORMAT). + virtual int query(int what, int* outValue); + + // connect attempts to connect a producer API to the GonkBufferQueue. This + // must be called before any other IGraphicBufferProducer methods are + // called except for getAllocator. A consumer must already be connected. + // + // This method will fail if connect was previously called on the + // GonkBufferQueue and no corresponding disconnect call was made (i.e. if + // it's still connected to a producer). + // + // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU). + virtual status_t connect(const sp& listener, + int api, bool producerControlledByApp, QueueBufferOutput* output); + + // disconnect attempts to disconnect a producer API from the GonkBufferQueue. + // Calling this method will cause any subsequent calls to other + // IGraphicBufferProducer methods to fail except for getAllocator and connect. + // Successfully calling connect after this will allow the other methods to + // succeed again. + // + // This method will fail if the the GonkBufferQueue is not currently + // connected to the specified producer API. + virtual status_t disconnect(int api); + + // Attaches a sideband buffer stream to the IGraphicBufferProducer. + // + // A sideband stream is a device-specific mechanism for passing buffers + // from the producer to the consumer without using dequeueBuffer/ + // queueBuffer. If a sideband stream is present, the consumer can choose + // whether to acquire buffers from the sideband stream or from the queued + // buffers. + // + // Passing NULL or a different stream handle will detach the previous + // handle if any. + virtual status_t setSidebandStream(const sp& stream); + + // See IGraphicBufferProducer::allocateBuffers + virtual void allocateBuffers(bool async, uint32_t width, uint32_t height, + uint32_t format, uint32_t usage); + +private: + // This is required by the IBinder::DeathRecipient interface + virtual void binderDied(const wp& who); + + // waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may + // block if there are no available slots and we are not in non-blocking + // mode (producer and consumer controlled by the application). If it blocks, + // it will release mCore->mMutex while blocked so that other operations on + // the GonkBufferQueue may succeed. + status_t waitForFreeSlotThenRelock(const char* caller, bool async, + int* found, status_t* returnFlags) const; + + sp mCore; + + // This references mCore->mSlots. Lock mCore->mMutex while accessing. + GonkBufferQueueDefs::SlotsType& mSlots; + + // This is a cached copy of the name stored in the GonkBufferQueueCore. + // It's updated during connect and dequeueBuffer (which should catch + // most updates). + String8 mConsumerName; + + uint32_t mStickyTransform; + +}; // class GonkBufferQueueProducer + +} // namespace android + +#endif diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.cpp b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.cpp new file mode 100644 index 000000000000..9e4a424a939c --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#include "GonkBufferSlot.h" + +namespace android { + +const char* GonkBufferSlot::bufferStateName(BufferState state) { + switch (state) { + case GonkBufferSlot::DEQUEUED: return "DEQUEUED"; + case GonkBufferSlot::QUEUED: return "QUEUED"; + case GonkBufferSlot::FREE: return "FREE"; + case GonkBufferSlot::ACQUIRED: return "ACQUIRED"; + default: return "Unknown"; + } +} + +} // namespace android diff --git a/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.h b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.h new file mode 100644 index 000000000000..c93827eb51ce --- /dev/null +++ b/widget/gonk/nativewindow/GonkBufferQueueLL/GonkBufferSlot.h @@ -0,0 +1,132 @@ +/* + * Copyright 2014 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_GONKBUFFERSLOT_LL_H +#define NATIVEWINDOW_GONKBUFFERSLOT_LL_H + +#include +#include + +#include + +#include "mozilla/layers/TextureClient.h" + +namespace android { + +struct GonkBufferSlot { + typedef mozilla::layers::TextureClient TextureClient; + + GonkBufferSlot() + : mBufferState(GonkBufferSlot::FREE), + mRequestBufferCalled(false), + mFrameNumber(0), + mAcquireCalled(false), + mNeedsCleanupOnRelease(false), + mAttachedByConsumer(false) { + } + + // mGraphicBuffer points to the buffer allocated for this slot or is NULL + // if no buffer has been allocated. + sp mGraphicBuffer; + + // BufferState represents the different states in which a buffer slot + // can be. All slots are initially FREE. + enum BufferState { + // FREE indicates that the buffer is available to be dequeued + // by the producer. The buffer may be in use by the consumer for + // a finite time, so the buffer must not be modified until the + // associated fence is signaled. + // + // The slot is "owned" by BufferQueue. It transitions to DEQUEUED + // when dequeueBuffer is called. + FREE = 0, + + // DEQUEUED indicates that the buffer has been dequeued by the + // producer, but has not yet been queued or canceled. The + // producer may modify the buffer's contents as soon as the + // associated ready fence is signaled. + // + // The slot is "owned" by the producer. It can transition to + // QUEUED (via queueBuffer) or back to FREE (via cancelBuffer). + DEQUEUED = 1, + + // QUEUED indicates that the buffer has been filled by the + // producer and queued for use by the consumer. The buffer + // contents may continue to be modified for a finite time, so + // the contents must not be accessed until the associated fence + // is signaled. + // + // The slot is "owned" by BufferQueue. It can transition to + // ACQUIRED (via acquireBuffer) or to FREE (if another buffer is + // queued in asynchronous mode). + QUEUED = 2, + + // ACQUIRED indicates that the buffer has been acquired by the + // consumer. As with QUEUED, the contents must not be accessed + // by the consumer until the fence is signaled. + // + // The slot is "owned" by the consumer. It transitions to FREE + // when releaseBuffer is called. + ACQUIRED = 3 + }; + + static const char* bufferStateName(BufferState state); + + // mBufferState is the current state of this buffer slot. + BufferState mBufferState; + + // mRequestBufferCalled is used for validating that the producer did + // call requestBuffer() when told to do so. Technically this is not + // needed but useful for debugging and catching producer bugs. + bool mRequestBufferCalled; + + // mFrameNumber is the number of the queued frame for this slot. This + // is used to dequeue buffers in LRU order (useful because buffers + // may be released before their release fence is signaled). + uint64_t mFrameNumber; + + // mFence is a fence which will signal when work initiated by the + // previous owner of the buffer is finished. When the buffer is FREE, + // the fence indicates when the consumer has finished reading + // from the buffer, or when the producer has finished writing if it + // called cancelBuffer after queueing some writes. When the buffer is + // QUEUED, it indicates when the producer has finished filling the + // buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been + // passed to the consumer or producer along with ownership of the + // buffer, and mFence is set to NO_FENCE. + sp mFence; + + // Indicates whether this buffer has been seen by a consumer yet + bool mAcquireCalled; + + // Indicates whether this buffer needs to be cleaned up by the + // consumer. This is set when a buffer in ACQUIRED state is freed. + // It causes releaseBuffer to return STALE_BUFFER_SLOT. + bool mNeedsCleanupOnRelease; + + // Indicates whether the buffer was attached on the consumer side. + // If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued + // to prevent the producer from using a stale cached buffer. + bool mAttachedByConsumer; + + // mTextureClient is a thin abstraction over remotely allocated GraphicBuffer. + mozilla::RefPtr mTextureClient; +}; + +} // namespace android + +#endif diff --git a/widget/gonk/nativewindow/GonkConsumerBaseJB.cpp b/widget/gonk/nativewindow/GonkConsumerBaseJB.cpp old mode 100755 new mode 100644 diff --git a/widget/gonk/nativewindow/GonkConsumerBaseJB.h b/widget/gonk/nativewindow/GonkConsumerBaseJB.h old mode 100755 new mode 100644 diff --git a/widget/gonk/nativewindow/GonkConsumerBaseLL.cpp b/widget/gonk/nativewindow/GonkConsumerBaseLL.cpp new file mode 100644 index 000000000000..8444665354a8 --- /dev/null +++ b/widget/gonk/nativewindow/GonkConsumerBaseLL.cpp @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#include + +#define LOG_TAG "GonkConsumerBase" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#define EGL_EGLEXT_PROTOTYPES + +#include + +#include + +#include +#include + +#include "GonkConsumerBaseLL.h" + +namespace android { + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +GonkConsumerBase::GonkConsumerBase(const sp& bufferQueue, bool controlledByApp) : + mAbandoned(false), + mConsumer(bufferQueue) { + // Choose a name using the PID and a process-unique ID. + mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); + + // Note that we can't create an sp<...>(this) in a ctor that will not keep a + // reference once the ctor ends, as that would cause the refcount of 'this' + // dropping to 0 at the end of the ctor. Since all we need is a wp<...> + // that's what we create. + wp listener = static_cast(this); + sp proxy = new GonkBufferQueue::ProxyConsumerListener(listener); + + status_t err = mConsumer->consumerConnect(proxy, controlledByApp); + if (err != NO_ERROR) { + ALOGE("GonkConsumerBase: error connecting to GonkBufferQueue: %s (%d)", + strerror(-err), err); + } else { + mConsumer->setConsumerName(mName); + } +} + +GonkConsumerBase::~GonkConsumerBase() { + ALOGV("~GonkConsumerBase"); + Mutex::Autolock lock(mMutex); + + // Verify that abandon() has been called before we get here. This should + // be done by GonkConsumerBase::onLastStrongRef(), but it's possible for a + // derived class to override that method and not call + // GonkConsumerBase::onLastStrongRef(). + LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~GonkConsumerBase was called, but the " + "consumer is not abandoned!", mName.string()); +} + +void GonkConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) { + abandon(); +} + +void GonkConsumerBase::freeBufferLocked(int slotIndex) { + ALOGV("freeBufferLocked: slotIndex=%d", slotIndex); + mSlots[slotIndex].mGraphicBuffer = 0; + mSlots[slotIndex].mFence = Fence::NO_FENCE; + mSlots[slotIndex].mFrameNumber = 0; +} + +void GonkConsumerBase::onFrameAvailable() { + ALOGV("onFrameAvailable"); + + sp listener; + { // scope for the lock + Mutex::Autolock lock(mMutex); + listener = mFrameAvailableListener.promote(); + } + + if (listener != NULL) { + ALOGV("actually calling onFrameAvailable"); + listener->onFrameAvailable(); + } +} + +void GonkConsumerBase::onBuffersReleased() { + Mutex::Autolock lock(mMutex); + + ALOGV("onBuffersReleased"); + + if (mAbandoned) { + // Nothing to do if we're already abandoned. + return; + } + + uint64_t mask = 0; + mConsumer->getReleasedBuffers(&mask); + for (int i = 0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) { + if (mask & (1ULL << i)) { + freeBufferLocked(i); + } + } +} + +void GonkConsumerBase::onSidebandStreamChanged() { +} + +void GonkConsumerBase::abandon() { + ALOGV("abandon"); + Mutex::Autolock lock(mMutex); + + if (!mAbandoned) { + abandonLocked(); + mAbandoned = true; + } +} + +void GonkConsumerBase::abandonLocked() { + ALOGV("abandonLocked"); + for (int i =0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) { + freeBufferLocked(i); + } + // disconnect from the BufferQueue + mConsumer->consumerDisconnect(); + mConsumer.clear(); +} + +void GonkConsumerBase::setFrameAvailableListener( + const wp& listener) { + ALOGV("setFrameAvailableListener"); + Mutex::Autolock lock(mMutex); + mFrameAvailableListener = listener; +} + +void GonkConsumerBase::dump(String8& result) const { + dump(result, ""); +} + +void GonkConsumerBase::dump(String8& result, const char* prefix) const { + Mutex::Autolock _l(mMutex); + dumpLocked(result, prefix); +} + +void GonkConsumerBase::dumpLocked(String8& result, const char* prefix) const { + result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned)); + + if (!mAbandoned) { + mConsumer->dump(result, prefix); + } +} + +status_t GonkConsumerBase::acquireBufferLocked(GonkBufferQueue::BufferItem *item, + nsecs_t presentWhen) { + status_t err = mConsumer->acquireBuffer(item, presentWhen); + if (err != NO_ERROR) { + return err; + } + + if (item->mGraphicBuffer != NULL) { + mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer; + } + + mSlots[item->mBuf].mFrameNumber = item->mFrameNumber; + mSlots[item->mBuf].mFence = item->mFence; + + ALOGV("acquireBufferLocked: -> slot=%d/%" PRIu64, + item->mBuf, item->mFrameNumber); + + return OK; +} + +status_t GonkConsumerBase::addReleaseFence(int slot, + const sp graphicBuffer, const sp& fence) { + Mutex::Autolock lock(mMutex); + return addReleaseFenceLocked(slot, graphicBuffer, fence); +} + +status_t GonkConsumerBase::addReleaseFenceLocked(int slot, + const sp graphicBuffer, const sp& fence) { + ALOGV("addReleaseFenceLocked: slot=%d", slot); + + // If consumer no longer tracks this graphicBuffer, we can safely + // drop this fence, as it will never be received by the producer. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + + if (!mSlots[slot].mFence.get()) { + mSlots[slot].mFence = fence; + } else { + sp mergedFence = Fence::merge( + String8::format("%.28s:%d", mName.string(), slot), + mSlots[slot].mFence, fence); + if (!mergedFence.get()) { + ALOGE("failed to merge release fences"); + // synchronization is broken, the best we can do is hope fences + // signal in order so the new fence will act like a union + mSlots[slot].mFence = fence; + return BAD_VALUE; + } + mSlots[slot].mFence = mergedFence; + } + + return OK; +} + +status_t GonkConsumerBase::releaseBufferLocked(int slot, const sp graphicBuffer) { + // If consumer no longer tracks this graphicBuffer (we received a new + // buffer on the same slot), the buffer producer is definitely no longer + // tracking it. + if (!stillTracking(slot, graphicBuffer)) { + return OK; + } + + ALOGV("releaseBufferLocked: slot=%d/%" PRIu64, + slot, mSlots[slot].mFrameNumber); + status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, mSlots[slot].mFence); + if (err == IGonkGraphicBufferConsumer::STALE_BUFFER_SLOT) { + freeBufferLocked(slot); + } + + mSlots[slot].mFence = Fence::NO_FENCE; + + return err; +} + +bool GonkConsumerBase::stillTracking(int slot, + const sp graphicBuffer) { + if (slot < 0 || slot >= GonkBufferQueue::NUM_BUFFER_SLOTS) { + return false; + } + return (mSlots[slot].mGraphicBuffer != NULL && + mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle); +} + +} // namespace android diff --git a/widget/gonk/nativewindow/GonkConsumerBaseLL.h b/widget/gonk/nativewindow/GonkConsumerBaseLL.h new file mode 100644 index 000000000000..ba7b6c33badd --- /dev/null +++ b/widget/gonk/nativewindow/GonkConsumerBaseLL.h @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_GONKCONSUMERBASE_LL_H +#define NATIVEWINDOW_GONKCONSUMERBASE_LL_H + +#include + +#include +#include +#include +#include + +#include "GonkBufferQueueLL.h" + +namespace android { +// ---------------------------------------------------------------------------- + +class String8; + +// GonkConsumerBase is a base class for GonkBufferQueue consumer end-points. It +// handles common tasks like management of the connection to the GonkBufferQueue +// and the buffer pool. +class GonkConsumerBase : public virtual RefBase, + protected ConsumerListener { +public: + struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called each time an additional frame becomes + // available for consumption. This means that frames that are queued + // while in asynchronous mode only trigger the callback if no previous + // frames are pending. Frames queued while in synchronous mode always + // trigger the callback. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onFrameAvailable() = 0; + }; + + virtual ~GonkConsumerBase(); + + // abandon frees all the buffers and puts the GonkConsumerBase into the + // 'abandoned' state. Once put in this state the GonkConsumerBase can never + // leave it. When in the 'abandoned' state, all methods of the + // IGraphicBufferProducer interface will fail with the NO_INIT error. + // + // Note that while calling this method causes all the buffers to be freed + // from the perspective of the the GonkConsumerBase, if there are additional + // references on the buffers (e.g. if a buffer is referenced by a client + // or by OpenGL ES as a texture) then those buffer will remain allocated. + void abandon(); + + // set the name of the GonkConsumerBase that will be used to identify it in + // log messages. + void setName(const String8& name); + + // dump writes the current state to a string. Child classes should add + // their state to the dump by overriding the dumpLocked method, which is + // called by these methods after locking the mutex. + void dump(String8& result) const; + void dump(String8& result, const char* prefix) const; + + // setFrameAvailableListener sets the listener object that will be notified + // when a new frame becomes available. + void setFrameAvailableListener(const wp& listener); + +private: + GonkConsumerBase(const GonkConsumerBase&); + void operator=(const GonkConsumerBase&); + +protected: + // GonkConsumerBase constructs a new GonkConsumerBase object to consume image + // buffers from the given IGonkGraphicBufferConsumer. + // The controlledByApp flag indicates that this consumer is under the application's + // control. + GonkConsumerBase(const sp& consumer, bool controlledByApp = false); + + // onLastStrongRef gets called by RefBase just before the dtor of the most + // derived class. It is used to clean up the buffers so that GonkConsumerBase + // can coordinate the clean-up by calling into virtual methods implemented + // by the derived classes. This would not be possible from the + // ConsuemrBase dtor because by the time that gets called the derived + // classes have already been destructed. + // + // This methods should not need to be overridden by derived classes, but + // if they are overridden the GonkConsumerBase implementation must be called + // from the derived class. + virtual void onLastStrongRef(const void* id); + + // Implementation of the IConsumerListener interface. These + // calls are used to notify the GonkConsumerBase of asynchronous events in the + // GonkBufferQueue. The onFrameAvailable and onBuffersReleased methods should + // not need to be overridden by derived classes, but if they are overridden + // the GonkConsumerBase implementation must be called from the derived class. + // The GonkConsumerBase version of onSidebandStreamChanged does nothing and can + // be overriden by derived classes if they want the notification. + virtual void onFrameAvailable(); + virtual void onBuffersReleased(); + virtual void onSidebandStreamChanged(); + + // freeBufferLocked frees up the given buffer slot. If the slot has been + // initialized this will release the reference to the GraphicBuffer in that + // slot. Otherwise it has no effect. + // + // Derived classes should override this method to clean up any state they + // keep per slot. If it is overridden, the derived class's implementation + // must call GonkConsumerBase::freeBufferLocked. + // + // This method must be called with mMutex locked. + virtual void freeBufferLocked(int slotIndex); + + // abandonLocked puts the GonkBufferQueue into the abandoned state, causing + // all future operations on it to fail. This method rather than the public + // abandon method should be overridden by child classes to add abandon- + // time behavior. + // + // Derived classes should override this method to clean up any object + // state they keep (as opposed to per-slot state). If it is overridden, + // the derived class's implementation must call GonkConsumerBase::abandonLocked. + // + // This method must be called with mMutex locked. + virtual void abandonLocked(); + + // dumpLocked dumps the current state of the GonkConsumerBase object to the + // result string. Each line is prefixed with the string pointed to by the + // prefix argument. The buffer argument points to a buffer that may be + // used for intermediate formatting data, and the size of that buffer is + // indicated by the size argument. + // + // Derived classes should override this method to dump their internal + // state. If this method is overridden the derived class's implementation + // should call GonkConsumerBase::dumpLocked. + // + // This method must be called with mMutex locked. + virtual void dumpLocked(String8& result, const char* prefix) const; + + // acquireBufferLocked fetches the next buffer from the GonkBufferQueue and + // updates the buffer slot for the buffer returned. + // + // Derived classes should override this method to perform any + // initialization that must take place the first time a buffer is assigned + // to a slot. If it is overridden the derived class's implementation must + // call GonkConsumerBase::acquireBufferLocked. + virtual status_t acquireBufferLocked(IGonkGraphicBufferConsumer::BufferItem *item, + nsecs_t presentWhen); + + // releaseBufferLocked relinquishes control over a buffer, returning that + // control to the GonkBufferQueue. + // + // Derived classes should override this method to perform any cleanup that + // must take place when a buffer is released back to the GonkBufferQueue. If + // it is overridden the derived class's implementation must call + // GonkConsumerBase::releaseBufferLocked. + virtual status_t releaseBufferLocked(int slot, const sp graphicBuffer); + + // returns true iff the slot still has the graphicBuffer in it. + bool stillTracking(int slot, const sp graphicBuffer); + + // addReleaseFence* adds the sync points associated with a fence to the set + // of sync points that must be reached before the buffer in the given slot + // may be used after the slot has been released. This should be called by + // derived classes each time some asynchronous work is kicked off that + // references the buffer. + status_t addReleaseFence(int slot, + const sp graphicBuffer, const sp& fence); + status_t addReleaseFenceLocked(int slot, + const sp graphicBuffer, const sp& fence); + + // Slot contains the information and object references that + // GonkConsumerBase maintains about a GonkBufferQueue buffer slot. + struct Slot { + // mGraphicBuffer is the Gralloc buffer store in the slot or NULL if + // no Gralloc buffer is in the slot. + sp mGraphicBuffer; + + // mFence is a fence which will signal when the buffer associated with + // this buffer slot is no longer being used by the consumer and can be + // overwritten. The buffer can be dequeued before the fence signals; + // the producer is responsible for delaying writes until it signals. + sp mFence; + + // the frame number of the last acquired frame for this slot + uint64_t mFrameNumber; + }; + + // mSlots stores the buffers that have been allocated by the GonkBufferQueue + // for each buffer slot. It is initialized to null pointers, and gets + // filled in with the result of GonkBufferQueue::acquire when the + // client dequeues a buffer from a + // slot that has not yet been used. The buffer allocated to a slot will also + // be replaced if the requested buffer usage or geometry differs from that + // of the buffer allocated to a slot. + Slot mSlots[GonkBufferQueue::NUM_BUFFER_SLOTS]; + + // mAbandoned indicates that the GonkBufferQueue will no longer be used to + // consume images buffers pushed to it using the IGraphicBufferProducer + // interface. It is initialized to false, and set to true in the abandon + // method. A GonkBufferQueue that has been abandoned will return the NO_INIT + // error from all IConsumerBase methods capable of returning an error. + bool mAbandoned; + + // mName is a string used to identify the GonkConsumerBase in log messages. + // It can be set by the setName method. + String8 mName; + + // mFrameAvailableListener is the listener object that will be called when a + // new frame becomes available. If it is not NULL it will be called from + // queueBuffer. + wp mFrameAvailableListener; + + // The GonkConsumerBase has-a GonkBufferQueue and is responsible for creating this object + // if none is supplied + sp mConsumer; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of GonkConsumerBase objects. It must be locked whenever the + // member variables are accessed or when any of the *Locked methods are + // called. + // + // This mutex is intended to be locked by derived classes. + mutable Mutex mMutex; +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // NATIVEWINDOW_GONKCONSUMERBASE_LL_H diff --git a/widget/gonk/nativewindow/GonkNativeWindow.h b/widget/gonk/nativewindow/GonkNativeWindow.h index dfe6a154b998..ed04b9f0dc36 100644 --- a/widget/gonk/nativewindow/GonkNativeWindow.h +++ b/widget/gonk/nativewindow/GonkNativeWindow.h @@ -13,7 +13,9 @@ * limitations under the License. */ -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21 +# include "GonkNativeWindowLL.h" +#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 # include "GonkNativeWindowKK.h" #elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 # include "GonkNativeWindowJB.h" diff --git a/widget/gonk/nativewindow/GonkNativeWindowClient.h b/widget/gonk/nativewindow/GonkNativeWindowClient.h index 3b0c8d7261c2..90ca0561dfa6 100644 --- a/widget/gonk/nativewindow/GonkNativeWindowClient.h +++ b/widget/gonk/nativewindow/GonkNativeWindowClient.h @@ -13,9 +13,11 @@ * limitations under the License. */ -#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21 +# include "GonkNativeWindowClientLL.h" +#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 # include "GonkNativeWindowClientKK.h" -#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 +#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 # include "GonkNativeWindowClientJB.h" #elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION == 15 # include "GonkNativeWindowClientICS.h" diff --git a/widget/gonk/nativewindow/GonkNativeWindowClientJB.cpp b/widget/gonk/nativewindow/GonkNativeWindowClientJB.cpp old mode 100755 new mode 100644 diff --git a/widget/gonk/nativewindow/GonkNativeWindowClientJB.h b/widget/gonk/nativewindow/GonkNativeWindowClientJB.h old mode 100755 new mode 100644 diff --git a/widget/gonk/nativewindow/GonkNativeWindowClientLL.cpp b/widget/gonk/nativewindow/GonkNativeWindowClientLL.cpp new file mode 100644 index 000000000000..2eb73f987d7c --- /dev/null +++ b/widget/gonk/nativewindow/GonkNativeWindowClientLL.cpp @@ -0,0 +1,743 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#define LOG_TAG "GonkNativeWindowClient" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#include + +#include + +#include +#include +#include + +#include + +#include +#include "GonkNativeWindowClientLL.h" + +namespace android { + +GonkNativeWindowClient::GonkNativeWindowClient( + const sp& bufferProducer, + bool controlledByApp) + : mGraphicBufferProducer(bufferProducer) +{ + // Initialize the ANativeWindow function pointers. + ANativeWindow::setSwapInterval = hook_setSwapInterval; + ANativeWindow::dequeueBuffer = hook_dequeueBuffer; + ANativeWindow::cancelBuffer = hook_cancelBuffer; + ANativeWindow::queueBuffer = hook_queueBuffer; + ANativeWindow::query = hook_query; + ANativeWindow::perform = hook_perform; + + ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED; + ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED; + ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED; + ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED; + + const_cast(ANativeWindow::minSwapInterval) = 0; + const_cast(ANativeWindow::maxSwapInterval) = 1; + + mReqWidth = 0; + mReqHeight = 0; + mReqFormat = 0; + mReqUsage = 0; + mTimestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; + mCrop.clear(); + mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mTransform = 0; + mStickyTransform = 0; + mDefaultWidth = 0; + mDefaultHeight = 0; + mUserWidth = 0; + mUserHeight = 0; + mTransformHint = 0; + mConsumerRunningBehind = false; + mConnectedToCpu = false; + mProducerControlledByApp = controlledByApp; + mSwapIntervalZero = false; +} + +GonkNativeWindowClient::~GonkNativeWindowClient() { + if (mConnectedToCpu) { + GonkNativeWindowClient::disconnect(NATIVE_WINDOW_API_CPU); + } +} + +sp GonkNativeWindowClient::getIGraphicBufferProducer() const { + return mGraphicBufferProducer; +} + +void GonkNativeWindowClient::setSidebandStream(const sp& stream) { + mGraphicBufferProducer->setSidebandStream(stream); +} + +void GonkNativeWindowClient::allocateBuffers() { + uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth; + uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight; + mGraphicBufferProducer->allocateBuffers(mSwapIntervalZero, reqWidth, + reqHeight, mReqFormat, mReqUsage); +} + +int GonkNativeWindowClient::hook_setSwapInterval(ANativeWindow* window, int interval) { + GonkNativeWindowClient* c = getSelf(window); + return c->setSwapInterval(interval); +} + +int GonkNativeWindowClient::hook_dequeueBuffer(ANativeWindow* window, + ANativeWindowBuffer** buffer, int* fenceFd) { + GonkNativeWindowClient* c = getSelf(window); + return c->dequeueBuffer(buffer, fenceFd); +} + +int GonkNativeWindowClient::hook_cancelBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd) { + GonkNativeWindowClient* c = getSelf(window); + return c->cancelBuffer(buffer, fenceFd); +} + +int GonkNativeWindowClient::hook_queueBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd) { + GonkNativeWindowClient* c = getSelf(window); + return c->queueBuffer(buffer, fenceFd); +} + +int GonkNativeWindowClient::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer** buffer) { + GonkNativeWindowClient* c = getSelf(window); + ANativeWindowBuffer* buf; + int fenceFd = -1; + int result = c->dequeueBuffer(&buf, &fenceFd); + sp fence(new Fence(fenceFd)); + int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED"); + if (waitResult != OK) { + ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", + waitResult); + c->cancelBuffer(buf, -1); + return waitResult; + } + *buffer = buf; + return result; +} + +int GonkNativeWindowClient::hook_cancelBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + GonkNativeWindowClient* c = getSelf(window); + return c->cancelBuffer(buffer, -1); +} + +int GonkNativeWindowClient::hook_lockBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + GonkNativeWindowClient* c = getSelf(window); + return c->lockBuffer_DEPRECATED(buffer); +} + +int GonkNativeWindowClient::hook_queueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer) { + GonkNativeWindowClient* c = getSelf(window); + return c->queueBuffer(buffer, -1); +} + +int GonkNativeWindowClient::hook_query(const ANativeWindow* window, + int what, int* value) { + const GonkNativeWindowClient* c = getSelf(window); + return c->query(what, value); +} + +int GonkNativeWindowClient::hook_perform(ANativeWindow* window, int operation, ...) { + va_list args; + va_start(args, operation); + GonkNativeWindowClient* c = getSelf(window); + return c->perform(operation, args); +} + +int GonkNativeWindowClient::setSwapInterval(int interval) { + ATRACE_CALL(); + // EGL specification states: + // interval is silently clamped to minimum and maximum implementation + // dependent values before being stored. + + if (interval < minSwapInterval) + interval = minSwapInterval; + + if (interval > maxSwapInterval) + interval = maxSwapInterval; + + return NO_ERROR; +} + +int GonkNativeWindowClient::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::dequeueBuffer"); + + int reqW; + int reqH; + bool swapIntervalZero; + uint32_t reqFormat; + uint32_t reqUsage; + + { + Mutex::Autolock lock(mMutex); + + reqW = mReqWidth ? mReqWidth : mUserWidth; + reqH = mReqHeight ? mReqHeight : mUserHeight; + + swapIntervalZero = mSwapIntervalZero; + reqFormat = mReqFormat; + reqUsage = mReqUsage; + } // Drop the lock so that we can still touch the GonkNativeWindowClient while blocking in IGBP::dequeueBuffer + + int buf = -1; + sp fence; + status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, swapIntervalZero, + reqW, reqH, reqFormat, reqUsage); + + if (result < 0) { + ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer(%d, %d, %d, %d, %d)" + "failed: %d", swapIntervalZero, reqW, reqH, reqFormat, reqUsage, + result); + return result; + } + + Mutex::Autolock lock(mMutex); + + sp& gbuf(mSlots[buf].buffer); + + // this should never happen + ALOGE_IF(fence == NULL, "GonkNativeWindowClient::dequeueBuffer: received null Fence! buf=%d", buf); + + if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { + freeAllBuffers(); + } + + if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == 0) { + result = mGraphicBufferProducer->requestBuffer(buf, &gbuf); + if (result != NO_ERROR) { + ALOGE("dequeueBuffer: IGraphicBufferProducer::requestBuffer failed: %d", result); + mGraphicBufferProducer->cancelBuffer(buf, fence); + return result; + } + } + + if (fence->isValid()) { + *fenceFd = fence->dup(); + if (*fenceFd == -1) { + ALOGE("dequeueBuffer: error duping fence: %d", errno); + // dup() should never fail; something is badly wrong. Soldier on + // and hope for the best; the worst that should happen is some + // visible corruption that lasts until the next frame. + } + } else { + *fenceFd = -1; + } + + *buffer = gbuf.get(); + return OK; +} + +int GonkNativeWindowClient::cancelBuffer(android_native_buffer_t* buffer, + int fenceFd) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::cancelBuffer"); + Mutex::Autolock lock(mMutex); + int i = getSlotFromBufferLocked(buffer); + if (i < 0) { + return i; + } + sp fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + mGraphicBufferProducer->cancelBuffer(i, fence); + return OK; +} + +int GonkNativeWindowClient::getSlotFromBufferLocked( + android_native_buffer_t* buffer) const { + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i].buffer != NULL && + mSlots[i].buffer->handle == buffer->handle) { + return i; + } + } + ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); + return BAD_VALUE; +} + +int GonkNativeWindowClient::lockBuffer_DEPRECATED(android_native_buffer_t* buffer __attribute__((unused))) { + ALOGV("GonkNativeWindowClient::lockBuffer"); + Mutex::Autolock lock(mMutex); + return OK; +} + +int GonkNativeWindowClient::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::queueBuffer"); + Mutex::Autolock lock(mMutex); + int64_t timestamp; + bool isAutoTimestamp = false; + if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { + timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + isAutoTimestamp = true; + ALOGV("GonkNativeWindowClient::queueBuffer making up timestamp: %.2f ms", + timestamp / 1000000.f); + } else { + timestamp = mTimestamp; + } + int i = getSlotFromBufferLocked(buffer); + if (i < 0) { + return i; + } + + + // Make sure the crop rectangle is entirely inside the buffer. + Rect crop; + mCrop.intersect(Rect(buffer->width, buffer->height), &crop); + + sp fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + IGraphicBufferProducer::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, + crop, mScalingMode, mTransform ^ mStickyTransform, mSwapIntervalZero, + fence, mStickyTransform); + status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); + if (err != OK) { + ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); + } + uint32_t numPendingBuffers = 0; + uint32_t hint = 0; + output.deflate(&mDefaultWidth, &mDefaultHeight, &hint, + &numPendingBuffers); + + // Disable transform hint if sticky transform is set. + if (mStickyTransform == 0) { + mTransformHint = hint; + } + + mConsumerRunningBehind = (numPendingBuffers >= 2); + + return err; +} + +int GonkNativeWindowClient::query(int what, int* value) const { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::query"); + { // scope for the lock + Mutex::Autolock lock(mMutex); + switch (what) { + case NATIVE_WINDOW_FORMAT: + if (mReqFormat) { + *value = mReqFormat; + return NO_ERROR; + } + break; + case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { + *value = 0; + return NO_ERROR; + } + case NATIVE_WINDOW_CONCRETE_TYPE: + *value = NATIVE_WINDOW_SURFACE; + return NO_ERROR; + case NATIVE_WINDOW_DEFAULT_WIDTH: + *value = mUserWidth ? mUserWidth : mDefaultWidth; + return NO_ERROR; + case NATIVE_WINDOW_DEFAULT_HEIGHT: + *value = mUserHeight ? mUserHeight : mDefaultHeight; + return NO_ERROR; + case NATIVE_WINDOW_TRANSFORM_HINT: + *value = mTransformHint; + return NO_ERROR; + case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND: { + status_t err = NO_ERROR; + if (!mConsumerRunningBehind) { + *value = 0; + } else { + err = mGraphicBufferProducer->query(what, value); + if (err == NO_ERROR) { + mConsumerRunningBehind = *value; + } + } + return err; + } + } + } + return mGraphicBufferProducer->query(what, value); +} + +int GonkNativeWindowClient::perform(int operation, va_list args) +{ + int res = NO_ERROR; + switch (operation) { + case NATIVE_WINDOW_CONNECT: + // deprecated. must return NO_ERROR. + break; + case NATIVE_WINDOW_DISCONNECT: + // deprecated. must return NO_ERROR. + break; + case NATIVE_WINDOW_SET_USAGE: + res = dispatchSetUsage(args); + break; + case NATIVE_WINDOW_SET_CROP: + res = dispatchSetCrop(args); + break; + case NATIVE_WINDOW_SET_BUFFER_COUNT: + res = dispatchSetBufferCount(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + res = dispatchSetBuffersGeometry(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + res = dispatchSetBuffersTransform(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM: + res = dispatchSetBuffersStickyTransform(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: + res = dispatchSetBuffersTimestamp(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: + res = dispatchSetBuffersDimensions(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS: + res = dispatchSetBuffersUserDimensions(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_FORMAT: + res = dispatchSetBuffersFormat(args); + break; + case NATIVE_WINDOW_LOCK: + res = dispatchLock(args); + break; + case NATIVE_WINDOW_UNLOCK_AND_POST: + res = dispatchUnlockAndPost(args); + break; + case NATIVE_WINDOW_SET_SCALING_MODE: + res = dispatchSetScalingMode(args); + break; + case NATIVE_WINDOW_API_CONNECT: + res = dispatchConnect(args); + break; + case NATIVE_WINDOW_API_DISCONNECT: + res = dispatchDisconnect(args); + break; + case NATIVE_WINDOW_SET_SIDEBAND_STREAM: + res = dispatchSetSidebandStream(args); + break; + default: + res = NAME_NOT_FOUND; + break; + } + return res; +} + +int GonkNativeWindowClient::dispatchConnect(va_list args) { + int api = va_arg(args, int); + return connect(api); +} + +int GonkNativeWindowClient::dispatchDisconnect(va_list args) { + int api = va_arg(args, int); + return disconnect(api); +} + +int GonkNativeWindowClient::dispatchSetUsage(va_list args) { + int usage = va_arg(args, int); + return setUsage(usage); +} + +int GonkNativeWindowClient::dispatchSetCrop(va_list args) { + android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); + return setCrop(reinterpret_cast(rect)); +} + +int GonkNativeWindowClient::dispatchSetBufferCount(va_list args) { + size_t bufferCount = va_arg(args, size_t); + return setBufferCount(bufferCount); +} + +int GonkNativeWindowClient::dispatchSetBuffersGeometry(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + int f = va_arg(args, int); + int err = setBuffersDimensions(w, h); + if (err != 0) { + return err; + } + return setBuffersFormat(f); +} + +int GonkNativeWindowClient::dispatchSetBuffersDimensions(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + return setBuffersDimensions(w, h); +} + +int GonkNativeWindowClient::dispatchSetBuffersUserDimensions(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + return setBuffersUserDimensions(w, h); +} + +int GonkNativeWindowClient::dispatchSetBuffersFormat(va_list args) { + int f = va_arg(args, int); + return setBuffersFormat(f); +} + +int GonkNativeWindowClient::dispatchSetScalingMode(va_list args) { + int m = va_arg(args, int); + return setScalingMode(m); +} + +int GonkNativeWindowClient::dispatchSetBuffersTransform(va_list args) { + int transform = va_arg(args, int); + return setBuffersTransform(transform); +} + +int GonkNativeWindowClient::dispatchSetBuffersStickyTransform(va_list args) { + int transform = va_arg(args, int); + return setBuffersStickyTransform(transform); +} + +int GonkNativeWindowClient::dispatchSetBuffersTimestamp(va_list args) { + int64_t timestamp = va_arg(args, int64_t); + return setBuffersTimestamp(timestamp); +} + +int GonkNativeWindowClient::dispatchLock(va_list args) { + ANativeWindow_Buffer* outBuffer = va_arg(args, ANativeWindow_Buffer*); + ARect* inOutDirtyBounds = va_arg(args, ARect*); + return lock(outBuffer, inOutDirtyBounds); +} + +int GonkNativeWindowClient::dispatchUnlockAndPost(va_list args __attribute__((unused))) { + return unlockAndPost(); +} + +int GonkNativeWindowClient::dispatchSetSidebandStream(va_list args) { + native_handle_t* sH = va_arg(args, native_handle_t*); + sp sidebandHandle = NativeHandle::create(sH, false); + setSidebandStream(sidebandHandle); + return OK; +} + +int GonkNativeWindowClient::connect(int api) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::connect"); + static sp listener = new DummyProducerListener(); + Mutex::Autolock lock(mMutex); + IGraphicBufferProducer::QueueBufferOutput output; + int err = mGraphicBufferProducer->connect(listener, api, true, &output); + if (err == NO_ERROR) { + uint32_t numPendingBuffers = 0; + uint32_t hint = 0; + output.deflate(&mDefaultWidth, &mDefaultHeight, &hint, + &numPendingBuffers); + + // Disable transform hint if sticky transform is set. + if (mStickyTransform == 0) { + mTransformHint = hint; + } + + mConsumerRunningBehind = (numPendingBuffers >= 2); + } + if (!err && api == NATIVE_WINDOW_API_CPU) { + mConnectedToCpu = true; + } + return err; +} + + +int GonkNativeWindowClient::disconnect(int api) { + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::disconnect"); + Mutex::Autolock lock(mMutex); + freeAllBuffers(); + int err = mGraphicBufferProducer->disconnect(api); + if (!err) { + mReqFormat = 0; + mReqWidth = 0; + mReqHeight = 0; + mReqUsage = 0; + mCrop.clear(); + mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mTransform = 0; + mStickyTransform = 0; + + if (api == NATIVE_WINDOW_API_CPU) { + mConnectedToCpu = false; + } + } + return err; +} + +int GonkNativeWindowClient::setUsage(uint32_t reqUsage) +{ + ALOGV("GonkNativeWindowClient::setUsage"); + Mutex::Autolock lock(mMutex); + mReqUsage = reqUsage; + return OK; +} + +int GonkNativeWindowClient::setCrop(Rect const* rect) +{ + ATRACE_CALL(); + + Rect realRect; + if (rect == NULL || rect->isEmpty()) { + realRect.clear(); + } else { + realRect = *rect; + } + + ALOGV("GonkNativeWindowClient::setCrop rect=[%d %d %d %d]", + realRect.left, realRect.top, realRect.right, realRect.bottom); + + Mutex::Autolock lock(mMutex); + mCrop = realRect; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBufferCount(int bufferCount) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setBufferCount"); + Mutex::Autolock lock(mMutex); + + status_t err = mGraphicBufferProducer->setBufferCount(bufferCount); + ALOGE_IF(err, "IGraphicBufferProducer::setBufferCount(%d) returned %s", + bufferCount, strerror(-err)); + + if (err == NO_ERROR) { + freeAllBuffers(); + } + + return err; +} + +int GonkNativeWindowClient::setBuffersDimensions(int w, int h) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setBuffersDimensions"); + + if (w<0 || h<0) + return BAD_VALUE; + + if ((w && !h) || (!w && h)) + return BAD_VALUE; + + Mutex::Autolock lock(mMutex); + mReqWidth = w; + mReqHeight = h; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBuffersUserDimensions(int w, int h) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setBuffersUserDimensions"); + + if (w<0 || h<0) + return BAD_VALUE; + + if ((w && !h) || (!w && h)) + return BAD_VALUE; + + Mutex::Autolock lock(mMutex); + mUserWidth = w; + mUserHeight = h; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBuffersFormat(int format) +{ + ALOGV("GonkNativeWindowClient::setBuffersFormat"); + + if (format<0) + return BAD_VALUE; + + Mutex::Autolock lock(mMutex); + mReqFormat = format; + return NO_ERROR; +} + +int GonkNativeWindowClient::setScalingMode(int mode) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setScalingMode(%d)", mode); + + switch (mode) { + case NATIVE_WINDOW_SCALING_MODE_FREEZE: + case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: + case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: + break; + default: + ALOGE("unknown scaling mode: %d", mode); + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + mScalingMode = mode; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBuffersTransform(int transform) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setBuffersTransform"); + Mutex::Autolock lock(mMutex); + mTransform = transform; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBuffersStickyTransform(int transform) +{ + ATRACE_CALL(); + ALOGV("GonkNativeWindowClient::setBuffersStickyTransform"); + Mutex::Autolock lock(mMutex); + mStickyTransform = transform; + return NO_ERROR; +} + +int GonkNativeWindowClient::setBuffersTimestamp(int64_t timestamp) +{ + ALOGV("GonkNativeWindowClient::setBuffersTimestamp"); + Mutex::Autolock lock(mMutex); + mTimestamp = timestamp; + return NO_ERROR; +} + +void GonkNativeWindowClient::freeAllBuffers() { + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + mSlots[i].buffer = 0; + } +} + +// ---------------------------------------------------------------------- +// the lock/unlock APIs must be used from the same thread + +// ---------------------------------------------------------------------------- + +status_t GonkNativeWindowClient::lock( + ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) +{ + return INVALID_OPERATION; +} + +status_t GonkNativeWindowClient::unlockAndPost() +{ + return INVALID_OPERATION; +} + +}; // namespace android diff --git a/widget/gonk/nativewindow/GonkNativeWindowClientLL.h b/widget/gonk/nativewindow/GonkNativeWindowClientLL.h new file mode 100644 index 000000000000..d20314524c1b --- /dev/null +++ b/widget/gonk/nativewindow/GonkNativeWindowClientLL.h @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_LL_H +#define NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_LL_H + +#include + +#include +#include + +#include +#include +#include + +#include "GonkBufferQueueLL.h" + +struct ANativeWindow_Buffer; + +namespace android { + +/* + * An implementation of ANativeWindow that feeds graphics buffers into a + * GonkBufferQueue. + * + * This is typically used by programs that want to render frames through + * some means (maybe OpenGL, a software renderer, or a hardware decoder) + * and have the frames they create forwarded to SurfaceFlinger for + * compositing. For example, a video decoder could render a frame and call + * eglSwapBuffers(), which invokes ANativeWindow callbacks defined by + * GonkNativeWindowClient. GonkNativeWindowClient then forwards the buffers through Binder IPC + * to the GonkBufferQueue's producer interface, providing the new frame to a + * consumer such as GLConsumer. + */ +class GonkNativeWindowClient + : public ANativeObjectBase +{ +public: + + /* + * creates a GonkNativeWindowClient from the given IGraphicBufferProducer (which concrete + * implementation is a GonkBufferQueue). + * + * GonkNativeWindowClient is mainly state-less while it's disconnected, it can be + * viewed as a glorified IGraphicBufferProducer holder. It's therefore + * safe to create other GonkNativeWindowClients from the same IGraphicBufferProducer. + * + * However, once a GonkNativeWindowClient is connected, it'll prevent other GonkNativeWindowClients + * referring to the same IGraphicBufferProducer to become connected and + * therefore prevent them to be used as actual producers of buffers. + * + * the controlledByApp flag indicates that this GonkNativeWindowClient (producer) is + * controlled by the application. This flag is used at connect time. + */ + GonkNativeWindowClient(const sp& bufferProducer, bool controlledByApp = false); + + /* getIGraphicBufferProducer() returns the IGraphicBufferProducer this + * GonkNativeWindowClient was created with. Usually it's an error to use the + * IGraphicBufferProducer while the GonkNativeWindowClient is connected. + */ + sp getIGraphicBufferProducer() const; + + /* convenience function to check that the given surface is non NULL as + * well as its IGraphicBufferProducer */ + static bool isValid(const sp& surface) { + return surface != NULL && surface->getIGraphicBufferProducer() != NULL; + } + + /* Attaches a sideband buffer stream to the GonkNativeWindowClient's IGraphicBufferProducer. + * + * A sideband stream is a device-specific mechanism for passing buffers + * from the producer to the consumer without using dequeueBuffer/ + * queueBuffer. If a sideband stream is present, the consumer can choose + * whether to acquire buffers from the sideband stream or from the queued + * buffers. + * + * Passing NULL or a different stream handle will detach the previous + * handle if any. + */ + void setSidebandStream(const sp& stream); + + /* Allocates buffers based on the current dimensions/format. + * + * This function will allocate up to the maximum number of buffers + * permitted by the current GonkBufferQueue configuration. It will use the + * default format and dimensions. This is most useful to avoid an allocation + * delay during dequeueBuffer. If there are already the maximum number of + * buffers allocated, this function has no effect. + */ + void allocateBuffers(); + +protected: + virtual ~GonkNativeWindowClient(); + +private: + // can't be copied + GonkNativeWindowClient& operator = (const GonkNativeWindowClient& rhs); + GonkNativeWindowClient(const GonkNativeWindowClient& rhs); + + // ANativeWindow hooks + static int hook_cancelBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd); + static int hook_dequeueBuffer(ANativeWindow* window, + ANativeWindowBuffer** buffer, int* fenceFd); + static int hook_perform(ANativeWindow* window, int operation, ...); + static int hook_query(const ANativeWindow* window, int what, int* value); + static int hook_queueBuffer(ANativeWindow* window, + ANativeWindowBuffer* buffer, int fenceFd); + static int hook_setSwapInterval(ANativeWindow* window, int interval); + + static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer); + static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer** buffer); + static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer); + static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, + ANativeWindowBuffer* buffer); + + int dispatchConnect(va_list args); + int dispatchDisconnect(va_list args); + int dispatchSetBufferCount(va_list args); + int dispatchSetBuffersGeometry(va_list args); + int dispatchSetBuffersDimensions(va_list args); + int dispatchSetBuffersUserDimensions(va_list args); + int dispatchSetBuffersFormat(va_list args); + int dispatchSetScalingMode(va_list args); + int dispatchSetBuffersTransform(va_list args); + int dispatchSetBuffersStickyTransform(va_list args); + int dispatchSetBuffersTimestamp(va_list args); + int dispatchSetCrop(va_list args); + int dispatchSetPostTransformCrop(va_list args); + int dispatchSetUsage(va_list args); + int dispatchLock(va_list args); + int dispatchUnlockAndPost(va_list args); + int dispatchSetSidebandStream(va_list args); + +protected: + virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); + virtual int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd); + virtual int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd); + virtual int perform(int operation, va_list args); + virtual int query(int what, int* value) const; + virtual int setSwapInterval(int interval); + + virtual int lockBuffer_DEPRECATED(ANativeWindowBuffer* buffer); + + virtual int connect(int api); + virtual int disconnect(int api); + virtual int setBufferCount(int bufferCount); + virtual int setBuffersDimensions(int w, int h); + virtual int setBuffersUserDimensions(int w, int h); + virtual int setBuffersFormat(int format); + virtual int setScalingMode(int mode); + virtual int setBuffersTransform(int transform); + virtual int setBuffersStickyTransform(int transform); + virtual int setBuffersTimestamp(int64_t timestamp); + virtual int setCrop(Rect const* rect); + virtual int setUsage(uint32_t reqUsage); + +public: + virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); + virtual int unlockAndPost(); + +protected: + enum { NUM_BUFFER_SLOTS = GonkBufferQueue::NUM_BUFFER_SLOTS }; + enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; + +private: + void freeAllBuffers(); + int getSlotFromBufferLocked(android_native_buffer_t* buffer) const; + + struct BufferSlot { + sp buffer; + Region dirtyRegion; + }; + + // mSurfaceTexture is the interface to the surface texture server. All + // operations on the surface texture client ultimately translate into + // interactions with the server using this interface. + // TODO: rename to mBufferProducer + sp mGraphicBufferProducer; + + // mSlots stores the buffers that have been allocated for each buffer slot. + // It is initialized to null pointers, and gets filled in with the result of + // IGraphicBufferProducer::requestBuffer when the client dequeues a buffer from a + // slot that has not yet been used. The buffer allocated to a slot will also + // be replaced if the requested buffer usage or geometry differs from that + // of the buffer allocated to a slot. + BufferSlot mSlots[NUM_BUFFER_SLOTS]; + + // mReqWidth is the buffer width that will be requested at the next dequeue + // operation. It is initialized to 1. + uint32_t mReqWidth; + + // mReqHeight is the buffer height that will be requested at the next + // dequeue operation. It is initialized to 1. + uint32_t mReqHeight; + + // mReqFormat is the buffer pixel format that will be requested at the next + // deuque operation. It is initialized to PIXEL_FORMAT_RGBA_8888. + uint32_t mReqFormat; + + // mReqUsage is the set of buffer usage flags that will be requested + // at the next deuque operation. It is initialized to 0. + uint32_t mReqUsage; + + // mTimestamp is the timestamp that will be used for the next buffer queue + // operation. It defaults to NATIVE_WINDOW_TIMESTAMP_AUTO, which means that + // a timestamp is auto-generated when queueBuffer is called. + int64_t mTimestamp; + + // mCrop is the crop rectangle that will be used for the next buffer + // that gets queued. It is set by calling setCrop. + Rect mCrop; + + // mScalingMode is the scaling mode that will be used for the next + // buffers that get queued. It is set by calling setScalingMode. + int mScalingMode; + + // mTransform is the transform identifier that will be used for the next + // buffer that gets queued. It is set by calling setTransform. + uint32_t mTransform; + + // mStickyTransform is a transform that is applied on top of mTransform + // in each buffer that is queued. This is typically used to force the + // compositor to apply a transform, and will prevent the transform hint + // from being set by the compositor. + uint32_t mStickyTransform; + + // mDefaultWidth is default width of the buffers, regardless of the + // native_window_set_buffers_dimensions call. + uint32_t mDefaultWidth; + + // mDefaultHeight is default height of the buffers, regardless of the + // native_window_set_buffers_dimensions call. + uint32_t mDefaultHeight; + + // mUserWidth, if non-zero, is an application-specified override + // of mDefaultWidth. This is lower priority than the width set by + // native_window_set_buffers_dimensions. + uint32_t mUserWidth; + + // mUserHeight, if non-zero, is an application-specified override + // of mDefaultHeight. This is lower priority than the height set + // by native_window_set_buffers_dimensions. + uint32_t mUserHeight; + + // mTransformHint is the transform probably applied to buffers of this + // window. this is only a hint, actual transform may differ. + uint32_t mTransformHint; + + // mProducerControlledByApp whether this buffer producer is controlled + // by the application + bool mProducerControlledByApp; + + // mSwapIntervalZero set if we should drop buffers at queue() time to + // achieve an asynchronous swap interval + bool mSwapIntervalZero; + + // mConsumerRunningBehind whether the consumer is running more than + // one buffer behind the producer. + mutable bool mConsumerRunningBehind; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of GonkNativeWindowClient objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; + + // must be used from the lock/unlock thread + sp mLockedBuffer; + sp mPostedBuffer; + bool mConnectedToCpu; + + // must be accessed from lock/unlock thread only + Region mDirtyRegion; +}; + +}; // namespace android + +#endif // NATIVEWINDOW_GONKNATIVEWINDOWCLIENT_LL_H diff --git a/widget/gonk/nativewindow/GonkNativeWindowJB.cpp b/widget/gonk/nativewindow/GonkNativeWindowJB.cpp old mode 100755 new mode 100644 diff --git a/widget/gonk/nativewindow/GonkNativeWindowJB.h b/widget/gonk/nativewindow/GonkNativeWindowJB.h old mode 100755 new mode 100644 diff --git a/widget/gonk/nativewindow/GonkNativeWindowLL.cpp b/widget/gonk/nativewindow/GonkNativeWindowLL.cpp new file mode 100644 index 000000000000..ee939ce740ba --- /dev/null +++ b/widget/gonk/nativewindow/GonkNativeWindowLL.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "GonkNativeWindow" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include + +#include "GonkNativeWindowLL.h" + +using namespace mozilla; +using namespace mozilla::layers; + +namespace android { + +GonkNativeWindow::GonkNativeWindow( + const sp& consumer, int bufferCount) : + GonkConsumerBase(consumer, false), + mNewFrameCallback(nullptr) +{ + if (bufferCount != DEFAULT_MAX_BUFFERS) { + status_t err = mConsumer->setMaxAcquiredBufferCount(bufferCount); + LOG_ALWAYS_FATAL_IF(err != OK, + "Failed to set max acquired buffer count to %d", bufferCount); + } +} + +GonkNativeWindow::GonkNativeWindow( + const sp& consumer, uint32_t consumerUsage, + int bufferCount, bool controlledByApp) : + GonkConsumerBase(consumer, controlledByApp) +{ + status_t err = mConsumer->setConsumerUsageBits(consumerUsage); + LOG_ALWAYS_FATAL_IF(err != OK, + "Failed to set consumer usage bits to %#x", consumerUsage); + if (bufferCount != DEFAULT_MAX_BUFFERS) { + err = mConsumer->setMaxAcquiredBufferCount(bufferCount); + LOG_ALWAYS_FATAL_IF(err != OK, + "Failed to set max acquired buffer count to %d", bufferCount); + } +} + +GonkNativeWindow::~GonkNativeWindow() { +} + +void GonkNativeWindow::setName(const String8& name) { + Mutex::Autolock _l(mMutex); + mName = name; + mConsumer->setConsumerName(name); +} + +status_t GonkNativeWindow::acquireBuffer(BufferItem *item, + nsecs_t presentWhen, bool waitForFence) { + status_t err; + + if (!item) return BAD_VALUE; + + Mutex::Autolock _l(mMutex); + + err = acquireBufferLocked(item, presentWhen); + if (err != OK) { + if (err != NO_BUFFER_AVAILABLE) { + ALOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + } + return err; + } + + if (waitForFence) { + err = item->mFence->waitForever("GonkNativeWindow::acquireBuffer"); + if (err != OK) { + ALOGE("Failed to wait for fence of acquired buffer: %s (%d)", + strerror(-err), err); + return err; + } + } + + item->mGraphicBuffer = mSlots[item->mBuf].mGraphicBuffer; + + return OK; +} + +status_t GonkNativeWindow::releaseBuffer(const BufferItem &item, + const sp& releaseFence) { + status_t err; + + Mutex::Autolock _l(mMutex); + + err = addReleaseFenceLocked(item.mBuf, item.mGraphicBuffer, releaseFence); + + err = releaseBufferLocked(item.mBuf, item.mGraphicBuffer); + if (err != OK) { + ALOGE("Failed to release buffer: %s (%d)", + strerror(-err), err); + } + return err; +} + +status_t GonkNativeWindow::setDefaultBufferSize(uint32_t w, uint32_t h) { + Mutex::Autolock _l(mMutex); + return mConsumer->setDefaultBufferSize(w, h); +} + +status_t GonkNativeWindow::setDefaultBufferFormat(uint32_t defaultFormat) { + Mutex::Autolock _l(mMutex); + return mConsumer->setDefaultBufferFormat(defaultFormat); +} + +TemporaryRef +GonkNativeWindow::getCurrentBuffer() { + Mutex::Autolock _l(mMutex); + BufferItem item; + + // In asynchronous mode the list is guaranteed to be one buffer + // deep, while in synchronous mode we use the oldest buffer. + status_t err = acquireBufferLocked(&item, 0); //??? + if (err != NO_ERROR) { + return NULL; + } + + RefPtr textureClient = + mConsumer->getTextureClientFromBuffer(item.mGraphicBuffer.get()); + if (!textureClient) { + return NULL; + } + textureClient->SetRecycleCallback(GonkNativeWindow::RecycleCallback, this); + return textureClient; +} + +/* static */ void +GonkNativeWindow::RecycleCallback(TextureClient* client, void* closure) { + GonkNativeWindow* nativeWindow = + static_cast(closure); + + client->ClearRecycleCallback(); + nativeWindow->returnBuffer(client); +} + +void GonkNativeWindow::returnBuffer(TextureClient* client) { + ALOGD("GonkNativeWindow::returnBuffer"); + Mutex::Autolock lock(mMutex); + + int index = mConsumer->getSlotFromTextureClientLocked(client); + if (index < 0) { + return; + } + + sp fence = client->GetReleaseFenceHandle().mFence; + if (!fence.get()) { + fence = Fence::NO_FENCE; + } + + status_t err; + err = addReleaseFenceLocked(index, + mSlots[index].mGraphicBuffer, + fence); + + err = releaseBufferLocked(index, mSlots[index].mGraphicBuffer); + + if (err != OK) { + ALOGE("Failed to return buffer: %s (%d)", strerror(-err), err); + } +} + +TemporaryRef +GonkNativeWindow::getTextureClientFromBuffer(ANativeWindowBuffer* buffer) { + Mutex::Autolock lock(mMutex); + return mConsumer->getTextureClientFromBuffer(buffer); +} + +void GonkNativeWindow::setNewFrameCallback( + GonkNativeWindowNewFrameCallback* callback) { + ALOGD("setNewFrameCallback"); + Mutex::Autolock lock(mMutex); + mNewFrameCallback = callback; +} + +void GonkNativeWindow::onFrameAvailable() { + GonkConsumerBase::onFrameAvailable(); + + if (mNewFrameCallback) { + mNewFrameCallback->OnNewFrame(); + } +} + +} // namespace android diff --git a/widget/gonk/nativewindow/GonkNativeWindowLL.h b/widget/gonk/nativewindow/GonkNativeWindowLL.h new file mode 100644 index 000000000000..a257cc562f37 --- /dev/null +++ b/widget/gonk/nativewindow/GonkNativeWindowLL.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2014 Mozilla Foundation + * + * 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. + */ + +#ifndef NATIVEWINDOW_GONKNATIVEWINDOW_LL_H +#define NATIVEWINDOW_GONKNATIVEWINDOW_LL_H + +#include + +#include +#include +#include + +#include "GonkConsumerBaseLL.h" +#include "IGonkGraphicBufferConsumerLL.h" + +namespace android { + +// The user of GonkNativeWindow who wants to receive notification of +// new frames should implement this interface. +class GonkNativeWindowNewFrameCallback { +public: + virtual void OnNewFrame() = 0; +}; + +/** + * GonkNativeWindow is a GonkBufferQueue consumer endpoint that allows clients + * access to the whole BufferItem entry from GonkBufferQueue. Multiple buffers may + * be acquired at once, to be used concurrently by the client. This consumer can + * operate either in synchronous or asynchronous mode. + */ +class GonkNativeWindow: public GonkConsumerBase +{ + typedef mozilla::layers::TextureClient TextureClient; + public: + typedef GonkConsumerBase::FrameAvailableListener FrameAvailableListener; + typedef GonkBufferQueue::BufferItem BufferItem; + + enum { DEFAULT_MAX_BUFFERS = -1 }; + enum { INVALID_BUFFER_SLOT = GonkBufferQueue::INVALID_BUFFER_SLOT }; + enum { NO_BUFFER_AVAILABLE = GonkBufferQueue::NO_BUFFER_AVAILABLE }; + + // Create a new buffer item consumer. The consumerUsage parameter determines + // the consumer usage flags passed to the graphics allocator. The + // bufferCount parameter specifies how many buffers can be locked for user + // access at the same time. + // controlledByApp tells whether this consumer is controlled by the + // application. + GonkNativeWindow(const sp& consumer, + int bufferCount = DEFAULT_MAX_BUFFERS); + GonkNativeWindow(const sp& consumer, + uint32_t consumerUsage, int bufferCount = DEFAULT_MAX_BUFFERS, + bool controlledByApp = false); + + virtual ~GonkNativeWindow(); + + // set the name of the GonkNativeWindow that will be used to identify it in + // log messages. + void setName(const String8& name); + + // Gets the next graphics buffer from the producer, filling out the + // passed-in BufferItem structure. Returns NO_BUFFER_AVAILABLE if the queue + // of buffers is empty, and INVALID_OPERATION if the maximum number of + // buffers is already acquired. + // + // Only a fixed number of buffers can be acquired at a time, determined by + // the construction-time bufferCount parameter. If INVALID_OPERATION is + // returned by acquireBuffer, then old buffers must be returned to the + // queue by calling releaseBuffer before more buffers can be acquired. + // + // If waitForFence is true, and the acquired BufferItem has a valid fence object, + // acquireBuffer will wait on the fence with no timeout before returning. + status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen, + bool waitForFence = true); + + // Returns an acquired buffer to the queue, allowing it to be reused. Since + // only a fixed number of buffers may be acquired at a time, old buffers + // must be released by calling releaseBuffer to ensure new buffers can be + // acquired by acquireBuffer. Once a BufferItem is released, the caller must + // not access any members of the BufferItem, and should immediately remove + // all of its references to the BufferItem itself. + status_t releaseBuffer(const BufferItem &item, + const sp& releaseFence = Fence::NO_FENCE); + + // setDefaultBufferSize is used to set the size of buffers returned by + // requestBuffers when a with and height of zero is requested. + status_t setDefaultBufferSize(uint32_t w, uint32_t h); + + // setDefaultBufferFormat allows the GonkBufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // in dequeueBuffer + status_t setDefaultBufferFormat(uint32_t defaultFormat); + + // Get next frame from the queue, caller owns the returned buffer. + mozilla::TemporaryRef getCurrentBuffer(); + + // Return the buffer to the queue and mark it as FREE. After that + // the buffer is useable again for the decoder. + void returnBuffer(TextureClient* client); + + mozilla::TemporaryRef getTextureClientFromBuffer(ANativeWindowBuffer* buffer); + + void setNewFrameCallback(GonkNativeWindowNewFrameCallback* callback); + + static void RecycleCallback(TextureClient* client, void* closure); + +protected: + virtual void onFrameAvailable(); + +private: + GonkNativeWindowNewFrameCallback* mNewFrameCallback; +}; + +} // namespace android + +#endif // NATIVEWINDOW_GONKNATIVEWINDOW_LL_H diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h b/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h index b933bff4c9e2..14541c9b474b 100644 --- a/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h +++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.h @@ -1,11 +1,10 @@ -/* - * Copyright (C) 2013 The Android Open Source Project +/* Copyright 2013 Mozilla Foundation and Mozilla contributors * * 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 + * 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, @@ -14,215 +13,8 @@ * limitations under the License. */ -#ifndef ANDROID_GUI_IGONKGRAPHICBUFFERCONSUMER_H -#define ANDROID_GUI_IGONKGRAPHICBUFFERCONSUMER_H - -#include -#include - -#include -#include -#include - -#include -#include - -#include "mozilla/Types.h" -#include "mozilla/layers/LayersSurfaces.h" - -namespace mozilla { - -namespace layers { -class TextureClient; -} -} - -namespace android { -// ---------------------------------------------------------------------------- - -class MOZ_EXPORT IConsumerListener; -class MOZ_EXPORT GraphicBuffer; -class MOZ_EXPORT Fence; - -class IGonkGraphicBufferConsumer : public IInterface { - typedef mozilla::layers::TextureClient TextureClient; -public: - - // public facing structure for BufferSlot - class BufferItem : public Flattenable { - friend class Flattenable; - size_t getPodSize() const; - size_t getFlattenedSize() const; - size_t getFdCount() const; - status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; - status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); - - public: - enum { INVALID_BUFFER_SLOT = -1 }; - BufferItem(); - - // mGraphicBuffer points to the buffer allocated for this slot, or is NULL - // if the buffer in this slot has been acquired in the past (see - // BufferSlot.mAcquireCalled). - sp mGraphicBuffer; - - // mFence is a fence that will signal when the buffer is idle. - sp mFence; - - // mCrop is the current crop rectangle for this buffer slot. - Rect mCrop; - - // mTransform is the current transform flags for this buffer slot. - uint32_t mTransform; - - // mScalingMode is the current scaling mode for this buffer slot. - uint32_t mScalingMode; - - // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. - int64_t mTimestamp; - - // mIsAutoTimestamp indicates whether mTimestamp was generated - // automatically when the buffer was queued. - bool mIsAutoTimestamp; - - // mFrameNumber is the number of the queued frame for this slot. - uint64_t mFrameNumber; - - // mBuf is the slot index of this buffer - int mBuf; - - // mIsDroppable whether this buffer was queued with the - // property that it can be replaced by a new buffer for the purpose of - // making sure dequeueBuffer() won't block. - // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer - // was queued. - bool mIsDroppable; - - // Indicates whether this buffer has been seen by a consumer yet - bool mAcquireCalled; - - // Indicates this buffer must be transformed by the inverse transform of the screen - // it is displayed onto. This is applied after mTransform. - bool mTransformToDisplayInverse; - }; - - - // acquireBuffer attempts to acquire ownership of the next pending buffer in - // the BufferQueue. If no buffer is pending then it returns -EINVAL. If a - // buffer is successfully acquired, the information about the buffer is - // returned in BufferItem. If the buffer returned had previously been - // acquired then the BufferItem::mGraphicBuffer field of buffer is set to - // NULL and it is assumed that the consumer still holds a reference to the - // buffer. - // - // If presentWhen is nonzero, it indicates the time when the buffer will - // be displayed on screen. If the buffer's timestamp is farther in the - // future, the buffer won't be acquired, and PRESENT_LATER will be - // returned. The presentation time is in nanoseconds, and the time base - // is CLOCK_MONOTONIC. - virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) = 0; - - // releaseBuffer releases a buffer slot from the consumer back to the - // BufferQueue. This may be done while the buffer's contents are still - // being accessed. The fence will signal when the buffer is no longer - // in use. frameNumber is used to indentify the exact buffer returned. - // - // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free - // any references to the just-released buffer that it might have, as if it - // had received a onBuffersReleased() call with a mask set for the released - // buffer. - // - // Note that the dependencies on EGL will be removed once we switch to using - // the Android HW Sync HAL. - virtual status_t releaseBuffer(int buf, uint64_t frameNumber, const sp& releaseFence) = 0; - - // consumerConnect connects a consumer to the BufferQueue. Only one - // consumer may be connected, and when that consumer disconnects the - // BufferQueue is placed into the "abandoned" state, causing most - // interactions with the BufferQueue by the producer to fail. - // controlledByApp indicates whether the consumer is controlled by - // the application. - // - // consumer may not be NULL. - virtual status_t consumerConnect(const sp& consumer, bool controlledByApp) = 0; - - // consumerDisconnect disconnects a consumer from the BufferQueue. All - // buffers will be freed and the BufferQueue is placed in the "abandoned" - // state, causing most interactions with the BufferQueue by the producer to - // fail. - virtual status_t consumerDisconnect() = 0; - - // getReleasedBuffers sets the value pointed to by slotMask to a bit mask - // indicating which buffer slots have been released by the BufferQueue - // but have not yet been released by the consumer. - // - // This should be called from the onBuffersReleased() callback. - virtual status_t getReleasedBuffers(uint32_t* slotMask) = 0; - - // setDefaultBufferSize is used to set the size of buffers returned by - // dequeueBuffer when a width and height of zero is requested. Default - // is 1x1. - virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; - - // setDefaultMaxBufferCount sets the default value for the maximum buffer - // count (the initial default is 2). If the producer has requested a - // buffer count using setBufferCount, the default buffer count will only - // take effect if the producer sets the count back to zero. - // - // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. - virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0; - - // disableAsyncBuffer disables the extra buffer used in async mode - // (when both producer and consumer have set their "isControlledByApp" - // flag) and has dequeueBuffer() return WOULD_BLOCK instead. - // - // This can only be called before consumerConnect(). - virtual status_t disableAsyncBuffer() = 0; - - // setMaxAcquiredBufferCount sets the maximum number of buffers that can - // be acquired by the consumer at one time (default 1). This call will - // fail if a producer is connected to the BufferQueue. - virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; - - // setConsumerName sets the name used in logging - virtual void setConsumerName(const String8& name) = 0; - - // setDefaultBufferFormat allows the BufferQueue to create - // GraphicBuffers of a defaultFormat if no format is specified - // in dequeueBuffer. Formats are enumerated in graphics.h; the - // initial default is HAL_PIXEL_FORMAT_RGBA_8888. - virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0; - - // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. - // These are merged with the bits passed to dequeueBuffer. The values are - // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. - virtual status_t setConsumerUsageBits(uint32_t usage) = 0; - - // setTransformHint bakes in rotation to buffers so overlays can be used. - // The values are enumerated in window.h, e.g. - // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). - virtual status_t setTransformHint(uint32_t hint) = 0; - - // dump state into a string - virtual void dumpToString(String8& result, const char* prefix) const = 0; - -public: - DECLARE_META_INTERFACE(GonkGraphicBufferConsumer); -}; - -// ---------------------------------------------------------------------------- - -class BnGonkGraphicBufferConsumer : public BnInterface -{ -public: - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21 +# include "IGonkGraphicBufferConsumerLL.h" +#elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19 +# include "IGonkGraphicBufferConsumerKK.h" +#endif diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.cpp b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.cpp similarity index 99% rename from widget/gonk/nativewindow/IGonkGraphicBufferConsumer.cpp rename to widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.cpp index c1fcaee15acd..c4c9f65782f3 100644 --- a/widget/gonk/nativewindow/IGonkGraphicBufferConsumer.cpp +++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.cpp @@ -25,7 +25,7 @@ #include #include -#include "IGonkGraphicBufferConsumer.h" +#include "IGonkGraphicBufferConsumerKK.h" #include #include diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.h b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.h new file mode 100644 index 000000000000..ce51e1ef2259 --- /dev/null +++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerKK.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef NATIVEWINDOW_IGONKGRAPHICBUFFERCONSUMER_KK_H +#define NATIVEWINDOW_IGONKGRAPHICBUFFERCONSUMER_KK_H + +#include +#include + +#include +#include +#include + +#include +#include + +#include "mozilla/Types.h" +#include "mozilla/layers/LayersSurfaces.h" + +namespace mozilla { + +namespace layers { +class TextureClient; +} +} + +namespace android { +// ---------------------------------------------------------------------------- + +class MOZ_EXPORT IConsumerListener; +class MOZ_EXPORT GraphicBuffer; +class MOZ_EXPORT Fence; + +class IGonkGraphicBufferConsumer : public IInterface { + typedef mozilla::layers::TextureClient TextureClient; +public: + + // public facing structure for BufferSlot + class BufferItem : public Flattenable { + friend class Flattenable; + size_t getPodSize() const; + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + public: + enum { INVALID_BUFFER_SLOT = -1 }; + BufferItem(); + + // mGraphicBuffer points to the buffer allocated for this slot, or is NULL + // if the buffer in this slot has been acquired in the past (see + // BufferSlot.mAcquireCalled). + sp mGraphicBuffer; + + // mFence is a fence that will signal when the buffer is idle. + sp mFence; + + // mCrop is the current crop rectangle for this buffer slot. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. + int64_t mTimestamp; + + // mIsAutoTimestamp indicates whether mTimestamp was generated + // automatically when the buffer was queued. + bool mIsAutoTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + + // mBuf is the slot index of this buffer + int mBuf; + + // mIsDroppable whether this buffer was queued with the + // property that it can be replaced by a new buffer for the purpose of + // making sure dequeueBuffer() won't block. + // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer + // was queued. + bool mIsDroppable; + + // Indicates whether this buffer has been seen by a consumer yet + bool mAcquireCalled; + + // Indicates this buffer must be transformed by the inverse transform of the screen + // it is displayed onto. This is applied after mTransform. + bool mTransformToDisplayInverse; + }; + + + // acquireBuffer attempts to acquire ownership of the next pending buffer in + // the BufferQueue. If no buffer is pending then it returns -EINVAL. If a + // buffer is successfully acquired, the information about the buffer is + // returned in BufferItem. If the buffer returned had previously been + // acquired then the BufferItem::mGraphicBuffer field of buffer is set to + // NULL and it is assumed that the consumer still holds a reference to the + // buffer. + // + // If presentWhen is nonzero, it indicates the time when the buffer will + // be displayed on screen. If the buffer's timestamp is farther in the + // future, the buffer won't be acquired, and PRESENT_LATER will be + // returned. The presentation time is in nanoseconds, and the time base + // is CLOCK_MONOTONIC. + virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) = 0; + + // releaseBuffer releases a buffer slot from the consumer back to the + // BufferQueue. This may be done while the buffer's contents are still + // being accessed. The fence will signal when the buffer is no longer + // in use. frameNumber is used to indentify the exact buffer returned. + // + // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free + // any references to the just-released buffer that it might have, as if it + // had received a onBuffersReleased() call with a mask set for the released + // buffer. + // + // Note that the dependencies on EGL will be removed once we switch to using + // the Android HW Sync HAL. + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, const sp& releaseFence) = 0; + + // consumerConnect connects a consumer to the BufferQueue. Only one + // consumer may be connected, and when that consumer disconnects the + // BufferQueue is placed into the "abandoned" state, causing most + // interactions with the BufferQueue by the producer to fail. + // controlledByApp indicates whether the consumer is controlled by + // the application. + // + // consumer may not be NULL. + virtual status_t consumerConnect(const sp& consumer, bool controlledByApp) = 0; + + // consumerDisconnect disconnects a consumer from the BufferQueue. All + // buffers will be freed and the BufferQueue is placed in the "abandoned" + // state, causing most interactions with the BufferQueue by the producer to + // fail. + virtual status_t consumerDisconnect() = 0; + + // getReleasedBuffers sets the value pointed to by slotMask to a bit mask + // indicating which buffer slots have been released by the BufferQueue + // but have not yet been released by the consumer. + // + // This should be called from the onBuffersReleased() callback. + virtual status_t getReleasedBuffers(uint32_t* slotMask) = 0; + + // setDefaultBufferSize is used to set the size of buffers returned by + // dequeueBuffer when a width and height of zero is requested. Default + // is 1x1. + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; + + // setDefaultMaxBufferCount sets the default value for the maximum buffer + // count (the initial default is 2). If the producer has requested a + // buffer count using setBufferCount, the default buffer count will only + // take effect if the producer sets the count back to zero. + // + // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0; + + // disableAsyncBuffer disables the extra buffer used in async mode + // (when both producer and consumer have set their "isControlledByApp" + // flag) and has dequeueBuffer() return WOULD_BLOCK instead. + // + // This can only be called before consumerConnect(). + virtual status_t disableAsyncBuffer() = 0; + + // setMaxAcquiredBufferCount sets the maximum number of buffers that can + // be acquired by the consumer at one time (default 1). This call will + // fail if a producer is connected to the BufferQueue. + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; + + // setConsumerName sets the name used in logging + virtual void setConsumerName(const String8& name) = 0; + + // setDefaultBufferFormat allows the BufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // in dequeueBuffer. Formats are enumerated in graphics.h; the + // initial default is HAL_PIXEL_FORMAT_RGBA_8888. + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0; + + // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. + // These are merged with the bits passed to dequeueBuffer. The values are + // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. + virtual status_t setConsumerUsageBits(uint32_t usage) = 0; + + // setTransformHint bakes in rotation to buffers so overlays can be used. + // The values are enumerated in window.h, e.g. + // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). + virtual status_t setTransformHint(uint32_t hint) = 0; + + // dump state into a string + virtual void dumpToString(String8& result, const char* prefix) const = 0; + +public: + DECLARE_META_INTERFACE(GonkGraphicBufferConsumer); +}; + +// ---------------------------------------------------------------------------- + +class BnGonkGraphicBufferConsumer : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.cpp b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.cpp new file mode 100644 index 000000000000..5e570298e1ca --- /dev/null +++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.cpp @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2013 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. + */ + +#include +#include + +#include +#include + +#include +#include + +#include +#include "IGonkGraphicBufferConsumerLL.h" + +#include +#include + +#include + +#include "mozilla/layers/TextureClient.h" + +namespace android { +// --------------------------------------------------------------------------- + +IGonkGraphicBufferConsumer::BufferItem::BufferItem() : + mTransform(0), + mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), + mTimestamp(0), + mIsAutoTimestamp(false), + mFrameNumber(0), + mBuf(INVALID_BUFFER_SLOT), + mIsDroppable(false), + mAcquireCalled(false), + mTransformToDisplayInverse(false) { + mCrop.makeInvalid(); +} + +size_t IGonkGraphicBufferConsumer::BufferItem::getPodSize() const { + size_t c = sizeof(mCrop) + + sizeof(mTransform) + + sizeof(mScalingMode) + + sizeof(mTimestamp) + + sizeof(mIsAutoTimestamp) + + sizeof(mFrameNumber) + + sizeof(mBuf) + + sizeof(mIsDroppable) + + sizeof(mAcquireCalled) + + sizeof(mTransformToDisplayInverse); + return c; +} + +size_t IGonkGraphicBufferConsumer::BufferItem::getFlattenedSize() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFlattenedSize(); + c = FlattenableUtils::align<4>(c); + } + if (mFence != 0) { + c += mFence->getFlattenedSize(); + c = FlattenableUtils::align<4>(c); + } + return sizeof(int32_t) + c + getPodSize(); +} + +size_t IGonkGraphicBufferConsumer::BufferItem::getFdCount() const { + size_t c = 0; + if (mGraphicBuffer != 0) { + c += mGraphicBuffer->getFdCount(); + } + if (mFence != 0) { + c += mFence->getFdCount(); + } + return c; +} + +static void writeBoolAsInt(void*& buffer, size_t& size, bool b) { + FlattenableUtils::write(buffer, size, static_cast(b)); +} + +static bool readBoolFromInt(void const*& buffer, size_t& size) { + int32_t i; + FlattenableUtils::read(buffer, size, i); + return static_cast(i); +} + +status_t IGonkGraphicBufferConsumer::BufferItem::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + + // make sure we have enough space + if (size < BufferItem::getFlattenedSize()) { + return NO_MEMORY; + } + + // content flags are stored first + uint32_t& flags = *static_cast(buffer); + + // advance the pointer + FlattenableUtils::advance(buffer, size, sizeof(uint32_t)); + + flags = 0; + if (mGraphicBuffer != 0) { + status_t err = mGraphicBuffer->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 1; + } + if (mFence != 0) { + status_t err = mFence->flatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + flags |= 2; + } + + // check we have enough space (in case flattening the fence/graphicbuffer lied to us) + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, mCrop); + FlattenableUtils::write(buffer, size, mTransform); + FlattenableUtils::write(buffer, size, mScalingMode); + FlattenableUtils::write(buffer, size, mTimestamp); + writeBoolAsInt(buffer, size, mIsAutoTimestamp); + FlattenableUtils::write(buffer, size, mFrameNumber); + FlattenableUtils::write(buffer, size, mBuf); + writeBoolAsInt(buffer, size, mIsDroppable); + writeBoolAsInt(buffer, size, mAcquireCalled); + writeBoolAsInt(buffer, size, mTransformToDisplayInverse); + + return NO_ERROR; +} + +status_t IGonkGraphicBufferConsumer::BufferItem::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + + if (size < sizeof(uint32_t)) + return NO_MEMORY; + + uint32_t flags = 0; + FlattenableUtils::read(buffer, size, flags); + + if (flags & 1) { + mGraphicBuffer = new GraphicBuffer(); + status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + if (flags & 2) { + mFence = new Fence(); + status_t err = mFence->unflatten(buffer, size, fds, count); + if (err) return err; + size -= FlattenableUtils::align<4>(buffer); + } + + // check we have enough space + if (size < getPodSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, mCrop); + FlattenableUtils::read(buffer, size, mTransform); + FlattenableUtils::read(buffer, size, mScalingMode); + FlattenableUtils::read(buffer, size, mTimestamp); + mIsAutoTimestamp = readBoolFromInt(buffer, size); + FlattenableUtils::read(buffer, size, mFrameNumber); + FlattenableUtils::read(buffer, size, mBuf); + mIsDroppable = readBoolFromInt(buffer, size); + mAcquireCalled = readBoolFromInt(buffer, size); + mTransformToDisplayInverse = readBoolFromInt(buffer, size); + + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +enum { + ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + DETACH_BUFFER, + ATTACH_BUFFER, + RELEASE_BUFFER, + CONSUMER_CONNECT, + CONSUMER_DISCONNECT, + GET_RELEASED_BUFFERS, + SET_DEFAULT_BUFFER_SIZE, + SET_DEFAULT_MAX_BUFFER_COUNT, + DISABLE_ASYNC_BUFFER, + SET_MAX_ACQUIRED_BUFFER_COUNT, + SET_CONSUMER_NAME, + SET_DEFAULT_BUFFER_FORMAT, + SET_CONSUMER_USAGE_BITS, + SET_TRANSFORM_HINT, + GET_SIDEBAND_STREAM, + DUMP, +}; + + +class BpGonkGraphicBufferConsumer : public BpInterface +{ +public: + BpGonkGraphicBufferConsumer(const sp& impl) + : BpInterface(impl) + { + } + + virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt64(presentWhen); + status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.read(*buffer); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t detachBuffer(int slot) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(slot); + status_t result = remote()->transact(DETACH_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32(); + return result; + } + + virtual status_t attachBuffer(int* slot, const sp& buffer) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.write(*buffer.get()); + status_t result = remote()->transact(ATTACH_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + *slot = reply.readInt32(); + result = reply.readInt32(); + return result; + } + + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, const sp& releaseFence) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(buf); + data.writeInt64(frameNumber); + data.write(*releaseFence); + status_t result = remote()->transact(RELEASE_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t consumerConnect(const sp& consumer, bool controlledByApp) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeStrongBinder(consumer->asBinder()); + data.writeInt32(controlledByApp); + status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t consumerDisconnect() { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t getReleasedBuffers(uint64_t* slotMask) { + Parcel data, reply; + if (slotMask == NULL) { + ALOGE("getReleasedBuffers: slotMask must not be NULL"); + return BAD_VALUE; + } + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + *slotMask = reply.readInt64(); + return reply.readInt32(); + } + + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setDefaultMaxBufferCount(int bufferCount) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + status_t result = remote()->transact(SET_DEFAULT_MAX_BUFFER_COUNT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t disableAsyncBuffer() { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + status_t result = remote()->transact(DISABLE_ASYNC_BUFFER, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(maxAcquiredBuffers); + status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual void setConsumerName(const String8& name) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeString8(name); + remote()->transact(SET_CONSUMER_NAME, data, &reply); + } + + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(defaultFormat); + status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setConsumerUsageBits(uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(usage); + status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual status_t setTransformHint(uint32_t hint) { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeInt32(hint); + status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply); + if (result != NO_ERROR) { + return result; + } + return reply.readInt32(); + } + + virtual sp getSidebandStream() const { + Parcel data, reply; + status_t err; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + if ((err = remote()->transact(GET_SIDEBAND_STREAM, data, &reply)) != NO_ERROR) { + return NULL; + } + sp stream; + if (reply.readInt32()) { + stream = NativeHandle::create(reply.readNativeHandle(), true); + } + return stream; + } + + virtual void dump(String8& result, const char* prefix) const { + Parcel data, reply; + data.writeInterfaceToken(IGonkGraphicBufferConsumer::getInterfaceDescriptor()); + data.writeString8(result); + data.writeString8(String8(prefix ? prefix : "")); + remote()->transact(DUMP, data, &reply); + reply.readString8(); + } + + // Added by mozilla + virtual mozilla::TemporaryRef + getTextureClientFromBuffer(ANativeWindowBuffer* buffer) + { + return nullptr; + } + + virtual int + getSlotFromTextureClientLocked(mozilla::layers::TextureClient* client) const + { + return BAD_VALUE; + } +}; + +IMPLEMENT_META_INTERFACE(GonkGraphicBufferConsumer, "android.gui.IGonkGraphicBufferConsumer"); + +// ---------------------------------------------------------------------- + +status_t BnGonkGraphicBufferConsumer::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case ACQUIRE_BUFFER: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + BufferItem item; + int64_t presentWhen = data.readInt64(); + status_t result = acquireBuffer(&item, presentWhen); + status_t err = reply->write(item); + if (err) return err; + reply->writeInt32(result); + return NO_ERROR; + } break; + case DETACH_BUFFER: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + int slot = data.readInt32(); + int result = detachBuffer(slot); + reply->writeInt32(result); + return NO_ERROR; + } break; + case ATTACH_BUFFER: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + sp buffer = new GraphicBuffer(); + data.read(*buffer.get()); + int slot; + int result = attachBuffer(&slot, buffer); + reply->writeInt32(slot); + reply->writeInt32(result); + return NO_ERROR; + } break; + case RELEASE_BUFFER: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + int buf = data.readInt32(); + uint64_t frameNumber = data.readInt64(); + sp releaseFence = new Fence(); + status_t err = data.read(*releaseFence); + if (err) return err; + status_t result = releaseBuffer(buf, frameNumber, releaseFence); + reply->writeInt32(result); + return NO_ERROR; + } break; + case CONSUMER_CONNECT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + sp consumer = IConsumerListener::asInterface( data.readStrongBinder() ); + bool controlledByApp = data.readInt32(); + status_t result = consumerConnect(consumer, controlledByApp); + reply->writeInt32(result); + return NO_ERROR; + } break; + case CONSUMER_DISCONNECT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + status_t result = consumerDisconnect(); + reply->writeInt32(result); + return NO_ERROR; + } break; + case GET_RELEASED_BUFFERS: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint64_t slotMask; + status_t result = getReleasedBuffers(&slotMask); + reply->writeInt64(slotMask); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_DEFAULT_BUFFER_SIZE: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + status_t result = setDefaultBufferSize(w, h); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_DEFAULT_MAX_BUFFER_COUNT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t bufferCount = data.readInt32(); + status_t result = setDefaultMaxBufferCount(bufferCount); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DISABLE_ASYNC_BUFFER: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + status_t result = disableAsyncBuffer(); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_MAX_ACQUIRED_BUFFER_COUNT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t maxAcquiredBuffers = data.readInt32(); + status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_CONSUMER_NAME: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + setConsumerName( data.readString8() ); + return NO_ERROR; + } break; + case SET_DEFAULT_BUFFER_FORMAT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t defaultFormat = data.readInt32(); + status_t result = setDefaultBufferFormat(defaultFormat); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_CONSUMER_USAGE_BITS: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t usage = data.readInt32(); + status_t result = setConsumerUsageBits(usage); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_TRANSFORM_HINT: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + uint32_t hint = data.readInt32(); + status_t result = setTransformHint(hint); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DUMP: { + CHECK_INTERFACE(IGonkGraphicBufferConsumer, data, reply); + String8 result = data.readString8(); + String8 prefix = data.readString8(); + static_cast(this)->dump(result, prefix); + reply->writeString8(result); + return NO_ERROR; + } + } + return BBinder::onTransact(code, data, reply, flags); +} + +}; // namespace android diff --git a/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.h b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.h new file mode 100644 index 000000000000..c76aa90f19d2 --- /dev/null +++ b/widget/gonk/nativewindow/IGonkGraphicBufferConsumerLL.h @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef NATIVEWINDOW_IGONKGRAPHICBUFFERCONSUMER_LL_H +#define NATIVEWINDOW_IGONKGRAPHICBUFFERCONSUMER_LL_H + +#include +#include + +#include +#include +#include + +#include +#include + +#include "mozilla/RefPtr.h" + +class ANativeWindowBuffer; + +namespace mozilla { +namespace layers { +class TextureClient; +} +} + +namespace android { +// ---------------------------------------------------------------------------- + +class Fence; +class GraphicBuffer; +class IConsumerListener; +class NativeHandle; + +class IGonkGraphicBufferConsumer : public IInterface { +public: + + // public facing structure for BufferSlot + class BufferItem : public Flattenable { + friend class Flattenable; + size_t getPodSize() const; + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + public: + // The default value of mBuf, used to indicate this doesn't correspond to a slot. + enum { INVALID_BUFFER_SLOT = -1 }; + BufferItem(); + + // mGraphicBuffer points to the buffer allocated for this slot, or is NULL + // if the buffer in this slot has been acquired in the past (see + // BufferSlot.mAcquireCalled). + sp mGraphicBuffer; + + // mFence is a fence that will signal when the buffer is idle. + sp mFence; + + // mCrop is the current crop rectangle for this buffer slot. + Rect mCrop; + + // mTransform is the current transform flags for this buffer slot. + // refer to NATIVE_WINDOW_TRANSFORM_* in + uint32_t mTransform; + + // mScalingMode is the current scaling mode for this buffer slot. + // refer to NATIVE_WINDOW_SCALING_* in + uint32_t mScalingMode; + + // mTimestamp is the current timestamp for this buffer slot. This gets + // to set by queueBuffer each time this slot is queued. This value + // is guaranteed to be monotonically increasing for each newly + // acquired buffer. + int64_t mTimestamp; + + // mIsAutoTimestamp indicates whether mTimestamp was generated + // automatically when the buffer was queued. + bool mIsAutoTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + + // mBuf is the slot index of this buffer (default INVALID_BUFFER_SLOT). + int mBuf; + + // mIsDroppable whether this buffer was queued with the + // property that it can be replaced by a new buffer for the purpose of + // making sure dequeueBuffer() won't block. + // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer + // was queued. + bool mIsDroppable; + + // Indicates whether this buffer has been seen by a consumer yet + bool mAcquireCalled; + + // Indicates this buffer must be transformed by the inverse transform of the screen + // it is displayed onto. This is applied after mTransform. + bool mTransformToDisplayInverse; + }; + + enum { + // Returned by releaseBuffer, after which the consumer must + // free any references to the just-released buffer that it might have. + STALE_BUFFER_SLOT = 1, + // Returned by dequeueBuffer if there are no pending buffers available. + NO_BUFFER_AVAILABLE, + // Returned by dequeueBuffer if it's too early for the buffer to be acquired. + PRESENT_LATER, + }; + + // acquireBuffer attempts to acquire ownership of the next pending buffer in + // the BufferQueue. If no buffer is pending then it returns + // NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the + // information about the buffer is returned in BufferItem. + // + // If the buffer returned had previously been + // acquired then the BufferItem::mGraphicBuffer field of buffer is set to + // NULL and it is assumed that the consumer still holds a reference to the + // buffer. + // + // If presentWhen is non-zero, it indicates the time when the buffer will + // be displayed on screen. If the buffer's timestamp is farther in the + // future, the buffer won't be acquired, and PRESENT_LATER will be + // returned. The presentation time is in nanoseconds, and the time base + // is CLOCK_MONOTONIC. + // + // Return of NO_ERROR means the operation completed as normal. + // + // Return of a positive value means the operation could not be completed + // at this time, but the user should try again later: + // * NO_BUFFER_AVAILABLE - no buffer is pending (nothing queued by producer) + // * PRESENT_LATER - the buffer's timestamp is farther in the future + // + // Return of a negative value means an error has occurred: + // * INVALID_OPERATION - too many buffers have been acquired + virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 0; + + // detachBuffer attempts to remove all ownership of the buffer in the given + // slot from the buffer queue. If this call succeeds, the slot will be + // freed, and there will be no way to obtain the buffer from this interface. + // The freed slot will remain unallocated until either it is selected to + // hold a freshly allocated buffer in dequeueBuffer or a buffer is attached + // to the slot. The buffer must have already been acquired. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - the given slot number is invalid, either because it is + // out of the range [0, NUM_BUFFER_SLOTS) or because the slot + // it refers to is not currently acquired. + virtual status_t detachBuffer(int slot) = 0; + + // attachBuffer attempts to transfer ownership of a buffer to the buffer + // queue. If this call succeeds, it will be as if this buffer was acquired + // from the returned slot number. As such, this call will fail if attaching + // this buffer would cause too many buffers to be simultaneously acquired. + // + // If the buffer is successfully attached, its frameNumber is initialized + // to 0. This must be passed into the releaseBuffer call or else the buffer + // will be deallocated as stale. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - outSlot or buffer were NULL + // * INVALID_OPERATION - cannot attach the buffer because it would cause too + // many buffers to be acquired. + // * NO_MEMORY - no free slots available + virtual status_t attachBuffer(int *outSlot, + const sp& buffer) = 0; + + // releaseBuffer releases a buffer slot from the consumer back to the + // BufferQueue. This may be done while the buffer's contents are still + // being accessed. The fence will signal when the buffer is no longer + // in use. frameNumber is used to indentify the exact buffer returned. + // + // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free + // any references to the just-released buffer that it might have, as if it + // had received a onBuffersReleased() call with a mask set for the released + // buffer. + // + // Note that the dependencies on EGL will be removed once we switch to using + // the Android HW Sync HAL. + // + // Return of NO_ERROR means the operation completed as normal. + // + // Return of a positive value means the operation could not be completed + // at this time, but the user should try again later: + // * STALE_BUFFER_SLOT - see above (second paragraph) + // + // Return of a negative value means an error has occurred: + // * BAD_VALUE - one of the following could've happened: + // * the buffer slot was invalid + // * the fence was NULL + // * the buffer slot specified is not in the acquired state + virtual status_t releaseBuffer(int buf, uint64_t frameNumber, const sp& releaseFence) = 0; + + // consumerConnect connects a consumer to the BufferQueue. Only one + // consumer may be connected, and when that consumer disconnects the + // BufferQueue is placed into the "abandoned" state, causing most + // interactions with the BufferQueue by the producer to fail. + // controlledByApp indicates whether the consumer is controlled by + // the application. + // + // consumer may not be NULL. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned + // * BAD_VALUE - a NULL consumer was provided + virtual status_t consumerConnect(const sp& consumer, bool controlledByApp) = 0; + + // consumerDisconnect disconnects a consumer from the BufferQueue. All + // buffers will be freed and the BufferQueue is placed in the "abandoned" + // state, causing most interactions with the BufferQueue by the producer to + // fail. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - no consumer is currently connected + virtual status_t consumerDisconnect() = 0; + + // getReleasedBuffers sets the value pointed to by slotMask to a bit set. + // Each bit index with a 1 corresponds to a released buffer slot with that + // index value. In particular, a released buffer is one that has + // been released by the BufferQueue but have not yet been released by the consumer. + // + // This should be called from the onBuffersReleased() callback. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned. + virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0; + + // setDefaultBufferSize is used to set the size of buffers returned by + // dequeueBuffer when a width and height of zero is requested. Default + // is 1x1. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - either w or h was zero + virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; + + // setDefaultMaxBufferCount sets the default value for the maximum buffer + // count (the initial default is 2). If the producer has requested a + // buffer count using setBufferCount, the default buffer count will only + // take effect if the producer sets the count back to zero. + // + // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - bufferCount was out of range (see above). + virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0; + + // disableAsyncBuffer disables the extra buffer used in async mode + // (when both producer and consumer have set their "isControlledByApp" + // flag) and has dequeueBuffer() return WOULD_BLOCK instead. + // + // This can only be called before consumerConnect(). + // + // Return of a value other than NO_ERROR means an error has occurred: + // * INVALID_OPERATION - attempting to call this after consumerConnect. + virtual status_t disableAsyncBuffer() = 0; + + // setMaxAcquiredBufferCount sets the maximum number of buffers that can + // be acquired by the consumer at one time (default 1). This call will + // fail if a producer is connected to the BufferQueue. + // + // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - maxAcquiredBuffers was out of range (see above). + // * INVALID_OPERATION - attempting to call this after a producer connected. + virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; + + // setConsumerName sets the name used in logging + virtual void setConsumerName(const String8& name) = 0; + + // setDefaultBufferFormat allows the BufferQueue to create + // GraphicBuffers of a defaultFormat if no format is specified + // in dequeueBuffer. Formats are enumerated in graphics.h; the + // initial default is HAL_PIXEL_FORMAT_RGBA_8888. + // + // Return of a value other than NO_ERROR means an unknown error has occurred. + virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0; + + // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. + // These are merged with the bits passed to dequeueBuffer. The values are + // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. + // + // Return of a value other than NO_ERROR means an unknown error has occurred. + virtual status_t setConsumerUsageBits(uint32_t usage) = 0; + + // setTransformHint bakes in rotation to buffers so overlays can be used. + // The values are enumerated in window.h, e.g. + // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). + // + // Return of a value other than NO_ERROR means an unknown error has occurred. + virtual status_t setTransformHint(uint32_t hint) = 0; + + // Retrieve the sideband buffer stream, if any. + virtual sp getSidebandStream() const = 0; + + // dump state into a string + virtual void dump(String8& result, const char* prefix) const = 0; + + // Added by mozilla + virtual mozilla::TemporaryRef + getTextureClientFromBuffer(ANativeWindowBuffer* buffer) = 0; + + virtual int getSlotFromTextureClientLocked(mozilla::layers::TextureClient* client) const = 0; + +public: + DECLARE_META_INTERFACE(GonkGraphicBufferConsumer); +}; + +// ---------------------------------------------------------------------------- + +class BnGonkGraphicBufferConsumer : public BnInterface +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_GUI_IGONKGRAPHICBUFFERCONSUMER_H diff --git a/widget/gonk/nativewindow/moz.build b/widget/gonk/nativewindow/moz.build index 9691abb2708d..416ed09ba6d7 100644 --- a/widget/gonk/nativewindow/moz.build +++ b/widget/gonk/nativewindow/moz.build @@ -20,13 +20,28 @@ EXPORTS += [ 'GonkNativeWindowClient.h', ] -if CONFIG['ANDROID_VERSION'] == '19': +if CONFIG['ANDROID_VERSION'] >= '19': + EXPORTS += [ + 'IGonkGraphicBufferConsumer.h', + ] + +if CONFIG['ANDROID_VERSION'] >= '21': + EXPORTS += [ + 'GonkBufferQueueLL/GonkBufferQueueDefs.h', + 'GonkBufferQueueLL/GonkBufferQueueLL.h', + 'GonkBufferQueueLL/GonkBufferSlot.h', + 'GonkConsumerBaseLL.h', + 'GonkNativeWindowClientLL.h', + 'GonkNativeWindowLL.h', + 'IGonkGraphicBufferConsumerLL.h', + ] +elif CONFIG['ANDROID_VERSION'] >= '19': EXPORTS += [ 'GonkBufferQueueKK.h', 'GonkConsumerBaseKK.h', 'GonkNativeWindowClientKK.h', 'GonkNativeWindowKK.h', - 'IGonkGraphicBufferConsumer.h', + 'IGonkGraphicBufferConsumerKK.h', ] elif CONFIG['ANDROID_VERSION'] in ('17', '18'): EXPORTS += [ @@ -42,13 +57,26 @@ elif CONFIG['ANDROID_VERSION'] == '15': ] if CONFIG['MOZ_B2G_CAMERA'] or CONFIG['MOZ_OMX_DECODER'] or CONFIG['MOZ_WEBRTC']: - if CONFIG['ANDROID_VERSION'] == '19': + if CONFIG['ANDROID_VERSION'] >= '21': + SOURCES += [ + 'GonkBufferQueueLL/GonkBufferItem.cpp', + 'GonkBufferQueueLL/GonkBufferQueueConsumer.cpp', + 'GonkBufferQueueLL/GonkBufferQueueCore.cpp', + 'GonkBufferQueueLL/GonkBufferQueueLL.cpp', + 'GonkBufferQueueLL/GonkBufferQueueProducer.cpp', + 'GonkBufferQueueLL/GonkBufferSlot.cpp', + 'GonkConsumerBaseLL.cpp', + 'GonkNativeWindowClientLL.cpp', + 'GonkNativeWindowLL.cpp', + 'IGonkGraphicBufferConsumerLL.cpp', + ] + elif CONFIG['ANDROID_VERSION'] >= '19': SOURCES += [ 'GonkBufferQueueKK.cpp', 'GonkConsumerBaseKK.cpp', 'GonkNativeWindowClientKK.cpp', 'GonkNativeWindowKK.cpp', - 'IGonkGraphicBufferConsumer.cpp', + 'IGonkGraphicBufferConsumerKK.cpp', ] elif CONFIG['ANDROID_VERSION'] in ('17', '18'): SOURCES += [ @@ -82,3 +110,5 @@ if CONFIG['GNU_CC']: FINAL_LIBRARY = 'xul' DISABLE_STL_WRAPPING = True + +NO_VISIBILITY_FLAGS = True