diff --git a/b2g/app/B2GLoader.cpp b/b2g/app/B2GLoader.cpp new file mode 100644 index 000000000000..7e0aa9627840 --- /dev/null +++ b/b2g/app/B2GLoader.cpp @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 autoindent cindent expandtab: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsXULAppAPI.h" +#include "application.ini.h" +#include "nsXPCOMGlue.h" +#include "nsStringGlue.h" +#include "nsCOMPtr.h" +#include "nsIFile.h" +#include "BinaryPath.h" +#include "nsAutoPtr.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "nsXPCOMPrivate.h" // for MAXPATHLEN and XPCOM_DLL + +#define ASSERT(x) if (!(x)) { MOZ_CRASH(); } + + +// Functions being loaded by XPCOMGlue +XRE_ProcLoaderServiceRunType XRE_ProcLoaderServiceRun; +XRE_ProcLoaderClientInitType XRE_ProcLoaderClientInit; +XRE_ProcLoaderPreloadType XRE_ProcLoaderPreload; +extern XRE_CreateAppDataType XRE_CreateAppData; +extern XRE_GetFileFromPathType XRE_GetFileFromPath; + +static const nsDynamicFunctionLoad kXULFuncs[] = { + { "XRE_ProcLoaderServiceRun", (NSFuncPtr*) &XRE_ProcLoaderServiceRun }, + { "XRE_ProcLoaderClientInit", (NSFuncPtr*) &XRE_ProcLoaderClientInit }, + { "XRE_ProcLoaderPreload", (NSFuncPtr*) &XRE_ProcLoaderPreload }, + { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData }, + { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath }, + { nullptr, nullptr } +}; + +static int +GetDirnameSlash(const char *aPath, char *aOutDir, int aMaxLen) +{ + char *lastSlash = strrchr(aPath, XPCOM_FILE_PATH_SEPARATOR[0]); + if (lastSlash == nullptr) { + return 0; + } + int cpsz = lastSlash - aPath + 1; // include slash + if (aMaxLen <= cpsz) { + return 0; + } + strncpy(aOutDir, aPath, cpsz); + aOutDir[cpsz] = 0; + return cpsz; +} + +static bool +GetXPCOMPath(const char *aProgram, char *aOutPath, int aMaxLen) +{ + nsAutoArrayPtr progBuf(new char[aMaxLen]); + nsresult rv = mozilla::BinaryPath::Get(aProgram, progBuf); + NS_ENSURE_SUCCESS(rv, false); + + int len = GetDirnameSlash(progBuf, aOutPath, aMaxLen); + NS_ENSURE_TRUE(!!len, false); + + NS_ENSURE_TRUE((len + sizeof(XPCOM_DLL)) < aMaxLen, false); + char *afterSlash = aOutPath + len; + strcpy(afterSlash, XPCOM_DLL); + return true; +} + +static bool +LoadLibxul(const char *aXPCOMPath) +{ + nsresult rv; + + XPCOMGlueEnablePreload(); + rv = XPCOMGlueStartup(aXPCOMPath); + NS_ENSURE_SUCCESS(rv, false); + + rv = XPCOMGlueLoadXULFunctions(kXULFuncs); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + +/** + * Return true if |arg| matches the given argument name. + */ +static bool +IsArg(const char* arg, const char* s) +{ + if (*arg == '-') { + if (*++arg == '-') { + ++arg; + } + return !strcasecmp(arg, s); + } + +#if defined(XP_WIN) + if (*arg == '/') { + return !strcasecmp(++arg, s); + } +#endif + + return false; +} + +static already_AddRefed +GetAppIni(int argc, const char *argv[]) +{ + nsCOMPtr appini; + nsresult rv; + + // Allow firefox.exe to launch XULRunner apps via -app + // Note that -app must be the *first* argument. + const char *appDataFile = getenv("XUL_APP_FILE"); + if (appDataFile && *appDataFile) { + rv = XRE_GetFileFromPath(appDataFile, getter_AddRefs(appini)); + NS_ENSURE_SUCCESS(rv, nullptr); + } else if (argc > 1 && IsArg(argv[1], "app")) { + if (argc == 2) { + return nullptr; + } + + rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(appini)); + NS_ENSURE_SUCCESS(rv, nullptr); + + char appEnv[MAXPATHLEN]; + snprintf(appEnv, MAXPATHLEN, "XUL_APP_FILE=%s", argv[2]); + if (putenv(appEnv)) { + return nullptr; + } + } + + return appini.forget(); +} + +static bool +LoadStaticData(int argc, const char *argv[]) +{ + char xpcomPath[MAXPATHLEN]; + bool ok = GetXPCOMPath(argv[0], xpcomPath, MAXPATHLEN); + NS_ENSURE_TRUE(ok, false); + + ok = LoadLibxul(xpcomPath); + NS_ENSURE_TRUE(ok, false); + + char progDir[MAXPATHLEN]; + ok = GetDirnameSlash(xpcomPath, progDir, MAXPATHLEN); + NS_ENSURE_TRUE(ok, false); + + nsCOMPtr appini = GetAppIni(argc, argv); + const nsXREAppData *appData; + if (appini) { + nsresult rv = + XRE_CreateAppData(appini, const_cast(&appData)); + NS_ENSURE_SUCCESS(rv, false); + } else { + appData = &sAppData; + } + + XRE_ProcLoaderPreload(progDir, appData); + + if (appini) { + XRE_FreeAppData(const_cast(appData)); + } + + return true; +} + +/** + * Fork and run parent and child process. + * + * The parent is the b2g process and child for Nuwa. + */ +static int +RunProcesses(int argc, const char *argv[]) +{ + /* + * The original main() of the b2g process. It is renamed to + * b2g_main() for the b2g loader. + */ + int b2g_main(int argc, const char *argv[]); + + int ipcSockets[2] = {-1, -1}; + int r = socketpair(AF_LOCAL, SOCK_STREAM, 0, ipcSockets); + ASSERT(r == 0); + int parentSock = ipcSockets[0]; + int childSock = ipcSockets[1]; + + r = fcntl(parentSock, F_SETFL, O_NONBLOCK); + ASSERT(r != -1); + r = fcntl(childSock, F_SETFL, O_NONBLOCK); + ASSERT(r != -1); + + pid_t pid = fork(); + ASSERT(pid >= 0); + bool isChildProcess = pid == 0; + + close(isChildProcess ? parentSock : childSock); + + if (isChildProcess) { + /* The Nuwa process */ + /* This provides the IPC service of loading Nuwa at the process. + * The b2g process would send a IPC message of loading Nuwa + * as the replacement of forking and executing plugin-container. + */ + return XRE_ProcLoaderServiceRun(getppid(), childSock, argc, argv); + } + + // The b2g process + int childPid = pid; + XRE_ProcLoaderClientInit(childPid, parentSock); + return b2g_main(argc, argv); +} + +/** + * B2G Loader is responsible for loading the b2g process and the + * Nuwa process. It forks into the parent process, for the b2g + * process, and the child process, for the Nuwa process. + * + * The loader loads libxul and performs initialization of static data + * before forking, so relocation of libxul and static data can be + * shared between the b2g process, the Nuwa process, and the content + * processes. + */ +int +main(int argc, const char* argv[]) +{ + const char *program = argv[0]; + /* + * Before fork(), libxul and static data of Gecko are loaded for + * sharing. + */ + bool ok = LoadStaticData(argc, argv); + if (!ok) { + return 255; + } + + return RunProcesses(argc, argv); +} diff --git a/b2g/app/moz.build b/b2g/app/moz.build index 77879ddb4440..82b60ad6db31 100644 --- a/b2g/app/moz.build +++ b/b2g/app/moz.build @@ -9,6 +9,11 @@ if not CONFIG['LIBXUL_SDK']: PROGRAM = CONFIG['MOZ_APP_NAME'] + "-bin" else: PROGRAM = CONFIG['MOZ_APP_NAME'] + if CONFIG['MOZ_B2G_LOADER']: + SOURCES += [ + 'B2GLoader.cpp', + ] + SOURCES += [ 'nsBrowserApp.cpp', ] diff --git a/b2g/app/nsBrowserApp.cpp b/b2g/app/nsBrowserApp.cpp index bf57ef445744..f4b3e8e45a82 100644 --- a/b2g/app/nsBrowserApp.cpp +++ b/b2g/app/nsBrowserApp.cpp @@ -17,6 +17,7 @@ #include #include +#include #include "nsCOMPtr.h" #include "nsIFile.h" @@ -163,9 +164,22 @@ static int do_main(int argc, char* argv[]) return XRE_main(argc, argv, &sAppData, 0); } -int main(int argc, char* argv[]) +#ifdef MOZ_B2G_LOADER +/* + * The main() in B2GLoader.cpp is the new main function instead of the + * main() here if it is enabled. So, rename it to b2g_man(). + */ +#define main b2g_main +#define _CONST const +#else +#define _CONST +#endif + +int main(int argc, _CONST char* argv[]) { +#ifndef MOZ_B2G_LOADER char exePath[MAXPATHLEN]; +#endif #ifdef MOZ_WIDGET_GONK // This creates a ThreadPool for binder ipc. A ThreadPool is necessary to @@ -175,7 +189,9 @@ int main(int argc, char* argv[]) android::ProcessState::self()->startThreadPool(); #endif - nsresult rv = mozilla::BinaryPath::Get(argv[0], exePath); + nsresult rv; +#ifndef MOZ_B2G_LOADER + rv = mozilla::BinaryPath::Get(argv[0], exePath); if (NS_FAILED(rv)) { Output("Couldn't calculate the application directory.\n"); return 255; @@ -186,6 +202,7 @@ int main(int argc, char* argv[]) return 255; strcpy(++lastSlash, XPCOM_DLL); +#endif // MOZ_B2G_LOADER #if defined(XP_UNIX) // If the b2g app is launched from adb shell, then the shell will wind @@ -209,6 +226,9 @@ int main(int argc, char* argv[]) DllBlocklist_Initialize(); #endif + // B2G loader has already initialized Gecko so we can't initialize + // it again here. +#ifndef MOZ_B2G_LOADER // We do this because of data in bug 771745 XPCOMGlueEnablePreload(); @@ -219,6 +239,7 @@ int main(int argc, char* argv[]) } // Reset exePath so that it is the directory name and not the xpcom dll name *lastSlash = 0; +#endif // MOZ_B2G_LOADER rv = XPCOMGlueLoadXULFunctions(kXULFuncs); if (NS_FAILED(rv)) { @@ -253,7 +274,25 @@ int main(int argc, char* argv[]) int result; { ScopedLogging log; - result = do_main(argc, argv); + char **_argv; + + /* + * Duplicate argument vector to conform non-const argv of + * do_main() since XRE_main() is very stupid with non-const argv. + */ + _argv = new char *[argc + 1]; + for (int i = 0; i < argc; i++) { + _argv[i] = strdup(argv[i]); + MOZ_ASSERT(_argv[i] != nullptr); + } + _argv[argc] = nullptr; + + result = do_main(argc, _argv); + + for (int i = 0; i < argc; i++) { + free(_argv[i]); + } + delete[] _argv; } return result; diff --git a/b2g/chrome/content/settings.js b/b2g/chrome/content/settings.js index 27916c53bcf8..98847326d3ef 100644 --- a/b2g/chrome/content/settings.js +++ b/b2g/chrome/content/settings.js @@ -155,17 +155,25 @@ Components.utils.import('resource://gre/modules/ctypes.jsm'); product_model = libcutils.property_get('ro.product.model'); #endif - let software = os_name + ' ' + os_version; - let setting = { - 'deviceinfo.os': os_version, - 'deviceinfo.software': software, - 'deviceinfo.platform_version': appInfo.platformVersion, - 'deviceinfo.platform_build_id': appInfo.platformBuildID, - 'deviceinfo.hardware': hardware_info, - 'deviceinfo.firmware_revision': firmware_revision, - 'deviceinfo.product_model': product_model + // Populate deviceinfo settings, + // copying any existing deviceinfo.os into deviceinfo.previous_os + let lock = window.navigator.mozSettings.createLock(); + let req = lock.get('deviceinfo.os'); + req.onsuccess = req.onerror = () => { + let previous_os = req.result && req.result['deviceinfo.os'] || ''; + let software = os_name + ' ' + os_version; + let setting = { + 'deviceinfo.os': os_version, + 'deviceinfo.previous_os': previous_os, + 'deviceinfo.software': software, + 'deviceinfo.platform_version': appInfo.platformVersion, + 'deviceinfo.platform_build_id': appInfo.platformBuildID, + 'deviceinfo.hardware': hardware_info, + 'deviceinfo.firmware_revision': firmware_revision, + 'deviceinfo.product_model': product_model + } + lock.set(setting); } - window.navigator.mozSettings.createLock().set(setting); })(); // =================== DevTools ==================== diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 3f0637e03d0d..b59270f9f6f6 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,13 +19,13 @@ - + - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index d9405d1c8f80..32cadc48cc49 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,10 +17,10 @@ - + - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index 6c6a2d018e6a..b80c7855c0ca 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 3f0637e03d0d..b59270f9f6f6 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,13 +19,13 @@ - + - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index 9382e2adf068..308fcfce7062 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,10 +17,10 @@ - + - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 740dc62489d2..c4a2d8fb1f13 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "97a95feb1d1505c9d179266fee891e600ad8760b", + "revision": "9863517bfb599a39300f5e8c6f94e16189a4c698", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 077ed79a0159..1c1d5187dd58 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,12 +17,12 @@ - + - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 626c507870e5..dadd9218c814 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 5b77eacd7494..508c0ad0a25a 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,10 +17,10 @@ - + - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 10d3d5c2b3b7..30b644a27e90 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,12 +17,12 @@ - + - + diff --git a/b2g/confvars.sh b/b2g/confvars.sh index f77d791fb0dd..98ef5283328a 100644 --- a/b2g/confvars.sh +++ b/b2g/confvars.sh @@ -59,6 +59,7 @@ MOZ_B2G=1 if test "$OS_TARGET" = "Android"; then MOZ_NUWA_PROCESS=1 +MOZ_B2G_LOADER=1 fi MOZ_FOLD_LIBS=1 diff --git a/configure.in b/configure.in index dcc60a70d995..9c2a508d54f8 100644 --- a/configure.in +++ b/configure.in @@ -8631,6 +8631,14 @@ if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then AC_DEFINE(MOZ_NUWA_PROCESS) fi AC_SUBST(MOZ_NUWA_PROCESS) +if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_B2G_LOADER"; then + if test -z "$MOZ_NUWA_PROCESS"; then + AC_MSG_ERROR([B2G loader works with Nuwa]); + fi + export MOZ_B2G_LOADER + AC_DEFINE(MOZ_B2G_LOADER) +fi +AC_SUBST(MOZ_B2G_LOADER) AC_SUBST(NSPR_CFLAGS) AC_SUBST(NSPR_LIBS) diff --git a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp index 1ccc890cc0d1..065d7ead82a5 100644 --- a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp +++ b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp @@ -169,7 +169,7 @@ class UpdateElementAttrsTask : public nsRunnable } NS_ENSURE_TRUE(sBtAvrcpInterface, NS_OK); - sBtAvrcpInterface->GetElementAttrRsp(mNumAttr, attrs); + sBtAvrcpInterface->GetElementAttrRsp(mNumAttr, attrs, nullptr); return NS_OK; } @@ -492,6 +492,85 @@ static btrc_callbacks_t sBtAvrcpCallbacks = { }; #endif +#if ANDROID_VERSION > 17 +class InitAvrcpResultHandler MOZ_FINAL : public BluetoothAvrcpResultHandler +{ +public: + InitAvrcpResultHandler(BluetoothProfileResultHandler* aRes) + : mRes(aRes) + { } + + void OnError(bt_status_t aStatus) MOZ_OVERRIDE + { + BT_WARNING("BluetoothAvrcpInterface::Init failed: %d", + (int)aStatus); + if (mRes) { + mRes->OnError(NS_ERROR_FAILURE); + } + } + + void Init() MOZ_OVERRIDE + { + if (mRes) { + mRes->Init(); + } + } + +private: + nsRefPtr mRes; +}; +#endif + +class InitA2dpResultHandler MOZ_FINAL : public BluetoothA2dpResultHandler +{ +public: + InitA2dpResultHandler(BluetoothProfileResultHandler* aRes) + : mRes(aRes) + { } + + void OnError(bt_status_t aStatus) MOZ_OVERRIDE + { + BT_WARNING("BluetoothA2dpInterface::Init failed: %d", + (int)aStatus); + if (mRes) { + mRes->OnError(NS_ERROR_FAILURE); + } + } + + void Init() MOZ_OVERRIDE + { +#if ANDROID_VERSION > 17 + /* Also init AVRCP if it's available, ... */ + BluetoothInterface* btInf = BluetoothInterface::GetInstance(); + if (!btInf) { + if (mRes) { + mRes->OnError(NS_ERROR_FAILURE); + } + return; + } + + sBtAvrcpInterface = btInf->GetBluetoothAvrcpInterface(); + if (!sBtAvrcpInterface) { + if (mRes) { + mRes->OnError(NS_ERROR_FAILURE); + } + return; + } + + sBtAvrcpInterface->Init(&sBtAvrcpCallbacks, + new InitAvrcpResultHandler(mRes)); +#else + /* ...or signal success otherwise. */ + if (mRes) { + mRes->Init(); + } +#endif + } + +private: + nsRefPtr mRes; +}; + /* * This function will be only called when Bluetooth is turning on. * It is important to register a2dp callbacks before enable() gets called. @@ -520,30 +599,7 @@ BluetoothA2dpManager::InitA2dpInterface(BluetoothProfileResultHandler* aRes) return; } - int ret = sBtA2dpInterface->Init(&sBtA2dpCallbacks); - if (ret != BT_STATUS_SUCCESS) { - BT_LOGR("Warning: failed to init a2dp module"); - } - -#if ANDROID_VERSION > 17 - sBtAvrcpInterface = btInf->GetBluetoothAvrcpInterface(); - if (!sBtAvrcpInterface) { - BT_LOGR("Error: Bluetooth AVRCP interface not available"); - if (aRes) { - aRes->OnError(NS_ERROR_FAILURE); - } - return; - } - - ret = sBtAvrcpInterface->Init(&sBtAvrcpCallbacks); - if (ret != BT_STATUS_SUCCESS) { - BT_LOGR("Warning: failed to init avrcp module"); - } -#endif - - if (aRes) { - aRes->Init(); - } + sBtA2dpInterface->Init(&sBtA2dpCallbacks, new InitA2dpResultHandler(aRes)); } BluetoothA2dpManager::~BluetoothA2dpManager() @@ -617,6 +673,99 @@ BluetoothA2dpManager::Get() return sBluetoothA2dpManager; } +#if ANDROID_VERSION > 17 +class CleanupAvrcpResultHandler MOZ_FINAL : public BluetoothAvrcpResultHandler +{ +public: + CleanupAvrcpResultHandler(BluetoothProfileResultHandler* aRes) + : mRes(aRes) + { } + + void OnError(bt_status_t aStatus) MOZ_OVERRIDE + { + BT_WARNING("BluetoothAvrcpInterface::Cleanup failed: %d", + (int)aStatus); + if (mRes) { + mRes->OnError(NS_ERROR_FAILURE); + } + } + + void Cleanup() MOZ_OVERRIDE + { + sBtAvrcpInterface = nullptr; + if (mRes) { + mRes->Deinit(); + } + } + +private: + nsRefPtr mRes; +}; +#endif + +class CleanupA2dpResultHandler MOZ_FINAL : public BluetoothA2dpResultHandler +{ +public: + CleanupA2dpResultHandler(BluetoothProfileResultHandler* aRes) + : mRes(aRes) + { } + + void OnError(bt_status_t aStatus) MOZ_OVERRIDE + { + BT_WARNING("BluetoothA2dpInterface::Cleanup failed: %d", + (int)aStatus); + if (mRes) { + mRes->OnError(NS_ERROR_FAILURE); + } + } + + void Cleanup() MOZ_OVERRIDE + { + sBtA2dpInterface = nullptr; +#if ANDROID_VERSION > 17 + /* Cleanup AVRCP if it's available and initialized, ...*/ + if (sBtAvrcpInterface) { + sBtAvrcpInterface->Cleanup(new CleanupAvrcpResultHandler(mRes)); + } else +#endif + if (mRes) { + /* ...or simply signal success from here. */ + mRes->Deinit(); + } + } + +private: + nsRefPtr mRes; +}; + +class CleanupA2dpResultHandlerRunnable MOZ_FINAL : public nsRunnable +{ +public: + CleanupA2dpResultHandlerRunnable(BluetoothProfileResultHandler* aRes) + : mRes(aRes) + { } + + NS_IMETHOD Run() MOZ_OVERRIDE + { + sBtA2dpInterface = nullptr; +#if ANDROID_VERSION > 17 + /* Cleanup AVRCP if it's available and initialized, ...*/ + if (sBtAvrcpInterface) { + sBtAvrcpInterface->Cleanup(new CleanupAvrcpResultHandler(mRes)); + } else +#endif + if (mRes) { + /* ...or simply signal success from here. */ + mRes->Deinit(); + } + + return NS_OK; + } + +private: + nsRefPtr mRes; +}; + // static void BluetoothA2dpManager::DeinitA2dpInterface(BluetoothProfileResultHandler* aRes) @@ -624,17 +773,14 @@ BluetoothA2dpManager::DeinitA2dpInterface(BluetoothProfileResultHandler* aRes) MOZ_ASSERT(NS_IsMainThread()); if (sBtA2dpInterface) { - sBtA2dpInterface->Cleanup(); - sBtA2dpInterface = nullptr; - } -#if ANDROID_VERSION > 17 - if (sBtAvrcpInterface) { - sBtAvrcpInterface->Cleanup(); - sBtAvrcpInterface = nullptr; - } -#endif - if (aRes) { - aRes->Deinit(); + sBtA2dpInterface->Cleanup(new CleanupA2dpResultHandler(aRes)); + } else if (aRes) { + // We dispatch a runnable here to make the profile resource handler + // behave as if A2DP was initialized. + nsRefPtr r = new CleanupA2dpResultHandlerRunnable(aRes); + if (NS_FAILED(NS_DispatchToMainThread(r))) { + BT_LOGR("Failed to dispatch cleanup-result-handler runnable"); + } } } @@ -647,6 +793,29 @@ BluetoothA2dpManager::HandleShutdown() sBluetoothA2dpManager = nullptr; } +void +BluetoothA2dpManager::OnConnectError() +{ + MOZ_ASSERT(NS_IsMainThread()); + + mController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED)); + + mController = nullptr; + mDeviceAddress.Truncate(); +} + +class ConnectResultHandler MOZ_FINAL : public BluetoothA2dpResultHandler +{ +public: + void OnError(bt_status_t aStatus) MOZ_OVERRIDE + { + BT_LOGR("BluetoothA2dpInterface::Connect failed: %d", (int)aStatus); + + NS_ENSURE_TRUE_VOID(sBluetoothA2dpManager); + sBluetoothA2dpManager->OnConnectError(); + } +}; + void BluetoothA2dpManager::Connect(const nsAString& aDeviceAddress, BluetoothProfileController* aController) @@ -678,14 +847,29 @@ BluetoothA2dpManager::Connect(const nsAString& aDeviceAddress, bt_bdaddr_t remoteAddress; StringToBdAddressType(aDeviceAddress, &remoteAddress); - bt_status_t result = sBtA2dpInterface->Connect(&remoteAddress); - if (BT_STATUS_SUCCESS != result) { - BT_LOGR("Failed to connect: %x", result); - aController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED)); - return; - } + sBtA2dpInterface->Connect(&remoteAddress, new ConnectResultHandler()); } +void +BluetoothA2dpManager::OnDisconnectError() +{ + MOZ_ASSERT(NS_IsMainThread()); + + mController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED)); +} + +class DisconnectResultHandler MOZ_FINAL : public BluetoothA2dpResultHandler +{ +public: + void OnError(bt_status_t aStatus) MOZ_OVERRIDE + { + BT_LOGR("BluetoothA2dpInterface::Disconnect failed: %d", (int)aStatus); + + NS_ENSURE_TRUE_VOID(sBluetoothA2dpManager); + sBluetoothA2dpManager->OnDisconnectError(); + } +}; + void BluetoothA2dpManager::Disconnect(BluetoothProfileController* aController) { @@ -720,12 +904,7 @@ BluetoothA2dpManager::Disconnect(BluetoothProfileController* aController) bt_bdaddr_t remoteAddress; StringToBdAddressType(mDeviceAddress, &remoteAddress); - bt_status_t result = sBtA2dpInterface->Disconnect(&remoteAddress); - if (BT_STATUS_SUCCESS != result) { - BT_LOGR("Failed to disconnect: %x", result); - aController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED)); - return; - } + sBtA2dpInterface->Disconnect(&remoteAddress, new DisconnectResultHandler()); } void @@ -957,7 +1136,7 @@ BluetoothA2dpManager::UpdateMetaData(const nsAString& aTitle, mTrackChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED; sBtAvrcpInterface->RegisterNotificationRsp(BTRC_EVT_TRACK_CHANGE, BTRC_NOTIFICATION_TYPE_CHANGED, - ¶m); + ¶m, nullptr); if (mPlayPosChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) { param.song_pos = mPosition; // EVENT_PLAYBACK_POS_CHANGED shall be notified if changed current track @@ -965,7 +1144,7 @@ BluetoothA2dpManager::UpdateMetaData(const nsAString& aTitle, sBtAvrcpInterface->RegisterNotificationRsp( BTRC_EVT_PLAY_POS_CHANGED, BTRC_NOTIFICATION_TYPE_CHANGED, - ¶m); + ¶m, nullptr); } } @@ -993,7 +1172,7 @@ BluetoothA2dpManager::UpdatePlayStatus(uint32_t aDuration, NS_ENSURE_TRUE_VOID(sBtAvrcpInterface); // always update playstatus first sBtAvrcpInterface->GetPlayStatusRsp((btrc_play_status_t)aPlayStatus, - aDuration, aPosition); + aDuration, aPosition, nullptr); // when play status changed, send both play status and position if (mPlayStatus != aPlayStatus && mPlayStatusChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) { @@ -1002,7 +1181,7 @@ BluetoothA2dpManager::UpdatePlayStatus(uint32_t aDuration, mPlayStatusChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED; sBtAvrcpInterface->RegisterNotificationRsp(BTRC_EVT_PLAY_STATUS_CHANGED, BTRC_NOTIFICATION_TYPE_CHANGED, - ¶m); + ¶m, nullptr); } if (mPosition != aPosition && @@ -1012,7 +1191,7 @@ BluetoothA2dpManager::UpdatePlayStatus(uint32_t aDuration, mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED; sBtAvrcpInterface->RegisterNotificationRsp(BTRC_EVT_PLAY_POS_CHANGED, BTRC_NOTIFICATION_TYPE_CHANGED, - ¶m); + ¶m, nullptr); } mDuration = aDuration; @@ -1080,7 +1259,7 @@ BluetoothA2dpManager::UpdateRegisterNotification(int aEventId, int aParam) sBtAvrcpInterface->RegisterNotificationRsp((btrc_event_id_t)aEventId, BTRC_NOTIFICATION_TYPE_INTERIM, - ¶m); + ¶m, nullptr); #endif } diff --git a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.h b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.h index 9f7a01daeb20..52490e3f4189 100644 --- a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.h +++ b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.h @@ -34,6 +34,9 @@ class BluetoothA2dpManager : public BluetoothProfileManagerBase static void DeinitA2dpInterface(BluetoothProfileResultHandler* aRes); virtual ~BluetoothA2dpManager(); + void OnConnectError(); + void OnDisconnectError(); + // A2DP-specific functions void HandleSinkPropertyChanged(const BluetoothSignal& aSignal); diff --git a/dom/bluetooth2/bluedroid/BluetoothInterface.cpp b/dom/bluetooth2/bluedroid/BluetoothInterface.cpp index 58924c23c52a..41c62a5d325b 100644 --- a/dom/bluetooth2/bluedroid/BluetoothInterface.cpp +++ b/dom/bluetooth2/bluedroid/BluetoothInterface.cpp @@ -847,6 +847,37 @@ struct interface_traits } }; +typedef + BluetoothInterfaceRunnable0 + BluetoothA2dpResultRunnable; + +typedef + BluetoothInterfaceRunnable1 + BluetoothA2dpErrorRunnable; + +static nsresult +DispatchBluetoothA2dpResult( + BluetoothA2dpResultHandler* aRes, + void (BluetoothA2dpResultHandler::*aMethod)(), + bt_status_t aStatus) +{ + MOZ_ASSERT(aRes); + + nsRunnable* runnable; + + if (aStatus == BT_STATUS_SUCCESS) { + runnable = new BluetoothA2dpResultRunnable(aRes, aMethod); + } else { + runnable = new BluetoothA2dpErrorRunnable(aRes, + &BluetoothA2dpResultHandler::OnError, aStatus); + } + nsresult rv = NS_DispatchToMainThread(runnable); + if (NS_FAILED(rv)) { + BT_WARNING("NS_DispatchToMainThread failed: %X", rv); + } + return rv; +} + BluetoothA2dpInterface::BluetoothA2dpInterface( const btav_interface_t* aInterface) : mInterface(aInterface) @@ -857,28 +888,51 @@ BluetoothA2dpInterface::BluetoothA2dpInterface( BluetoothA2dpInterface::~BluetoothA2dpInterface() { } -bt_status_t -BluetoothA2dpInterface::Init(btav_callbacks_t* aCallbacks) +void +BluetoothA2dpInterface::Init(btav_callbacks_t* aCallbacks, + BluetoothA2dpResultHandler* aRes) { - return mInterface->init(aCallbacks); + bt_status_t status = mInterface->init(aCallbacks); + + if (aRes) { + DispatchBluetoothA2dpResult(aRes, &BluetoothA2dpResultHandler::Init, + status); + } } void -BluetoothA2dpInterface::Cleanup() +BluetoothA2dpInterface::Cleanup(BluetoothA2dpResultHandler* aRes) { mInterface->cleanup(); + + if (aRes) { + DispatchBluetoothA2dpResult(aRes, &BluetoothA2dpResultHandler::Cleanup, + BT_STATUS_SUCCESS); + } } -bt_status_t -BluetoothA2dpInterface::Connect(bt_bdaddr_t *aBdAddr) +void +BluetoothA2dpInterface::Connect(bt_bdaddr_t *aBdAddr, + BluetoothA2dpResultHandler* aRes) { - return mInterface->connect(aBdAddr); + bt_status_t status = mInterface->connect(aBdAddr); + + if (aRes) { + DispatchBluetoothA2dpResult(aRes, &BluetoothA2dpResultHandler::Connect, + status); + } } -bt_status_t -BluetoothA2dpInterface::Disconnect(bt_bdaddr_t *aBdAddr) +void +BluetoothA2dpInterface::Disconnect(bt_bdaddr_t *aBdAddr, + BluetoothA2dpResultHandler* aRes) { - return mInterface->disconnect(aBdAddr); + bt_status_t status = mInterface->disconnect(aBdAddr); + + if (aRes) { + DispatchBluetoothA2dpResult(aRes, &BluetoothA2dpResultHandler::Disconnect, + status); + } } // @@ -897,6 +951,37 @@ struct interface_traits } }; +typedef + BluetoothInterfaceRunnable0 + BluetoothAvrcpResultRunnable; + +typedef + BluetoothInterfaceRunnable1 + BluetoothAvrcpErrorRunnable; + +static nsresult +DispatchBluetoothAvrcpResult( + BluetoothAvrcpResultHandler* aRes, + void (BluetoothAvrcpResultHandler::*aMethod)(), + bt_status_t aStatus) +{ + MOZ_ASSERT(aRes); + + nsRunnable* runnable; + + if (aStatus == BT_STATUS_SUCCESS) { + runnable = new BluetoothAvrcpResultRunnable(aRes, aMethod); + } else { + runnable = new BluetoothAvrcpErrorRunnable(aRes, + &BluetoothAvrcpResultHandler::OnError, aStatus); + } + nsresult rv = NS_DispatchToMainThread(runnable); + if (NS_FAILED(rv)) { + BT_WARNING("NS_DispatchToMainThread failed: %X", rv); + } + return rv; +} + BluetoothAvrcpInterface::BluetoothAvrcpInterface( const btrc_interface_t* aInterface) : mInterface(aInterface) @@ -907,86 +992,157 @@ BluetoothAvrcpInterface::BluetoothAvrcpInterface( BluetoothAvrcpInterface::~BluetoothAvrcpInterface() { } -bt_status_t -BluetoothAvrcpInterface::Init(btrc_callbacks_t* aCallbacks) +void +BluetoothAvrcpInterface::Init(btrc_callbacks_t* aCallbacks, + BluetoothAvrcpResultHandler* aRes) { - return mInterface->init(aCallbacks); + bt_status_t status = mInterface->init(aCallbacks); + + if (aRes) { + DispatchBluetoothAvrcpResult(aRes, &BluetoothAvrcpResultHandler::Init, + status); + } } void -BluetoothAvrcpInterface::Cleanup() +BluetoothAvrcpInterface::Cleanup(BluetoothAvrcpResultHandler* aRes) { mInterface->cleanup(); + + if (aRes) { + DispatchBluetoothAvrcpResult(aRes, &BluetoothAvrcpResultHandler::Cleanup, + BT_STATUS_SUCCESS); + } } -bt_status_t +void BluetoothAvrcpInterface::GetPlayStatusRsp(btrc_play_status_t aPlayStatus, - uint32_t aSongLen, uint32_t aSongPos) + uint32_t aSongLen, uint32_t aSongPos, + BluetoothAvrcpResultHandler* aRes) { - return mInterface->get_play_status_rsp(aPlayStatus, aSongLen, aSongPos); + bt_status_t status = mInterface->get_play_status_rsp(aPlayStatus, aSongLen, + aSongPos); + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::GetPlayStatusRsp, status); + } } -bt_status_t -BluetoothAvrcpInterface::ListPlayerAppAttrRsp(int aNumAttr, - btrc_player_attr_t* aPAttrs) +void +BluetoothAvrcpInterface::ListPlayerAppAttrRsp( + int aNumAttr, btrc_player_attr_t* aPAttrs, + BluetoothAvrcpResultHandler* aRes) { - return mInterface->list_player_app_attr_rsp(aNumAttr, aPAttrs); + bt_status_t status = mInterface->list_player_app_attr_rsp(aNumAttr, aPAttrs); + + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::ListPlayerAppAttrRsp, status); + } } -bt_status_t -BluetoothAvrcpInterface::ListPlayerAppValueRsp(int aNumVal, uint8_t* aPVals) +void +BluetoothAvrcpInterface::ListPlayerAppValueRsp( + int aNumVal, uint8_t* aPVals, BluetoothAvrcpResultHandler* aRes) { - return mInterface->list_player_app_value_rsp(aNumVal, aPVals); + bt_status_t status = mInterface->list_player_app_value_rsp(aNumVal, aPVals); + + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::ListPlayerAppValueRsp, status); + } } -bt_status_t -BluetoothAvrcpInterface::GetPlayerAppValueRsp(btrc_player_settings_t* aPVals) +void +BluetoothAvrcpInterface::GetPlayerAppValueRsp( + btrc_player_settings_t* aPVals, BluetoothAvrcpResultHandler* aRes) { - return mInterface->get_player_app_value_rsp(aPVals); + bt_status_t status = mInterface->get_player_app_value_rsp(aPVals); + + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::GetPlayerAppValueRsp, status); + } } -bt_status_t -BluetoothAvrcpInterface::GetPlayerAppAttrTextRsp(int aNumAttr, - btrc_player_setting_text_t* aPAttrs) +void +BluetoothAvrcpInterface::GetPlayerAppAttrTextRsp( + int aNumAttr, btrc_player_setting_text_t* aPAttrs, + BluetoothAvrcpResultHandler* aRes) { - return mInterface->get_player_app_attr_text_rsp(aNumAttr, aPAttrs); + bt_status_t status = mInterface->get_player_app_attr_text_rsp(aNumAttr, + aPAttrs); + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::GetPlayerAppAttrTextRsp, status); + } } -bt_status_t -BluetoothAvrcpInterface::GetPlayerAppValueTextRsp(int aNumVal, - btrc_player_setting_text_t* aPVals) +void +BluetoothAvrcpInterface::GetPlayerAppValueTextRsp( + int aNumVal, btrc_player_setting_text_t* aPVals, + BluetoothAvrcpResultHandler* aRes) { - return mInterface->get_player_app_value_text_rsp(aNumVal, aPVals); + bt_status_t status = mInterface->get_player_app_value_text_rsp(aNumVal, + aPVals); + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::GetPlayerAppValueTextRsp, status); + } } -bt_status_t -BluetoothAvrcpInterface::GetElementAttrRsp(uint8_t aNumAttr, - btrc_element_attr_val_t* aPAttrs) +void +BluetoothAvrcpInterface::GetElementAttrRsp( + uint8_t aNumAttr, btrc_element_attr_val_t* aPAttrs, + BluetoothAvrcpResultHandler* aRes) { - return mInterface->get_element_attr_rsp(aNumAttr, aPAttrs); + bt_status_t status = mInterface->get_element_attr_rsp(aNumAttr, aPAttrs); + + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::GetElementAttrRsp, status); + } } -bt_status_t -BluetoothAvrcpInterface::SetPlayerAppValueRsp(btrc_status_t aRspStatus) +void +BluetoothAvrcpInterface::SetPlayerAppValueRsp( + btrc_status_t aRspStatus, BluetoothAvrcpResultHandler* aRes) { - return mInterface->set_player_app_value_rsp(aRspStatus); + bt_status_t status = mInterface->set_player_app_value_rsp(aRspStatus); + + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::SetPlayerAppValueRsp, status); + } } -bt_status_t -BluetoothAvrcpInterface::RegisterNotificationRsp(btrc_event_id_t aEventId, - btrc_notification_type_t aType, btrc_register_notification_t* aPParam) +void +BluetoothAvrcpInterface::RegisterNotificationRsp( + btrc_event_id_t aEventId, btrc_notification_type_t aType, + btrc_register_notification_t* aPParam, BluetoothAvrcpResultHandler* aRes) { - return mInterface->register_notification_rsp(aEventId, aType, aPParam); + bt_status_t status = mInterface->register_notification_rsp(aEventId, aType, + aPParam); + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::RegisterNotificationRsp, status); + } } -bt_status_t -BluetoothAvrcpInterface::SetVolume(uint8_t aVolume) +void +BluetoothAvrcpInterface::SetVolume(uint8_t aVolume, + BluetoothAvrcpResultHandler* aRes) { #if ANDROID_VERSION >= 19 - return mInterface->set_volume(aVolume); + bt_status_t status = mInterface->set_volume(aVolume); #else - return BT_STATUS_UNSUPPORTED; + bt_status_t status = BT_STATUS_UNSUPPORTED; #endif + + if (aRes) { + DispatchBluetoothAvrcpResult( + aRes, &BluetoothAvrcpResultHandler::SetVolume, status); + } } #endif // ANDROID_VERSION >= 18 diff --git a/dom/bluetooth2/bluedroid/BluetoothInterface.h b/dom/bluetooth2/bluedroid/BluetoothInterface.h index b3ff596e77e1..62d51c19ad5b 100644 --- a/dom/bluetooth2/bluedroid/BluetoothInterface.h +++ b/dom/bluetooth2/bluedroid/BluetoothInterface.h @@ -181,16 +181,37 @@ class BluetoothHandsfreeInterface // Bluetooth Advanced Audio Interface // +class BluetoothA2dpResultHandler +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothA2dpResultHandler) + + virtual ~BluetoothA2dpResultHandler() { } + + virtual void OnError(bt_status_t aStatus) + { + BT_WARNING("received error code %d", (int)aStatus); + } + + virtual void Init() { } + virtual void Cleanup() { } + virtual void Connect() { } + virtual void Disconnect() { } +}; + class BluetoothA2dpInterface { public: friend class BluetoothInterface; - bt_status_t Init(btav_callbacks_t *aCallbacks); - void Cleanup(); + void Init(btav_callbacks_t *aCallbacks, + BluetoothA2dpResultHandler* aRes); + void Cleanup(BluetoothA2dpResultHandler* aRes); - bt_status_t Connect(bt_bdaddr_t *aBdAddr); - bt_status_t Disconnect(bt_bdaddr_t *aBdAddr); + void Connect(bt_bdaddr_t *aBdAddr, + BluetoothA2dpResultHandler* aRes); + void Disconnect(bt_bdaddr_t *aBdAddr, + BluetoothA2dpResultHandler* aRes); protected: BluetoothA2dpInterface(const btav_interface_t* aInterface); @@ -204,37 +225,79 @@ class BluetoothA2dpInterface // Bluetooth AVRCP Interface // +class BluetoothAvrcpResultHandler +{ +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothAvrcpResultHandler) + + virtual ~BluetoothAvrcpResultHandler() { } + + virtual void OnError(bt_status_t aStatus) + { + BT_WARNING("received error code %d", (int)aStatus); + } + + virtual void Init() { } + virtual void Cleanup() { } + + virtual void GetPlayStatusRsp() { } + + virtual void ListPlayerAppAttrRsp() { } + virtual void ListPlayerAppValueRsp() { } + + virtual void GetPlayerAppValueRsp() { } + virtual void GetPlayerAppAttrTextRsp() { } + virtual void GetPlayerAppValueTextRsp() { } + + virtual void GetElementAttrRsp() { } + + virtual void SetPlayerAppValueRsp() { } + + virtual void RegisterNotificationRsp() { } + + virtual void SetVolume() { } +}; + class BluetoothAvrcpInterface { #if ANDROID_VERSION >= 18 public: friend class BluetoothInterface; - bt_status_t Init(btrc_callbacks_t* aCallbacks); - void Cleanup(); + void Init(btrc_callbacks_t* aCallbacks, + BluetoothAvrcpResultHandler* aRes); + void Cleanup(BluetoothAvrcpResultHandler* aRes); - bt_status_t GetPlayStatusRsp(btrc_play_status_t aPlayStatus, - uint32_t aSongLen, uint32_t aSongPos); + void GetPlayStatusRsp(btrc_play_status_t aPlayStatus, + uint32_t aSongLen, uint32_t aSongPos, + BluetoothAvrcpResultHandler* aRes); - bt_status_t ListPlayerAppAttrRsp(int aNumAttr, btrc_player_attr_t* aPAttrs); - bt_status_t ListPlayerAppValueRsp(int aNumVal, uint8_t* aPVals); + void ListPlayerAppAttrRsp(int aNumAttr, btrc_player_attr_t* aPAttrs, + BluetoothAvrcpResultHandler* aRes); + void ListPlayerAppValueRsp(int aNumVal, uint8_t* aPVals, + BluetoothAvrcpResultHandler* aRes); - bt_status_t GetPlayerAppValueRsp(btrc_player_settings_t* aPVals); - bt_status_t GetPlayerAppAttrTextRsp(int aNumAttr, - btrc_player_setting_text_t* aPAttrs); - bt_status_t GetPlayerAppValueTextRsp(int aNumVal, - btrc_player_setting_text_t* aPVals); + void GetPlayerAppValueRsp(btrc_player_settings_t* aPVals, + BluetoothAvrcpResultHandler* aRes); + void GetPlayerAppAttrTextRsp(int aNumAttr, + btrc_player_setting_text_t* aPAttrs, + BluetoothAvrcpResultHandler* aRes); + void GetPlayerAppValueTextRsp(int aNumVal, + btrc_player_setting_text_t* aPVals, + BluetoothAvrcpResultHandler* aRes); - bt_status_t GetElementAttrRsp(uint8_t aNumAttr, - btrc_element_attr_val_t* aPAttrs); + void GetElementAttrRsp(uint8_t aNumAttr, btrc_element_attr_val_t* aPAttrs, + BluetoothAvrcpResultHandler* aRes); - bt_status_t SetPlayerAppValueRsp(btrc_status_t aRspStatus); + void SetPlayerAppValueRsp(btrc_status_t aRspStatus, + BluetoothAvrcpResultHandler* aRes); - bt_status_t RegisterNotificationRsp(btrc_event_id_t aEventId, - btrc_notification_type_t aType, - btrc_register_notification_t* aPParam); + void RegisterNotificationRsp(btrc_event_id_t aEventId, + btrc_notification_type_t aType, + btrc_register_notification_t* aPParam, + BluetoothAvrcpResultHandler* aRes); - bt_status_t SetVolume(uint8_t aVolume); + void SetVolume(uint8_t aVolume, BluetoothAvrcpResultHandler* aRes); protected: BluetoothAvrcpInterface(const btrc_interface_t* aInterface); diff --git a/dom/system/gonk/AudioManager.cpp b/dom/system/gonk/AudioManager.cpp index 0fcb2b1ac862..a4a2ceead67b 100644 --- a/dom/system/gonk/AudioManager.cpp +++ b/dom/system/gonk/AudioManager.cpp @@ -78,6 +78,7 @@ static int sMaxStreamVolumeTbl[AUDIO_STREAM_CNT] = { static int sHeadsetState; static const int kBtSampleRate = 8000; static bool sSwitchDone = true; +static bool sA2dpSwitchDone = true; namespace mozilla { namespace dom { @@ -196,6 +197,19 @@ static void ProcessDelayedAudioRoute(SwitchState aState) sSwitchDone = true; } +static void ProcessDelayedA2dpRoute(audio_policy_dev_state_t aState, const char *aAddress) +{ + if (sA2dpSwitchDone) + return; + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, + aState, aAddress); + String8 cmd("bluetooth_enabled=false"); + AudioSystem::setParameters(0, cmd); + cmd.setTo("A2dpSuspended=true"); + AudioSystem::setParameters(0, cmd); + sA2dpSwitchDone = true; +} + NS_IMPL_ISUPPORTS(AudioManager, nsIAudioManager, nsIObserver) static void @@ -262,18 +276,18 @@ AudioManager::HandleBluetoothStatusChanged(nsISupports* aSubject, SetForceForUse(nsIAudioManager::USE_COMMUNICATION, nsIAudioManager::FORCE_NONE); } } else if (!strcmp(aTopic, BLUETOOTH_A2DP_STATUS_CHANGED_ID)) { - AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, - audioState, aAddress.get()); - if (audioState == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { + if (audioState == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE && sA2dpSwitchDone) { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, NewRunnableFunction(&ProcessDelayedA2dpRoute, audioState, aAddress.get()), 1000); + sA2dpSwitchDone = false; + } else { + AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, + audioState, aAddress.get()); String8 cmd("bluetooth_enabled=true"); AudioSystem::setParameters(0, cmd); cmd.setTo("A2dpSuspended=false"); AudioSystem::setParameters(0, cmd); - } else { - String8 cmd("bluetooth_enabled=false"); - AudioSystem::setParameters(0, cmd); - cmd.setTo("A2dpSuspended=true"); - AudioSystem::setParameters(0, cmd); + sA2dpSwitchDone = true; } } else if (!strcmp(aTopic, BLUETOOTH_HFP_STATUS_CHANGED_ID)) { AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, diff --git a/ipc/app/Makefile.in b/ipc/app/Makefile.in index 5cc4d7a2b41c..6c18864b7029 100644 --- a/ipc/app/Makefile.in +++ b/ipc/app/Makefile.in @@ -37,6 +37,10 @@ include $(topsrcdir)/config/config.mk include $(topsrcdir)/config/rules.mk +ifneq ($(MOZ_WIDGET_TOOLKIT),android) +#LIBS += ../contentproc/$(LIB_PREFIX)plugin-container.$(LIB_SUFFIX) +endif + ifeq ($(OS_ARCH),WINNT) #{ # Note the manifest file exists in the tree, so we use the explicit filename # here. diff --git a/ipc/app/MozillaRuntimeMain.cpp b/ipc/app/MozillaRuntimeMain.cpp index 4bdb6a56dd8a..67b636333145 100644 --- a/ipc/app/MozillaRuntimeMain.cpp +++ b/ipc/app/MozillaRuntimeMain.cpp @@ -4,148 +4,9 @@ * 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 "nsXPCOM.h" -#include "nsXULAppAPI.h" - -// FIXME/cjones testing -#if !defined(OS_WIN) -#include -#endif - -#ifdef XP_WIN -#include -// we want a wmain entry point -// but we don't want its DLL load protection, because we'll handle it here -#define XRE_DONT_PROTECT_DLL_LOAD -#include "nsWindowsWMain.cpp" -#include "nsSetDllDirectory.h" -#endif - -#if defined(XP_WIN) -#include "sandbox/chromium/base/basictypes.h" -#include "sandbox/win/src/sandbox.h" -#include "sandbox/win/src/sandbox_factory.h" -#include "mozilla/sandboxTarget.h" -#endif - -#ifdef MOZ_WIDGET_GONK -# include -# include - -# include - -# ifdef LOGE_IF -# undef LOGE_IF -# endif - -# include -# define LOGE_IF(cond, ...) \ - ( (CONDITION(cond)) \ - ? ((void)__android_log_print(ANDROID_LOG_ERROR, \ - "Gecko:MozillaRntimeMain", __VA_ARGS__)) \ - : (void)0 ) - -#endif - -#ifdef MOZ_NUWA_PROCESS -#include -#include "ipc/Nuwa.h" -#endif - -#ifdef MOZ_WIDGET_GONK -static void -InitializeBinder(void *aDummy) { - // Change thread priority to 0 only during calling ProcessState::self(). - // The priority is registered to binder driver and used for default Binder - // Thread's priority. - // To change the process's priority to small value need's root permission. - int curPrio = getpriority(PRIO_PROCESS, 0); - int err = setpriority(PRIO_PROCESS, 0, 0); - MOZ_ASSERT(!err); - LOGE_IF(err, "setpriority failed. Current process needs root permission."); - android::ProcessState::self()->startThreadPool(); - setpriority(PRIO_PROCESS, 0, curPrio); -} -#endif - -#if defined(XP_WIN) -static bool gIsSandboxEnabled = false; -void StartSandboxCallback() -{ - if (gIsSandboxEnabled) { - sandbox::TargetServices* target_service = - sandbox::SandboxFactory::GetTargetServices(); - target_service->LowerToken(); - } -} -#endif - +#include "../contentproc/plugin-container.cpp" + int -main(int argc, char* argv[]) -{ - bool isNuwa = false; - for (int i = 1; i < argc; i++) { - isNuwa |= strcmp(argv[i], "-nuwa") == 0; -#if defined(XP_WIN) - gIsSandboxEnabled |= strcmp(argv[i], "-sandbox") == 0; -#endif - } - -#ifdef MOZ_NUWA_PROCESS - if (isNuwa) { - PrepareNuwaProcess(); - } -#endif - -#ifdef MOZ_WIDGET_GONK - // This creates a ThreadPool for binder ipc. A ThreadPool is necessary to - // receive binder calls, though not necessary to send binder calls. - // ProcessState::Self() also needs to be called once on the main thread to - // register the main thread with the binder driver. - -#ifdef MOZ_NUWA_PROCESS - if (!isNuwa) { - InitializeBinder(nullptr); - } else { - NuwaAddFinalConstructor(&InitializeBinder, nullptr); - } -#else - InitializeBinder(nullptr); -#endif -#endif - - // Check for the absolute minimum number of args we need to move - // forward here. We expect the last arg to be the child process type. - if (argc < 1) - return 3; - GeckoProcessType proctype = XRE_StringToChildProcessType(argv[--argc]); - -#ifdef XP_WIN - // For plugins, this is done in PluginProcessChild::Init, as we need to - // avoid it for unsupported plugins. See PluginProcessChild::Init for - // the details. - if (proctype != GeckoProcessType_Plugin) { - mozilla::SanitizeEnvironmentVariables(); - SetDllDirectory(L""); - } - - if (gIsSandboxEnabled) { - sandbox::TargetServices* target_service = - sandbox::SandboxFactory::GetTargetServices(); - if (!target_service) { - return 1; - } - - sandbox::ResultCode result = target_service->Init(); - if (result != sandbox::SBOX_ALL_OK) { - return 2; - } - mozilla::SandboxTarget::Instance()->SetStartSandboxCallback(StartSandboxCallback); - } -#endif - - nsresult rv = XRE_InitChildProcess(argc, argv, proctype); - NS_ENSURE_SUCCESS(rv, 1); - - return 0; +main(int argc, char *argv[]) { + return content_process_main(argc, argv); } diff --git a/ipc/chromium/src/base/process_util_linux.cc b/ipc/chromium/src/base/process_util_linux.cc index 0be1b7edd770..741e912b8d9d 100644 --- a/ipc/chromium/src/base/process_util_linux.cc +++ b/ipc/chromium/src/base/process_util_linux.cc @@ -1,3 +1,5 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 autoindent cindent expandtab: */ // Copyright (c) 2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -18,6 +20,13 @@ #include "base/logging.h" #include "base/string_tokenizer.h" #include "base/string_util.h" +#include "nsLiteralString.h" + +#ifdef MOZ_B2G_LOADER +#include "ProcessUtils.h" + +using namespace mozilla::ipc; +#endif // MOZ_B2G_LOADER #ifdef MOZ_WIDGET_GONK /* @@ -188,12 +197,71 @@ bool LaunchApp(const std::vector& argv, wait, process_handle); } +#ifdef MOZ_B2G_LOADER +/** + * Launch an app using B2g Loader. + */ +static bool +LaunchAppProcLoader(const std::vector& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + ChildPrivileges privs, + ProcessHandle* process_handle) { + size_t i; + scoped_array argv_cstr(new char*[argv.size() + 1]); + for (i = 0; i < argv.size(); i++) { + argv_cstr[i] = const_cast(argv[i].c_str()); + } + argv_cstr[argv.size()] = nullptr; + + scoped_array env_cstr(new char*[env_vars_to_set.size() + 1]); + i = 0; + for (environment_map::const_iterator it = env_vars_to_set.begin(); + it != env_vars_to_set.end(); ++it) { + env_cstr[i++] = strdup((it->first + "=" + it->second).c_str()); + } + env_cstr[env_vars_to_set.size()] = nullptr; + + bool ok = ProcLoaderLoad((const char **)argv_cstr.get(), + (const char **)env_cstr.get(), + fds_to_remap, privs, + process_handle); + MOZ_ASSERT(ok, "ProcLoaderLoad() failed"); + + for (size_t i = 0; i < env_vars_to_set.size(); i++) { + free(env_cstr[i]); + } + + return ok; +} + +static bool +IsLaunchingNuwa(const std::vector& argv) { + std::vector::const_iterator it; + for (it = argv.begin(); it != argv.end(); ++it) { + if (*it == std::string("-nuwa")) { + return true; + } + } + return false; +} +#endif // MOZ_B2G_LOADER + bool LaunchApp(const std::vector& argv, const file_handle_mapping_vector& fds_to_remap, const environment_map& env_vars_to_set, ChildPrivileges privs, bool wait, ProcessHandle* process_handle, ProcessArchitecture arch) { +#ifdef MOZ_B2G_LOADER + static bool beforeFirstNuwaLaunch = true; + if (!wait && beforeFirstNuwaLaunch && IsLaunchingNuwa(argv)) { + beforeFirstNuwaLaunch = false; + return LaunchAppProcLoader(argv, fds_to_remap, env_vars_to_set, + privs, process_handle); + } +#endif // MOZ_B2G_LOADER + scoped_array argv_cstr(new char*[argv.size() + 1]); // Illegal to allocate memory after fork and before execvp InjectiveMultimap fd_shuffle1, fd_shuffle2; diff --git a/ipc/contentproc/moz.build b/ipc/contentproc/moz.build new file mode 100644 index 000000000000..39b5d3273791 --- /dev/null +++ b/ipc/contentproc/moz.build @@ -0,0 +1,29 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +LIBRARY_NAME = 'plugin-container' +if CONFIG['MOZ_B2G_LOADER']: + FINAL_LIBRARY = 'xul' + +SOURCES += [ + 'plugin-container.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +if CONFIG['OS_ARCH'] == 'WINNT': + LOCAL_INCLUDES += [ + '/toolkit/xre', + '/xpcom/base', + ] + +if CONFIG['OS_ARCH'] == 'WINNT': + # For sandbox includes and the include dependencies those have + LOCAL_INCLUDES += [ + '/security', + '/security/sandbox', + '/security/sandbox/chromium', + ] diff --git a/ipc/contentproc/plugin-container.cpp b/ipc/contentproc/plugin-container.cpp new file mode 100644 index 000000000000..e8fe2cc2549f --- /dev/null +++ b/ipc/contentproc/plugin-container.cpp @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * 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 "nsXPCOM.h" +#include "nsXULAppAPI.h" + +// FIXME/cjones testing +#if !defined(OS_WIN) +#include +#endif + +#ifdef XP_WIN +#include +// we want a wmain entry point +// but we don't want its DLL load protection, because we'll handle it here +#define XRE_DONT_PROTECT_DLL_LOAD +#include "nsWindowsWMain.cpp" +#include "nsSetDllDirectory.h" +#endif + +#if defined(XP_WIN) +#include "sandbox/chromium/base/basictypes.h" +#include "sandbox/win/src/sandbox.h" +#include "sandbox/win/src/sandbox_factory.h" +#include "mozilla/sandboxTarget.h" +#endif + +#ifdef MOZ_WIDGET_GONK +# include +# include + +# include + +# ifdef LOGE_IF +# undef LOGE_IF +# endif + +# include +# define LOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_print(ANDROID_LOG_ERROR, \ + "Gecko:MozillaRntimeMain", __VA_ARGS__)) \ + : (void)0 ) + +#endif + +#ifdef MOZ_NUWA_PROCESS +#include +#include "ipc/Nuwa.h" +#endif + +#ifdef MOZ_WIDGET_GONK +static void +InitializeBinder(void *aDummy) { + // Change thread priority to 0 only during calling ProcessState::self(). + // The priority is registered to binder driver and used for default Binder + // Thread's priority. + // To change the process's priority to small value need's root permission. + int curPrio = getpriority(PRIO_PROCESS, 0); + int err = setpriority(PRIO_PROCESS, 0, 0); + MOZ_ASSERT(!err); + LOGE_IF(err, "setpriority failed. Current process needs root permission."); + android::ProcessState::self()->startThreadPool(); + setpriority(PRIO_PROCESS, 0, curPrio); +} +#endif + +#if defined(XP_WIN) +static bool gIsSandboxEnabled = false; +void StartSandboxCallback() +{ + if (gIsSandboxEnabled) { + sandbox::TargetServices* target_service = + sandbox::SandboxFactory::GetTargetServices(); + target_service->LowerToken(); + } +} +#endif + +int +content_process_main(int argc, char* argv[]) +{ + bool isNuwa = false; + for (int i = 1; i < argc; i++) { + isNuwa |= strcmp(argv[i], "-nuwa") == 0; +#if defined(XP_WIN) + gIsSandboxEnabled |= strcmp(argv[i], "-sandbox") == 0; +#endif + } + +#ifdef MOZ_NUWA_PROCESS + if (isNuwa) { + PrepareNuwaProcess(); + } +#endif + +#ifdef MOZ_WIDGET_GONK + // This creates a ThreadPool for binder ipc. A ThreadPool is necessary to + // receive binder calls, though not necessary to send binder calls. + // ProcessState::Self() also needs to be called once on the main thread to + // register the main thread with the binder driver. + +#ifdef MOZ_NUWA_PROCESS + if (!isNuwa) { + InitializeBinder(nullptr); + } else { + NuwaAddFinalConstructor(&InitializeBinder, nullptr); + } +#else + InitializeBinder(nullptr); +#endif +#endif + + // Check for the absolute minimum number of args we need to move + // forward here. We expect the last arg to be the child process type. + if (argc < 1) + return 3; + GeckoProcessType proctype = XRE_StringToChildProcessType(argv[--argc]); + +#ifdef XP_WIN + // For plugins, this is done in PluginProcessChild::Init, as we need to + // avoid it for unsupported plugins. See PluginProcessChild::Init for + // the details. + if (proctype != GeckoProcessType_Plugin) { + mozilla::SanitizeEnvironmentVariables(); + SetDllDirectory(L""); + } + + if (gIsSandboxEnabled) { + sandbox::TargetServices* target_service = + sandbox::SandboxFactory::GetTargetServices(); + if (!target_service) { + return 1; + } + + sandbox::ResultCode result = target_service->Init(); + if (result != sandbox::SBOX_ALL_OK) { + return 2; + } + mozilla::SandboxTarget::Instance()->SetStartSandboxCallback(StartSandboxCallback); + } +#endif + + nsresult rv = XRE_InitChildProcess(argc, argv, proctype); + NS_ENSURE_SUCCESS(rv, 1); + + return 0; +} diff --git a/ipc/glue/PProcLoader.ipdl b/ipc/glue/PProcLoader.ipdl new file mode 100644 index 000000000000..d295d8b7221a --- /dev/null +++ b/ipc/glue/PProcLoader.ipdl @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 autoindent cindent expandtab: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +namespace mozilla { +namespace ipc { + +struct FDRemap { + FileDescriptor fd; + int mapto; +}; + +protocol PProcLoader +{ +child: + /** + * Request B2G loader service to load content process. + * + * It actually calls the main() function of plugin-container. + */ + async Load(nsCString[] argv, nsCString[] env, + FDRemap[] fdsRemap, uint32_t privs, + int32_t cookie); + +parent: + /** + * The acknowledgement of Load(). + */ + async LoadComplete(int32_t pid, int32_t cookie); +}; + +} +} diff --git a/ipc/glue/ProcessUtils.h b/ipc/glue/ProcessUtils.h index fc17609dcadc..99d0f532dd8b 100644 --- a/ipc/glue/ProcessUtils.h +++ b/ipc/glue/ProcessUtils.h @@ -1,3 +1,5 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 autoindent cindent expandtab: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -5,6 +7,10 @@ #ifndef mozilla_ipc_ProcessUtils_h #define mozilla_ipc_ProcessUtils_h +#ifdef MOZ_B2G_LOADER +#include "base/process_util.h" +#endif + namespace mozilla { namespace ipc { @@ -12,6 +18,17 @@ namespace ipc { // this directly. void SetThisProcessName(const char *aName); +#ifdef MOZ_B2G_LOADER +// see ProcessUtils_linux.cpp for explaination. +void ProcLoaderClientGeckoInit(); + +bool ProcLoaderLoad(const char *aArgv[], + const char *aEnvp[], + const base::file_handle_mapping_vector &aFdsRemap, + const base::ChildPrivileges aPrivs, + base::ProcessHandle *aProcessHandle); +#endif /* MOZ_B2G_LOADER */ + } // namespace ipc } // namespace mozilla diff --git a/ipc/glue/ProcessUtils_linux.cpp b/ipc/glue/ProcessUtils_linux.cpp index d5b8c092e0f3..10156e63a2c4 100644 --- a/ipc/glue/ProcessUtils_linux.cpp +++ b/ipc/glue/ProcessUtils_linux.cpp @@ -1,3 +1,5 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 autoindent cindent expandtab: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -8,6 +10,37 @@ #include +#ifdef MOZ_B2G_LOADER + +#include +#include + +#include "nsAutoPtr.h" + +#include "mozilla/Assertions.h" +#include "mozilla/ipc/PProcLoaderParent.h" +#include "mozilla/ipc/PProcLoaderChild.h" +#include "mozilla/ipc/Transport.h" +#include "mozilla/ipc/FileDescriptorUtils.h" +#include "mozilla/ipc/IOThreadChild.h" +#include "mozilla/dom/ContentProcess.h" +#include "base/file_descriptor_shuffle.h" +#include "mozilla/BackgroundHangMonitor.h" +#include "mozilla/DebugOnly.h" +#include "base/process_util.h" + +#include "prenv.h" + +#include "nsXULAppAPI.h" // export XRE_* functions + +#include "nsAppRunner.h" + +int content_process_main(int argc, char *argv[]); + +extern bool gDisableAndroidLog; + +#endif /* MOZ_B2G_LOADER */ + namespace mozilla { namespace ipc { @@ -16,5 +49,522 @@ void SetThisProcessName(const char *aName) prctl(PR_SET_NAME, (unsigned long)aName, 0uL, 0uL, 0uL); } +#ifdef MOZ_B2G_LOADER +/** + * How does B2G Loader Work? + * + * <> <> + * ProcLoaderParent -----> ProcLoaderChild + * ^ | + * | load() | content_process_main() + * | V + * ProcLoaderClient Nuwa/plugin-container + * ^ + * | ProcLoaderLoad() + * ... + * ContentParent + * + * + * B2G loader includes an IPC protocol PProcLoader for communication + * between client (parent) and server (child). The b2g process is the + * client. It requests the server to load/start the Nuwa process with + * the given arguments, env variables, and file descriptors. + * + * ProcLoaderClientInit() is called by B2G loader to initialize the + * client side, the b2g process. Then the b2g_main() is called to + * start b2g process. + * + * ProcLoaderClientGeckoInit() is called by XRE_main() to create the + * parent actor, |ProcLoaderParent|, of PProcLoader for servicing the + * request to run Nuwa process later once Gecko has been initialized. + * + * ProcLoaderServiceRun() is called by the server process. It starts + * an IOThread and event loop to serve the |ProcLoaderChild| + * implmentation of PProcLoader protocol as the child actor. Once it + * recieves a load() request, it stops the IOThread and event loop, + * then starts running the main function of the content process with + * the given arguments. + * + * NOTE: The server process serves at most one load() request. + */ + +using namespace base; +using namespace mozilla::dom; + +static bool sProcLoaderClientOnDeinit = false; +static DebugOnly sProcLoaderClientInitialized = false; +static DebugOnly sProcLoaderClientGeckoInitialized = false; +static pid_t sProcLoaderPid = 0; +static int sProcLoaderChannelFd = -1; +static PProcLoaderParent *sProcLoaderParent = nullptr; +static MessageLoop *sProcLoaderLoop = nullptr; + +static void ProcLoaderClientDeinit(); + + +class ProcLoaderParent : public PProcLoaderParent +{ +private: + nsAutoPtr mChannelFd; // To keep a reference. + +public: + ProcLoaderParent(FileDescriptor *aFd) : mChannelFd(aFd) {} + + virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool RecvLoadComplete(const int32_t &aPid, + const int32_t &aCookie) MOZ_OVERRIDE; + + virtual void OnChannelError() MOZ_OVERRIDE; +}; + +void +ProcLoaderParent::ActorDestroy(ActorDestroyReason aWhy) +{ +} + +static void +_ProcLoaderParentDestroy(PProcLoaderParent *aLoader) +{ + aLoader->Close(); + delete aLoader; + sProcLoaderClientOnDeinit = false; +} + +bool +ProcLoaderParent::RecvLoadComplete(const int32_t &aPid, + const int32_t &aCookie) +{ + ProcLoaderClientDeinit(); + return true; +} + +void +ProcLoaderParent::OnChannelError() +{ + if (sProcLoaderClientOnDeinit) { + // Get error for closing while the channel is already error. + return; + } + NS_WARNING("ProcLoaderParent is in channel error"); + ProcLoaderClientDeinit(); +} + +/** + * Initialize the client of B2G loader for loader itself. + * + * The initialization of B2G loader are divided into two stages. First + * stage is to collect child info passed from the main program of the + * loader. Second stage is to initialize Gecko according to info from the + * first stage and make the client of loader service ready. + * + * \param aPeerPid is the pid of the child. + * \param aChannelFd is the file descriptor of the socket used for IPC. + */ +static void +ProcLoaderClientInit(pid_t aPeerPid, int aChannelFd) +{ + MOZ_ASSERT(!sProcLoaderClientInitialized, "call ProcLoaderClientInit() more than once"); + MOZ_ASSERT(aPeerPid != 0 && aChannelFd != -1, "invalid argument"); + sProcLoaderPid = aPeerPid; + sProcLoaderChannelFd = aChannelFd; + sProcLoaderClientInitialized = true; +} + +/** + * Initialize the client of B2G loader for Gecko. + */ +void +ProcLoaderClientGeckoInit() +{ + MOZ_ASSERT(sProcLoaderClientInitialized, "call ProcLoaderClientInit() at first"); + MOZ_ASSERT(!sProcLoaderClientGeckoInitialized, + "call ProcLoaderClientGeckoInit() more than once"); + + sProcLoaderClientGeckoInitialized = true; + + FileDescriptor *fd = new FileDescriptor(sProcLoaderChannelFd); + close(sProcLoaderChannelFd); + sProcLoaderChannelFd = -1; + Transport *transport = OpenDescriptor(*fd, Transport::MODE_CLIENT); + sProcLoaderParent = new ProcLoaderParent(fd); + sProcLoaderParent->Open(transport, + sProcLoaderPid, + XRE_GetIOMessageLoop(), + ParentSide); + sProcLoaderLoop = MessageLoop::current(); +} + +/** + * Shutdown and destroy the client of B2G loader service. + */ +static void +ProcLoaderClientDeinit() +{ + MOZ_ASSERT(sProcLoaderClientGeckoInitialized && sProcLoaderClientInitialized); + sProcLoaderClientGeckoInitialized = false; + sProcLoaderClientInitialized = false; + + sProcLoaderClientOnDeinit = true; + + MOZ_ASSERT(sProcLoaderParent != nullptr); + PProcLoaderParent *procLoaderParent = sProcLoaderParent; + sProcLoaderParent = nullptr; + sProcLoaderLoop = nullptr; + + MessageLoop::current()-> + PostTask(FROM_HERE, + NewRunnableFunction(&_ProcLoaderParentDestroy, + procLoaderParent)); +} + +struct AsyncSendLoadData +{ + nsTArray mArgv; + nsTArray mEnv; + nsTArray mFdsremap; + ChildPrivileges mPrivs; + int mCookie; +}; + +static void +AsyncSendLoad(AsyncSendLoadData *aLoad) +{ + PProcLoaderParent *loader = sProcLoaderParent; + DebugOnly ok = + loader->SendLoad(aLoad->mArgv, aLoad->mEnv, aLoad->mFdsremap, + aLoad->mPrivs, aLoad->mCookie); + MOZ_ASSERT(ok); + delete aLoad; +} + +/** + * Request the loader service, the server, to load Nuwa. + */ +bool +ProcLoaderLoad(const char *aArgv[], + const char *aEnvp[], + const file_handle_mapping_vector &aFdsRemap, + const ChildPrivileges aPrivs, + ProcessHandle *aProcessHandle) +{ + static int cookie=0; + int i; + + if (sProcLoaderParent == nullptr || sProcLoaderPid == 0) { + return false; + } + + AsyncSendLoadData *load = new AsyncSendLoadData(); + nsTArray &argv = load->mArgv; + for (i = 0; aArgv[i] != nullptr; i++) { + argv.AppendElement(nsCString(aArgv[i])); + } + nsTArray &env = load->mEnv; + for (i = 0; aEnvp[i] != nullptr; i++) { + env.AppendElement(nsCString(aEnvp[i])); + } + nsTArray &fdsremap = load->mFdsremap; + for (file_handle_mapping_vector::const_iterator fdmap = + aFdsRemap.begin(); + fdmap != aFdsRemap.end(); + fdmap++) { + fdsremap.AppendElement(FDRemap(fdmap->first, fdmap->second)); + } + load->mPrivs = aPrivs; + load->mCookie = cookie++; + + *aProcessHandle = sProcLoaderPid; + sProcLoaderPid = 0; + + sProcLoaderLoop->PostTask(FROM_HERE, + NewRunnableFunction(AsyncSendLoad, load)); + return true; +} + + +class ProcLoaderRunnerBase; + +static bool sProcLoaderServing = false; +static ProcLoaderRunnerBase *sProcLoaderDispatchedTask = nullptr; + +class ProcLoaderRunnerBase +{ +public: + virtual int DoWork() = 0; +}; + + +class ProcLoaderNoopRunner : public ProcLoaderRunnerBase { +public: + virtual int DoWork(); +}; + +int +ProcLoaderNoopRunner::DoWork() { + return 0; +} + + +/** + * The runner to load Nuwa at the current process. + */ +class ProcLoaderLoadRunner : public ProcLoaderRunnerBase { +private: + const nsTArray mArgv; + const nsTArray mEnv; + const nsTArray mFdsRemap; + const ChildPrivileges mPrivs; + + void ShuffleFds(); + +public: + ProcLoaderLoadRunner(const InfallibleTArray& aArgv, + const InfallibleTArray& aEnv, + const InfallibleTArray& aFdsRemap, + const ChildPrivileges aPrivs) + : mArgv(aArgv) + , mEnv(aEnv) + , mFdsRemap(aFdsRemap) + , mPrivs(aPrivs) {} + + int DoWork(); +}; + +void +ProcLoaderLoadRunner::ShuffleFds() +{ + unsigned int i; + + InjectiveMultimap fd_shuffle1, fd_shuffle2; + fd_shuffle1.reserve(mFdsRemap.Length()); + fd_shuffle2.reserve(mFdsRemap.Length()); + for (i = 0; i < mFdsRemap.Length(); i++) { + const FDRemap *map = &mFdsRemap[i]; + int fd = map->fd().PlatformHandle(); + int tofd = map->mapto(); + + fd_shuffle1.push_back(InjectionArc(fd, tofd, false)); + fd_shuffle2.push_back(InjectionArc(fd, tofd, false)); + } + + DebugOnly ok = ShuffleFileDescriptors(&fd_shuffle1); + MOZ_ASSERT(ok, "ShuffleFileDescriptors failed"); + + CloseSuperfluousFds(fd_shuffle2); +} + +int +ProcLoaderLoadRunner::DoWork() +{ + unsigned int i; + + ShuffleFds(); + + unsigned int argc = mArgv.Length(); + char **argv = new char *[argc + 1]; + for (i = 0; i < argc; i++) { + argv[i] = ::strdup(mArgv[i].get()); + } + argv[argc] = nullptr; + + unsigned int envc = mEnv.Length(); + for (i = 0; i < envc; i++) { + PR_SetEnv(mEnv[i].get()); + } + + SetCurrentProcessPrivileges(mPrivs); + + MOZ_ASSERT(content_process_main != nullptr, + "content_process_main not found"); + // Start Nuwa (main function) + int ret = content_process_main(argc, argv); + + for (i = 0; i < argc; i++) { + free(argv[i]); + } + delete[] argv; + + return ret; +} + + +class ProcLoaderChild : public PProcLoaderChild +{ + pid_t mPeerPid; + +public: + ProcLoaderChild(pid_t aPeerPid) : mPeerPid(aPeerPid) {} + + virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + + virtual bool RecvLoad(const InfallibleTArray& aArgv, + const InfallibleTArray& aEnv, + const InfallibleTArray& aFdsremap, + const uint32_t& aPrivs, + const int32_t& aCookie); + + virtual void OnChannelError(); +}; + +void +ProcLoaderChild::ActorDestroy(ActorDestroyReason aWhy) +{ +} + +static void +_ProcLoaderChildDestroy(ProcLoaderChild *aChild) +{ + aChild->Close(); + delete aChild; + MessageLoop::current()->Quit(); +} + +bool +ProcLoaderChild::RecvLoad(const InfallibleTArray& aArgv, + const InfallibleTArray& aEnv, + const InfallibleTArray& aFdsRemap, + const uint32_t& aPrivs, + const int32_t& aCookie) { + if (!sProcLoaderServing) { + return true; + } + sProcLoaderServing = false; + + MOZ_ASSERT(sProcLoaderDispatchedTask == nullptr); + ChildPrivileges privs = static_cast(aPrivs); + sProcLoaderDispatchedTask = + new ProcLoaderLoadRunner(aArgv, aEnv, aFdsRemap, privs); + + SendLoadComplete(mPeerPid, aCookie); + + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(_ProcLoaderChildDestroy, + this)); + return true; +} + +void +ProcLoaderChild::OnChannelError() +{ + if (!sProcLoaderServing) { + return; + } + sProcLoaderServing = false; + + PProcLoaderChild::OnChannelError(); + + MOZ_ASSERT(sProcLoaderDispatchedTask == nullptr); + sProcLoaderDispatchedTask = new ProcLoaderNoopRunner(); + + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(_ProcLoaderChildDestroy, + this)); +} + +/** + * A helper class which calls NS_LogInit/NS_LogTerm in its scope. + */ +class ScopedLogging +{ +public: + ScopedLogging() { NS_LogInit(); } + ~ScopedLogging() { NS_LogTerm(); } +}; + +/** + * Run service of ProcLoader. + * + * \param aPeerPid is the pid of the parent. + * \param aFd is the file descriptor of the socket for IPC. + * + * See the comment near the head of this file. + */ +static int +ProcLoaderServiceRun(pid_t aPeerPid, int aFd, + int aArgc, const char *aArgv[]) +{ + ScopedLogging logging; + + char **_argv; + _argv = new char *[aArgc + 1]; + for (int i = 0; i < aArgc; i++) { + _argv[i] = ::strdup(aArgv[i]); + MOZ_ASSERT(_argv[i] != nullptr); + } + _argv[aArgc] = nullptr; + + gArgv = _argv; + gArgc = aArgc; + + { + gDisableAndroidLog = true; + + nsresult rv = XRE_InitCommandLine(aArgc, _argv); + if (NS_FAILED(rv)) { + gDisableAndroidLog = false; + MOZ_CRASH(); + } + + FileDescriptor fd(aFd); + close(aFd); + + MOZ_ASSERT(!sProcLoaderServing); + MessageLoop loop; + + nsAutoPtr process; + process = new ContentProcess(aPeerPid); + ChildThread *iothread = process->child_thread(); + + Transport *transport = OpenDescriptor(fd, Transport::MODE_CLIENT); + ProcLoaderChild *loaderChild = new ProcLoaderChild(aPeerPid); + // Pass a message loop to initialize (connect) the channel + // (connection). + loaderChild->Open(transport, aPeerPid, iothread->message_loop()); + + BackgroundHangMonitor::Prohibit(); + + sProcLoaderServing = true; + loop.Run(); + + BackgroundHangMonitor::Allow(); + + XRE_DeinitCommandLine(); + + gDisableAndroidLog = false; + } + + MOZ_ASSERT(sProcLoaderDispatchedTask != nullptr); + ProcLoaderRunnerBase *task = sProcLoaderDispatchedTask; + sProcLoaderDispatchedTask = nullptr; + int ret = task->DoWork(); + delete task; + + for (int i = 0; i < aArgc; i++) { + free(_argv[i]); + } + delete[] _argv; + + return ret; +} + +#endif /* MOZ_B2G_LOADER */ + } // namespace ipc } // namespace mozilla + +#ifdef MOZ_B2G_LOADER +void +XRE_ProcLoaderClientInit(pid_t aPeerPid, int aChannelFd) +{ + mozilla::ipc::ProcLoaderClientInit(aPeerPid, aChannelFd); +} + +int +XRE_ProcLoaderServiceRun(pid_t aPeerPid, int aFd, + int aArgc, const char *aArgv[]) +{ + return mozilla::ipc::ProcLoaderServiceRun(aPeerPid, aFd, + aArgc, aArgv); +} +#endif /* MOZ_B2G_LOADER */ diff --git a/ipc/glue/moz.build b/ipc/glue/moz.build index 08e50d92cfc4..af3e146b6292 100644 --- a/ipc/glue/moz.build +++ b/ipc/glue/moz.build @@ -130,12 +130,14 @@ IPDL_SOURCES = [ 'PBackground.ipdl', 'PBackgroundSharedTypes.ipdlh', 'PBackgroundTest.ipdl', + 'PProcLoader.ipdl', 'ProtocolTypes.ipdlh', 'URIParams.ipdlh', ] LOCAL_INCLUDES += [ + '/toolkit/xre', '/xpcom/threads', ] diff --git a/ipc/moz.build b/ipc/moz.build index 00273f514f59..53433dc2365e 100644 --- a/ipc/moz.build +++ b/ipc/moz.build @@ -26,4 +26,7 @@ if CONFIG['MOZ_B2G_RIL'] or CONFIG['MOZ_B2G_BT'] or CONFIG['MOZ_NFC'] or CONFIG[ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': DIRS += ['keystore', 'netd'] +if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': + DIRS += ['contentproc'] + DIRS += ['app'] diff --git a/testing/mochitest/runtests.py b/testing/mochitest/runtests.py index cd2d1f64c361..7c963ff9b59d 100644 --- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -47,6 +47,18 @@ # not yet present in the mozharness environment from mozrunner.utils import findInPath as which + +# Necessary to set up the global logger in automationutils.py +import logging +log = logging.getLogger() +def resetGlobalLog(): + while log.handlers: + log.removeHandler(log.handlers[0]) + handler = logging.StreamHandler(sys.stdout) + log.setLevel(logging.INFO) + log.addHandler(handler) +resetGlobalLog() + ########################### # Option for NSPR logging # ########################### diff --git a/toolkit/xre/moz.build b/toolkit/xre/moz.build index ba01e0d26258..0633851c8023 100644 --- a/toolkit/xre/moz.build +++ b/toolkit/xre/moz.build @@ -160,3 +160,5 @@ if CONFIG['MOZ_ENABLE_XREMOTE']: '/widget/xremoteclient', ] +if CONFIG['MOZ_B2G_LOADER']: + DEFINES['OMNIJAR_NAME'] = CONFIG['OMNIJAR_NAME'] diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 36e7aea2bc5e..fe8ed3c449e9 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -185,6 +185,10 @@ #include "GTestRunner.h" #endif +#ifdef MOZ_B2G_LOADER +#include "ProcessUtils.h" +#endif + #ifdef MOZ_WIDGET_ANDROID #include "AndroidBridge.h" #endif @@ -3772,6 +3776,10 @@ XREMain::XRE_mainRun() nsresult rv = NS_OK; NS_ASSERTION(mScopedXPCom, "Scoped xpcom not initialized."); +#ifdef MOZ_B2G_LOADER + mozilla::ipc::ProcLoaderClientGeckoInit(); +#endif + #ifdef NS_FUNCTION_TIMER // initialize some common services, so we don't pay the cost for these at odd times later on; // SetWindowCreator -> ChromeRegistry -> IOService -> SocketTransportService -> (nspr wspm init), Prefs diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp index 3a7f5b9780ff..4857abdf5f5c 100644 --- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -81,6 +81,11 @@ using mozilla::_ipdltest::IPDLUnitTestProcessChild; #endif // ifdef MOZ_IPDL_TESTS +#ifdef MOZ_B2G_LOADER +#include "nsLocalFile.h" +#include "nsXREAppData.h" +#endif + using namespace mozilla; using mozilla::ipc::BrowserProcessSubThread; @@ -815,3 +820,38 @@ XRE_GetWindowsEnvironment() } #endif // XP_WIN +#ifdef MOZ_B2G_LOADER +extern const nsXREAppData* gAppData; + +/** + * Preload static data of Gecko for B2G loader. + * + * This function is supposed to be called before XPCOM is initialized. + * For now, this function preloads + * - XPT interface Information + */ +void +XRE_ProcLoaderPreload(const char* aProgramDir, const nsXREAppData* aAppData) +{ + void PreloadXPT(nsIFile *); + + nsresult rv; + nsCOMPtr omnijarFile; + rv = NS_NewNativeLocalFile(nsCString(aProgramDir), + true, + getter_AddRefs(omnijarFile)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + rv = omnijarFile->AppendNative(NS_LITERAL_CSTRING(NS_STRINGIFY(OMNIJAR_NAME))); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + /* + * gAppData is required by nsXULAppInfo. The manifest parser + * evaluate flags with the information from nsXULAppInfo. + */ + gAppData = aAppData; + + PreloadXPT(omnijarFile); + + gAppData = nullptr; +} +#endif /* MOZ_B2G_LOADER */ diff --git a/tools/profiler/platform.cpp b/tools/profiler/platform.cpp index d84ef0478c2d..a4bac142f5ed 100644 --- a/tools/profiler/platform.cpp +++ b/tools/profiler/platform.cpp @@ -878,6 +878,10 @@ void mozilla_sampler_unlock() bool mozilla_sampler_register_thread(const char* aName, void* stackTop) { + if (sInitCount == 0) { + return false; + } + #if defined(MOZ_WIDGET_GONK) && !defined(MOZ_PROFILING) // The only way to profile secondary threads on b2g // is to build with profiling OR have the profiler @@ -895,6 +899,10 @@ bool mozilla_sampler_register_thread(const char* aName, void* stackTop) void mozilla_sampler_unregister_thread() { + if (sInitCount == 0) { + return; + } + Sampler::UnregisterCurrentThread(); PseudoStack *stack = tlsPseudoStack.get(); @@ -906,6 +914,10 @@ void mozilla_sampler_unregister_thread() } void mozilla_sampler_sleep_start() { + if (sInitCount == 0) { + return; + } + PseudoStack *stack = tlsPseudoStack.get(); if (stack == nullptr) { return; @@ -914,6 +926,10 @@ void mozilla_sampler_sleep_start() { } void mozilla_sampler_sleep_end() { + if (sInitCount == 0) { + return; + } + PseudoStack *stack = tlsPseudoStack.get(); if (stack == nullptr) { return; diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp index 0befeeece873..10735b25d5bd 100644 --- a/xpcom/base/nsDebugImpl.cpp +++ b/xpcom/base/nsDebugImpl.cpp @@ -101,6 +101,13 @@ Break(const char* aMsg); #include #endif +#ifdef MOZ_B2G_LOADER +/* Avoid calling Android logger/logd temporarily while running + * B2GLoader to start the child process. + */ +bool gDisableAndroidLog = false; +#endif + using namespace mozilla; static const char* sMultiprocessDescription = nullptr; @@ -392,6 +399,9 @@ NS_DebugBreak(uint32_t aSeverity, const char* aStr, const char* aExpr, #endif #ifdef ANDROID +#ifdef MOZ_B2G_LOADER + if (!gDisableAndroidLog) +#endif __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", buf.buffer); #endif diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h index 6f320744fd60..33329e33b23f 100644 --- a/xpcom/build/nsXULAppAPI.h +++ b/xpcom/build/nsXULAppAPI.h @@ -467,6 +467,16 @@ XRE_API(WindowsEnvironmentType, XRE_GetWindowsEnvironment, ()) #endif // XP_WIN +#ifdef MOZ_B2G_LOADER +XRE_API(int, + XRE_ProcLoaderServiceRun, (pid_t, int, int argc, const char *argv[])); +XRE_API(void, + XRE_ProcLoaderClientInit, (pid_t, int)); +XRE_API(void, + XRE_ProcLoaderPreload, (const char* aProgramDir, + const nsXREAppData* aAppData)); +#endif // MOZ_B2G_LOADER + XRE_API(int, XRE_XPCShellMain, (int argc, char** argv, char** envp)) diff --git a/xpcom/components/ManifestParser.cpp b/xpcom/components/ManifestParser.cpp index 28ac26814886..00a2d471407e 100644 --- a/xpcom/components/ManifestParser.cpp +++ b/xpcom/components/ManifestParser.cpp @@ -36,6 +36,18 @@ #include "nsIScriptError.h" #include "nsIXULAppInfo.h" #include "nsIXULRuntime.h" +#ifdef MOZ_B2G_LOADER +#include "mozilla/XPTInterfaceInfoManager.h" +#endif + +#ifdef MOZ_B2G_LOADER +#define XPTONLY_MANIFEST &nsComponentManagerImpl::XPTOnlyManifestManifest +#define XPTONLY_XPT &nsComponentManagerImpl::XPTOnlyManifestXPT +#else +#define XPTONLY_MANIFEST nullptr +#define XPTONLY_XPT nullptr +#endif + using namespace mozilla; @@ -64,36 +76,43 @@ struct ManifestDirective (nsChromeRegistry::ManifestProcessingContext& cx, int lineno, char *const *argv, bool platform, bool contentaccessible); +#ifdef MOZ_B2G_LOADER + // The function to handle the directive for XPT Only parsing. + void (*xptonlyfunc)(nsComponentManagerImpl::XPTOnlyManifestProcessingContext& cx, + int lineno, char *const * argv); +#else + void *xptonlyfunc; +#endif bool isContract; }; static const ManifestDirective kParsingTable[] = { { "manifest", 1, false, true, true, false, - &nsComponentManagerImpl::ManifestManifest, nullptr }, + &nsComponentManagerImpl::ManifestManifest, nullptr, XPTONLY_MANIFEST }, { "binary-component", 1, true, false, false, false, - &nsComponentManagerImpl::ManifestBinaryComponent, nullptr }, + &nsComponentManagerImpl::ManifestBinaryComponent, nullptr, nullptr }, { "interfaces", 1, true, false, false, false, - &nsComponentManagerImpl::ManifestXPT, nullptr }, + &nsComponentManagerImpl::ManifestXPT, nullptr, XPTONLY_XPT }, { "component", 2, true, false, false, false, - &nsComponentManagerImpl::ManifestComponent, nullptr }, + &nsComponentManagerImpl::ManifestComponent, nullptr, nullptr }, { "contract", 2, true, false, false, false, - &nsComponentManagerImpl::ManifestContract, nullptr, true}, + &nsComponentManagerImpl::ManifestContract, nullptr, nullptr, true}, { "category", 3, true, false, false, false, - &nsComponentManagerImpl::ManifestCategory, nullptr }, + &nsComponentManagerImpl::ManifestCategory, nullptr, nullptr }, { "content", 2, true, true, true, true, - nullptr, &nsChromeRegistry::ManifestContent }, + nullptr, &nsChromeRegistry::ManifestContent, nullptr }, { "locale", 3, true, true, true, false, - nullptr, &nsChromeRegistry::ManifestLocale }, + nullptr, &nsChromeRegistry::ManifestLocale, nullptr }, { "skin", 3, false, true, true, false, - nullptr, &nsChromeRegistry::ManifestSkin }, + nullptr, &nsChromeRegistry::ManifestSkin, nullptr }, { "overlay", 2, true, true, false, false, - nullptr, &nsChromeRegistry::ManifestOverlay }, + nullptr, &nsChromeRegistry::ManifestOverlay, nullptr }, { "style", 2, false, true, false, false, - nullptr, &nsChromeRegistry::ManifestStyle }, + nullptr, &nsChromeRegistry::ManifestStyle, nullptr }, { "override", 2, true, true, true, false, - nullptr, &nsChromeRegistry::ManifestOverride }, + nullptr, &nsChromeRegistry::ManifestOverride, nullptr }, { "resource", 2, true, true, false, false, - nullptr, &nsChromeRegistry::ManifestResource } + nullptr, &nsChromeRegistry::ManifestResource, nullptr } }; static const char kWhitespace[] = "\t "; @@ -126,8 +145,16 @@ struct AutoPR_smprintf_free } // anonymous namespace +/** + * If we are pre-loading XPTs, this method may do nothing because the + * console service is not initialized. + */ void LogMessage(const char* aMsg, ...) { + if (!nsComponentManagerImpl::gComponentManager) { + return; + } + nsCOMPtr console = do_GetService(NS_CONSOLESERVICE_CONTRACTID); if (!console) @@ -143,6 +170,10 @@ void LogMessage(const char* aMsg, ...) console->LogMessage(error); } +/** + * If we are pre-loading XPTs, this method may do nothing because the + * console service is not initialized. + */ void LogMessageWithContext(FileLocation &aFile, uint32_t aLineNumber, const char* aMsg, ...) { @@ -153,6 +184,10 @@ void LogMessageWithContext(FileLocation &aFile, if (!formatted) return; + if (!nsComponentManagerImpl::gComponentManager) { + return; + } + nsCString file; aFile.GetURIString(file); @@ -388,11 +423,23 @@ struct CachedDirective } // anonymous namespace +/** + * For XPT-Only mode, the parser handles only directives of "manifest" + * and "interfaces", and always call the function given by |xptonlyfunc| + * variable of struct |ManifestDirective|. + * + * This function is safe to be called before the component manager is + * ready if aXPTOnly is true for it don't invoke any component during + * parsing. + */ void -ParseManifest(NSLocationType type, FileLocation &file, char* buf, bool aChromeOnly) +ParseManifest(NSLocationType type, FileLocation &file, char* buf, bool aChromeOnly, bool aXPTOnly) { nsComponentManagerImpl::ManifestProcessingContext mgrcx(type, file, aChromeOnly); nsChromeRegistry::ManifestProcessingContext chromecx(type, file); +#ifdef MOZ_B2G_LOADER + nsComponentManagerImpl::XPTOnlyManifestProcessingContext xptonlycx(file); +#endif nsresult rv; NS_NAMED_LITERAL_STRING(kPlatform, "platform"); @@ -416,7 +463,12 @@ ParseManifest(NSLocationType type, FileLocation &file, char* buf, bool aChromeOn nsAutoString osTarget; nsAutoString abi; - nsCOMPtr xapp (do_GetService(XULAPPINFO_SERVICE_CONTRACTID)); + nsCOMPtr xapp; + if (!aXPTOnly) { + // Avoid to create any component for XPT only mode. + // No xapp means no ID, version, ..., modifiers checking. + xapp = do_GetService(XULAPPINFO_SERVICE_CONTRACTID); + } if (xapp) { nsAutoCString s; rv = xapp->GetID(s); @@ -516,9 +568,10 @@ ParseManifest(NSLocationType type, FileLocation &file, char* buf, bool aChromeOn for (const ManifestDirective* d = kParsingTable; d < ArrayEnd(kParsingTable); ++d) { - if (!strcmp(d->directive, token)) { - directive = d; - break; + if (!strcmp(d->directive, token) && + (!aXPTOnly || d->xptonlyfunc)) { + directive = d; + break; } } @@ -577,8 +630,9 @@ ParseManifest(NSLocationType type, FileLocation &file, char* buf, bool aChromeOn CheckStringFlag(kABI, wtoken, abi, stABI) || CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) || CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) || - CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion, stGeckoVersion)) + CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion, stGeckoVersion)) { continue; + } #if defined(MOZ_WIDGET_ANDROID) bool tablet = false; @@ -619,6 +673,11 @@ ParseManifest(NSLocationType type, FileLocation &file, char* buf, bool aChromeOn stABI == eBad) continue; +#ifdef MOZ_B2G_LOADER + if (aXPTOnly) { + directive->xptonlyfunc(xptonlycx, line, argv); + } else +#endif /* MOZ_B2G_LOADER */ if (directive->regfunc) { if (GeckoProcessType_Default != XRE_GetProcessType()) continue; @@ -636,7 +695,7 @@ ParseManifest(NSLocationType type, FileLocation &file, char* buf, bool aChromeOn (nsChromeRegistry::gChromeRegistry->*(directive->regfunc)) (chromecx, line, argv, platform, contentAccessible); } - else if (directive->ischrome || !aChromeOnly) { + else if (directive->mgrfunc && (directive->ischrome || !aChromeOnly)) { if (directive->isContract) { CachedDirective* cd = contracts.AppendElement(); cd->lineno = line; @@ -646,6 +705,9 @@ ParseManifest(NSLocationType type, FileLocation &file, char* buf, bool aChromeOn else (nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc)) (mgrcx, line, argv); + } else { + LogMessageWithContext(file, line, + "No valid manifest directive."); } } diff --git a/xpcom/components/ManifestParser.h b/xpcom/components/ManifestParser.h index 5dc98602bdf6..61f70182deef 100644 --- a/xpcom/components/ManifestParser.h +++ b/xpcom/components/ManifestParser.h @@ -13,7 +13,7 @@ class nsIFile; void ParseManifest(NSLocationType type, mozilla::FileLocation &file, - char* buf, bool aChromeOnly); + char* buf, bool aChromeOnly, bool aXPTOnly=false); void LogMessage(const char* aMsg, ...); diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp index 8a5c0955ebc2..61f670636eed 100644 --- a/xpcom/components/nsComponentManager.cpp +++ b/xpcom/components/nsComponentManager.cpp @@ -69,6 +69,7 @@ #include "nsStringEnumerator.h" #include "mozilla/FileUtils.h" #include "nsNetUtil.h" +#include "nsDataHashtable.h" #include // for placement new @@ -104,6 +105,36 @@ NS_DEFINE_CID(kCategoryManagerCID, NS_CATEGORYMANAGER_CID); #define UID_STRING_LENGTH 39 +#ifdef MOZ_B2G_LOADER +typedef nsDataHashtable XPTIInfosBookType; +static XPTIInfosBookType *sXPTIInfosBook = nullptr; + +static XPTIInfosBookType * +GetXPTIInfosBook() +{ + if (sXPTIInfosBook == nullptr) { + sXPTIInfosBook = new XPTIInfosBookType; + } + return sXPTIInfosBook; +} + +static bool +IsRegisteredXPTIInfo(FileLocation &aFile) +{ + nsAutoCString uri; + aFile.GetURIString(uri); + return GetXPTIInfosBook()->Get(uri); +} + +static void +MarkRegisteredXPTIInfo(FileLocation &aFile) +{ + nsAutoCString uri; + aFile.GetURIString(uri); + GetXPTIInfosBook()->Put(uri, true); +} +#endif /* MOZ_B2G_LOADER */ + nsresult nsGetServiceFromCategory::operator()(const nsIID& aIID, void** aInstancePtr) const { @@ -524,11 +555,14 @@ CutExtension(nsCString& path) path.Cut(0, dotPos + 1); } -void -nsComponentManagerImpl::RegisterManifest(NSLocationType aType, - FileLocation &aFile, - bool aChromeOnly) +static void +DoRegisterManifest(NSLocationType aType, + FileLocation &aFile, + bool aChromeOnly, + bool aXPTOnly) { + MOZ_ASSERT(!aXPTOnly || + nsComponentManagerImpl::gComponentManager == nullptr); uint32_t len; FileLocation::Data data; nsAutoArrayPtr buf; @@ -542,7 +576,7 @@ nsComponentManagerImpl::RegisterManifest(NSLocationType aType, } if (NS_SUCCEEDED(rv)) { buf[len] = '\0'; - ParseManifest(aType, aFile, buf, aChromeOnly); + ParseManifest(aType, aFile, buf, aChromeOnly, aXPTOnly); } else if (NS_BOOTSTRAPPED_LOCATION != aType) { nsCString uri; aFile.GetURIString(uri); @@ -550,6 +584,14 @@ nsComponentManagerImpl::RegisterManifest(NSLocationType aType, } } +void +nsComponentManagerImpl::RegisterManifest(NSLocationType aType, + FileLocation &aFile, + bool aChromeOnly) +{ + DoRegisterManifest(aType, aFile, aChromeOnly, false); +} + void nsComponentManagerImpl::ManifestManifest(ManifestProcessingContext& cx, int lineno, char *const * argv) { @@ -587,14 +629,19 @@ nsComponentManagerImpl::ManifestBinaryComponent(ManifestProcessingContext& cx, i RegisterModule(m, &f); } -void -nsComponentManagerImpl::ManifestXPT(ManifestProcessingContext& cx, int lineno, char *const * argv) +static void +DoRegisterXPT(FileLocation &aFile) { - FileLocation f(cx.mFile, argv[0]); +#ifdef MOZ_B2G_LOADER + if (IsRegisteredXPTIInfo(aFile)) { + return; + } +#endif + uint32_t len; FileLocation::Data data; nsAutoArrayPtr buf; - nsresult rv = f.GetData(data); + nsresult rv = aFile.GetData(data); if (NS_SUCCEEDED(rv)) { rv = data.GetSize(&len); } @@ -604,13 +651,23 @@ nsComponentManagerImpl::ManifestXPT(ManifestProcessingContext& cx, int lineno, c } if (NS_SUCCEEDED(rv)) { XPTInterfaceInfoManager::GetSingleton()->RegisterBuffer(buf, len); +#ifdef MOZ_B2G_LOADER + MarkRegisteredXPTIInfo(aFile); +#endif } else { nsCString uri; - f.GetURIString(uri); + aFile.GetURIString(uri); LogMessage("Could not read '%s'.", uri.get()); } } +void +nsComponentManagerImpl::ManifestXPT(ManifestProcessingContext& cx, int lineno, char *const * argv) +{ + FileLocation f(cx.mFile, argv[0]); + DoRegisterXPT(f); +} + void nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& cx, int lineno, char *const * argv) { @@ -794,6 +851,10 @@ nsresult nsComponentManagerImpl::Shutdown(void) delete sStaticModules; delete sModuleLocations; +#ifdef MOZ_B2G_LOADER + delete sXPTIInfosBook; + sXPTIInfosBook = nullptr; +#endif // Unload libraries mNativeModuleLoader.UnloadLibraries(); @@ -1942,6 +2003,54 @@ nsComponentManagerImpl::GetManifestLocations(nsIArray **aLocations) return NS_OK; } +#ifdef MOZ_B2G_LOADER + +/* static */ +void +nsComponentManagerImpl::XPTOnlyManifestManifest(XPTOnlyManifestProcessingContext &aCx, + int aLineno, + char * const * aArgv) +{ + char* file = aArgv[0]; + FileLocation f(aCx.mFile, file); + + DoRegisterManifest(NS_COMPONENT_LOCATION, f, false, true); +} + +/* static */ +void +nsComponentManagerImpl::XPTOnlyManifestXPT(XPTOnlyManifestProcessingContext &aCx, + int aLineno, + char * const * aArgv) +{ + FileLocation f(aCx.mFile, aArgv[0]); + DoRegisterXPT(f); +} + +/** + * To load XPT Interface Information before the component manager is ready. + * + * With this function, B2G loader could XPT interface info. as earier + * as possible to gain benefit of shared memory model of the kernel. + */ +/* static */ void +nsComponentManagerImpl::PreloadXPT(nsIFile *aFile) +{ + MOZ_ASSERT(nsComponentManagerImpl::gComponentManager == nullptr); + FileLocation location(aFile, "chrome.manifest"); + + DoRegisterManifest(NS_COMPONENT_LOCATION, location, + false, true /* aXPTOnly */); +} + +void +PreloadXPT(nsIFile *aOmnijarFile) +{ + nsComponentManagerImpl::PreloadXPT(aOmnijarFile); +} + +#endif /* MOZ_B2G_LOADER */ + EXPORT_XPCOM_API(nsresult) XRE_AddManifestLocation(NSLocationType aType, nsIFile* aLocation) { diff --git a/xpcom/components/nsComponentManager.h b/xpcom/components/nsComponentManager.h index 7eb903f0d1eb..e0d3d159687a 100644 --- a/xpcom/components/nsComponentManager.h +++ b/xpcom/components/nsComponentManager.h @@ -38,6 +38,10 @@ #include "mozilla/Omnijar.h" #include "mozilla/Attributes.h" +#ifdef MOZ_B2G_LOADER +#include "mozilla/FileLocation.h" +#endif + struct nsFactoryEntry; class nsIServiceManager; struct PRThread; @@ -316,6 +320,30 @@ class nsComponentManagerImpl MOZ_FINAL size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); +#ifdef MOZ_B2G_LOADER + // Preload XPT interface info for B2G loader. + // This function is called before XPCOM has been initialized. + static void PreloadXPT(nsIFile *aFile); +#endif + +#ifdef MOZ_B2G_LOADER + // Parsing functions of directives of manifest for XPT only parsing. + struct XPTOnlyManifestProcessingContext + { + XPTOnlyManifestProcessingContext(mozilla::FileLocation &aFile) + : mFile(aFile) + { } + + ~XPTOnlyManifestProcessingContext() { } + + mozilla::FileLocation mFile; + }; + static void XPTOnlyManifestManifest(XPTOnlyManifestProcessingContext& aCx, + int aLineno, char * const *aArgv); + static void XPTOnlyManifestXPT(XPTOnlyManifestProcessingContext& aCx, + int aLineno, char * const *aArgv); +#endif + private: ~nsComponentManagerImpl(); }; diff --git a/xpcom/threads/BackgroundHangMonitor.cpp b/xpcom/threads/BackgroundHangMonitor.cpp index 7f4328b41573..93b38f50f547 100644 --- a/xpcom/threads/BackgroundHangMonitor.cpp +++ b/xpcom/threads/BackgroundHangMonitor.cpp @@ -65,6 +65,7 @@ class BackgroundHangManager public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BackgroundHangManager) static StaticRefPtr sInstance; + static bool sProhibited; // Lock for access to members of this class Monitor mLock; @@ -162,6 +163,7 @@ class BackgroundHangThread : public LinkedListElement StaticRefPtr BackgroundHangManager::sInstance; +bool BackgroundHangManager::sProhibited = false; ThreadLocal BackgroundHangThread::sTlsKey; @@ -409,8 +411,16 @@ BackgroundHangThread* BackgroundHangThread::FindThread() { #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR + if (BackgroundHangManager::sInstance == nullptr) { + MOZ_ASSERT(BackgroundHangManager::sProhibited, + "BackgroundHandleManager is not initialized"); + return nullptr; + } + if (sTlsKey.initialized()) { // Use TLS if available + MOZ_ASSERT(!BackgroundHangManager::sProhibited, + "BackgroundHandleManager is not initialized"); return sTlsKey.get(); } // If TLS is unavailable, we can search through the thread list @@ -436,6 +446,7 @@ void BackgroundHangMonitor::Startup() { #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR + MOZ_ASSERT(!BackgroundHangManager::sProhibited, "Prohibited"); MOZ_ASSERT(!BackgroundHangManager::sInstance, "Already initialized"); ThreadStackHelper::Startup(); BackgroundHangThread::Startup(); @@ -447,6 +458,7 @@ void BackgroundHangMonitor::Shutdown() { #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR + MOZ_ASSERT(!BackgroundHangManager::sProhibited, "Prohibited"); MOZ_ASSERT(BackgroundHangManager::sInstance, "Not initialized"); /* Scope our lock inside Shutdown() because the sInstance object can be destroyed as soon as we set sInstance to nullptr below, and @@ -463,7 +475,8 @@ BackgroundHangMonitor::BackgroundHangMonitor(const char* aName, : mThread(BackgroundHangThread::FindThread()) { #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR - if (!mThread) { + if (!BackgroundHangManager::sProhibited && !mThread) { + // If sProhibit is true, mThread would be null, and no monitoring. mThread = new BackgroundHangThread(aName, aTimeoutMs, aMaxTimeoutMs); } #endif @@ -473,7 +486,8 @@ BackgroundHangMonitor::BackgroundHangMonitor() : mThread(BackgroundHangThread::FindThread()) { #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR - MOZ_ASSERT(mThread, "Thread not initialized for hang monitoring"); + MOZ_ASSERT(!BackgroundHangManager::sProhibited || mThread, + "This thread is not initialized for hang monitoring"); #endif } @@ -485,6 +499,11 @@ void BackgroundHangMonitor::NotifyActivity() { #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR + if (mThread == nullptr) { + MOZ_ASSERT(BackgroundHangManager::sProhibited, + "This thread is not initialized for hang monitoring"); + return; + } mThread->NotifyActivity(); #endif } @@ -493,18 +512,49 @@ void BackgroundHangMonitor::NotifyWait() { #ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR + if (mThread == nullptr) { + MOZ_ASSERT(BackgroundHangManager::sProhibited, + "This thread is not initialized for hang monitoring"); + return; + } mThread->NotifyWait(); #endif } +void +BackgroundHangMonitor::Prohibit() +{ +#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR + MOZ_ASSERT(BackgroundHangManager::sInstance == nullptr, + "The background hang monitor is already initialized"); + BackgroundHangManager::sProhibited = true; +#endif +} + +void +BackgroundHangMonitor::Allow() +{ +#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR + MOZ_ASSERT(BackgroundHangManager::sInstance == nullptr, + "The background hang monitor is already initialized"); + BackgroundHangManager::sProhibited = false; +#endif +} + /* Because we are iterating through the BackgroundHangThread linked list, we need to take a lock. Using MonitorAutoLock as a base class makes sure all of that is taken care of for us. */ BackgroundHangMonitor::ThreadHangStatsIterator::ThreadHangStatsIterator() : MonitorAutoLock(BackgroundHangManager::sInstance->mLock) - , mThread(BackgroundHangManager::sInstance->mHangThreads.getFirst()) + , mThread(BackgroundHangManager::sInstance ? + BackgroundHangManager::sInstance->mHangThreads.getFirst() : + nullptr) { +#ifdef MOZ_ENABLE_BACKGROUND_HANG_MONITOR + MOZ_ASSERT(BackgroundHangManager::sInstance || BackgroundHangManager::sProhibited, + "Inconsistent state"); +#endif } Telemetry::ThreadHangStats* diff --git a/xpcom/threads/BackgroundHangMonitor.h b/xpcom/threads/BackgroundHangMonitor.h index 76d517fce4dd..059fb0a08ff9 100644 --- a/xpcom/threads/BackgroundHangMonitor.h +++ b/xpcom/threads/BackgroundHangMonitor.h @@ -107,6 +107,8 @@ class BackgroundHangThread; * } * } * + * Prohibit() and Allow() make the background hang monitor work safely + * before Startup(). */ class BackgroundHangMonitor { @@ -204,6 +206,27 @@ class BackgroundHangMonitor * NotifyActivity when subsequently exiting the wait state. */ void NotifyWait(); + + /** + * Prohibit the hang monitor from activating. + * + * Startup() should not be called between Prohibit() and Allow(). + * This function makes the background hang monitor stop monitoring + * threads. + * + * Prohibit() and Allow() can be called before XPCOM is ready. If + * we don't stop monitoring threads it could case errors. + */ + static void Prohibit(); + + /** + * Allow the hang monitor to run. + * + * Allow() and Prohibit() should be called in pair. + * + * \see Prohibit() + */ + static void Allow(); }; } // namespace mozilla