dom/bluetooth/tests/marionette/head.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  * vim: sw=2 ts=2 sts=2 et filetype=javascript
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 // https://github.com/mozilla-b2g/platform_external_qemu/blob/master/vl-android.c#L765
     8 // static int bt_hci_parse(const char *str) {
     9 //   ...
    10 //   bdaddr.b[0] = 0x52;
    11 //   bdaddr.b[1] = 0x54;
    12 //   bdaddr.b[2] = 0x00;
    13 //   bdaddr.b[3] = 0x12;
    14 //   bdaddr.b[4] = 0x34;
    15 //   bdaddr.b[5] = 0x56 + nb_hcis;
    16 const EMULATOR_ADDRESS = "56:34:12:00:54:52";
    18 // $ adb shell hciconfig /dev/ttyS2 name
    19 // hci0:  Type: BR/EDR  Bus: UART
    20 //        BD Address: 56:34:12:00:54:52  ACL MTU: 512:1  SCO MTU: 0:0
    21 //        Name: 'Full Android on Emulator'
    22 const EMULATOR_NAME = "Full Android on Emulator";
    24 // $ adb shell hciconfig /dev/ttyS2 class
    25 // hci0:  Type: BR/EDR  Bus: UART
    26 //        BD Address: 56:34:12:00:54:52  ACL MTU: 512:1  SCO MTU: 0:0
    27 //        Class: 0x58020c
    28 //        Service Classes: Capturing, Object Transfer, Telephony
    29 //        Device Class: Phone, Smart phone
    30 const EMULATOR_CLASS = 0x58020c;
    32 // Use same definition in QEMU for special bluetooth address,
    33 // which were defined at external/qemu/hw/bt.h:
    34 const BDADDR_ANY   = "00:00:00:00:00:00";
    35 const BDADDR_ALL   = "ff:ff:ff:ff:ff:ff";
    36 const BDADDR_LOCAL = "ff:ff:ff:00:00:00";
    38 // A user friendly name for remote BT device.
    39 const REMOTE_DEVICE_NAME = "Remote BT Device";
    41 let Promise =
    42   SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
    44 let bluetoothManager;
    46 let pendingEmulatorCmdCount = 0;
    48 /**
    49  * Send emulator command with safe guard.
    50  *
    51  * We should only call |finish()| after all emulator command transactions
    52  * end, so here comes with the pending counter.  Resolve when the emulator
    53  * gives positive response, and reject otherwise.
    54  *
    55  * Fulfill params:
    56  *   result -- an array of emulator response lines.
    57  *
    58  * Reject params:
    59  *   result -- an array of emulator response lines.
    60  *
    61  * @return A deferred promise.
    62  */
    63 function runEmulatorCmdSafe(aCommand) {
    64   let deferred = Promise.defer();
    66   ++pendingEmulatorCmdCount;
    67   runEmulatorCmd(aCommand, function(aResult) {
    68     --pendingEmulatorCmdCount;
    70     ok(true, "Emulator response: " + JSON.stringify(aResult));
    71     if (Array.isArray(aResult) && aResult[aResult.length - 1] === "OK") {
    72       deferred.resolve(aResult);
    73     } else {
    74       ok(false, "Got an abnormal response from emulator.");
    75       log("Fail to execute emulator command: [" + aCommand + "]");
    76       deferred.reject(aResult);
    77     }
    78   });
    80   return deferred.promise;
    81 }
    83 /**
    84  * Add a Bluetooth remote device to scatternet and set its properties.
    85  *
    86  * Use QEMU command 'bt remote add' to add a virtual Bluetooth remote
    87  * and set its properties by setEmulatorDeviceProperty().
    88  *
    89  * Fulfill params:
    90  *   result -- bluetooth address of the remote device.
    91  * Reject params: (none)
    92  *
    93  * @param aProperies
    94  *        A javascript object with zero or several properties for initializing
    95  *        the remote device. By now, the properies could be 'name' or
    96  *        'discoverable'. It valid to put a null object or a javascript object
    97  *        which don't have any properies.
    98  *
    99  * @return A promise object.
   100  */
   101 function addEmulatorRemoteDevice(aProperties) {
   102   let address;
   103   let promise = runEmulatorCmdSafe("bt remote add")
   104     .then(function(aResults) {
   105       address = aResults[0].toUpperCase();
   106     });
   108   for (let key in aProperties) {
   109     let value = aProperties[key];
   110     let propertyName = key;
   111     promise = promise.then(function() {
   112       return setEmulatorDeviceProperty(address, propertyName, value);
   113     });
   114   }
   116   return promise.then(function() {
   117     return address;
   118   });
   119 }
   121 /**
   122  * Remove Bluetooth remote devices in scatternet.
   123  *
   124  * Use QEMU command 'bt remote remove <addr>' to remove a specific virtual
   125  * Bluetooth remote device in scatternet or remove them all by  QEMU command
   126  * 'bt remote remove BDADDR_ALL'.
   127  *
   128  * @param aAddress
   129  *        The string of Bluetooth address with format xx:xx:xx:xx:xx:xx.
   130  *
   131  * Fulfill params:
   132  *   result -- an array of emulator response lines.
   133  * Reject params: (none)
   134  *
   135  * @return A promise object.
   136  */
   137 function removeEmulatorRemoteDevice(aAddress) {
   138   let cmd = "bt remote remove " + aAddress;
   139   return runEmulatorCmdSafe(cmd)
   140     .then(function(aResults) {
   141       // 'bt remote remove <bd_addr>' returns a list of removed device one at a line.
   142       // The last item is "OK".
   143       return aResults.slice(0, -1);
   144     });
   145 }
   147 /**
   148  * Set a property for a Bluetooth device.
   149  *
   150  * Use QEMU command 'bt property <bd_addr> <prop_name> <value>' to set property.
   151  *
   152  * Fulfill params:
   153  *   result -- an array of emulator response lines.
   154  * Reject params:
   155  *   result -- an array of emulator response lines.
   156  *
   157  * @param aAddress
   158  *        The string of Bluetooth address with format xx:xx:xx:xx:xx:xx.
   159  * @param aPropertyName
   160  *        The property name of Bluetooth device.
   161  * @param aValue
   162  *        The new value of the specifc property.
   163  *
   164  * @return A deferred promise.
   165  */
   166 function setEmulatorDeviceProperty(aAddress, aPropertyName, aValue) {
   167   let cmd = "bt property " + aAddress + " " + aPropertyName + " " + aValue;
   168   return runEmulatorCmdSafe(cmd);
   169 }
   171 /**
   172  * Get a property from a Bluetooth device.
   173  *
   174  * Use QEMU command 'bt property <bd_addr> <prop_name>' to get properties.
   175  *
   176  * Fulfill params:
   177  *   result -- a string with format <prop_name>: <value_of_prop>
   178  * Reject params:
   179  *   result -- an array of emulator response lines.
   180  *
   181  * @param aAddress
   182  *        The string of Bluetooth address with format xx:xx:xx:xx:xx:xx.
   183  * @param aPropertyName
   184  *        The property name of Bluetooth device.
   185  *
   186  * @return A deferred promise.
   187  */
   188 function getEmulatorDeviceProperty(aAddress, aPropertyName) {
   189   let cmd = "bt property " + aAddress + " " + aPropertyName;
   190   return runEmulatorCmdSafe(cmd)
   191     .then(function(aResults) {
   192       return aResults[0];
   193     });
   194 }
   196 /**
   197  * Start dicovering Bluetooth devices.
   198  *
   199  * Allows the device's adapter to start seeking for remote devices.
   200  *
   201  * Fulfill params: (none)
   202  * Reject params: a DOMError
   203  *
   204  * @param aAdapter
   205  *        A BluetoothAdapter which is used to interact with local BT dev
   206  *
   207  * @return A deferred promise.
   208  */
   209 function startDiscovery(aAdapter) {
   210   let deferred = Promise.defer();
   212   let request = aAdapter.startDiscovery();
   213   request.onsuccess = function () {
   214     log("  Start discovery - Success");
   215     // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
   216     //     Currently, discovering state wouldn't change immediately here.
   217     //     We would turn on this check when the redesigned API are landed.
   218     // is(aAdapter.discovering, true, "BluetoothAdapter.discovering");
   219     deferred.resolve();
   220   }
   221   request.onerror = function (aEvent) {
   222     ok(false, "Start discovery - Fail");
   223     deferred.reject(aEvent.target.error);
   224   }
   226   return deferred.promise;
   227 }
   229 /**
   230  * Stop dicovering Bluetooth devices.
   231  *
   232  * Allows the device's adapter to stop seeking for remote devices.
   233  *
   234  * Fulfill params: (none)
   235  * Reject params: a DOMError
   236  *
   237  * @param aAdapter
   238  *        A BluetoothAdapter which is used to interact with local BT device.
   239  *
   240  * @return A deferred promise.
   241  */
   242 function stopDiscovery(aAdapter) {
   243   let deferred = Promise.defer();
   245   let request = aAdapter.stopDiscovery();
   246   request.onsuccess = function () {
   247     log("  Stop discovery - Success");
   248     // TODO (bug 892207): Make Bluetooth APIs available for 3rd party apps.
   249     //     Currently, discovering state wouldn't change immediately here.
   250     //     We would turn on this check when the redesigned API are landed.
   251     // is(aAdapter.discovering, false, "BluetoothAdapter.discovering");
   252     deferred.resolve();
   253   }
   254   request.onerror = function (aEvent) {
   255     ok(false, "Stop discovery - Fail");
   256     deferred.reject(aEvent.target.error);
   257   }
   258   return deferred.promise;
   259 }
   261 /**
   262  * Get mozSettings value specified by @aKey.
   263  *
   264  * Resolve if that mozSettings value is retrieved successfully, reject
   265  * otherwise.
   266  *
   267  * Fulfill params:
   268  *   The corresponding mozSettings value of the key.
   269  * Reject params: (none)
   270  *
   271  * @param aKey
   272  *        A string.
   273  *
   274  * @return A deferred promise.
   275  */
   276 function getSettings(aKey) {
   277   let deferred = Promise.defer();
   279   let request = navigator.mozSettings.createLock().get(aKey);
   280   request.addEventListener("success", function(aEvent) {
   281     ok(true, "getSettings(" + aKey + ")");
   282     deferred.resolve(aEvent.target.result[aKey]);
   283   });
   284   request.addEventListener("error", function() {
   285     ok(false, "getSettings(" + aKey + ")");
   286     deferred.reject();
   287   });
   289   return deferred.promise;
   290 }
   292 /**
   293  * Set mozSettings values.
   294  *
   295  * Resolve if that mozSettings value is set successfully, reject otherwise.
   296  *
   297  * Fulfill params: (none)
   298  * Reject params: (none)
   299  *
   300  * @param aSettings
   301  *        An object of format |{key1: value1, key2: value2, ...}|.
   302  *
   303  * @return A deferred promise.
   304  */
   305 function setSettings(aSettings) {
   306   let deferred = Promise.defer();
   308   let request = navigator.mozSettings.createLock().set(aSettings);
   309   request.addEventListener("success", function() {
   310     ok(true, "setSettings(" + JSON.stringify(aSettings) + ")");
   311     deferred.resolve();
   312   });
   313   request.addEventListener("error", function() {
   314     ok(false, "setSettings(" + JSON.stringify(aSettings) + ")");
   315     deferred.reject();
   316   });
   318   return deferred.promise;
   319 }
   321 /**
   322  * Get mozSettings value of 'bluetooth.enabled'.
   323  *
   324  * Resolve if that mozSettings value is retrieved successfully, reject
   325  * otherwise.
   326  *
   327  * Fulfill params:
   328  *   A boolean value.
   329  * Reject params: (none)
   330  *
   331  * @return A deferred promise.
   332  */
   333 function getBluetoothEnabled() {
   334   return getSettings("bluetooth.enabled");
   335 }
   337 /**
   338  * Set mozSettings value of 'bluetooth.enabled'.
   339  *
   340  * Resolve if that mozSettings value is set successfully, reject otherwise.
   341  *
   342  * Fulfill params: (none)
   343  * Reject params: (none)
   344  *
   345  * @param aEnabled
   346  *        A boolean value.
   347  *
   348  * @return A deferred promise.
   349  */
   350 function setBluetoothEnabled(aEnabled) {
   351   let obj = {};
   352   obj["bluetooth.enabled"] = aEnabled;
   353   return setSettings(obj);
   354 }
   356 /**
   357  * Push required permissions and test if |navigator.mozBluetooth| exists.
   358  * Resolve if it does, reject otherwise.
   359  *
   360  * Fulfill params:
   361  *   bluetoothManager -- an reference to navigator.mozBluetooth.
   362  * Reject params: (none)
   363  *
   364  * @param aPermissions
   365  *        Additional permissions to push before any test cases.  Could be either
   366  *        a string or an array of strings.
   367  *
   368  * @return A deferred promise.
   369  */
   370 function ensureBluetoothManager(aPermissions) {
   371   let deferred = Promise.defer();
   373   let permissions = ["bluetooth"];
   374   if (aPermissions) {
   375     if (Array.isArray(aPermissions)) {
   376       permissions = permissions.concat(aPermissions);
   377     } else if (typeof aPermissions == "string") {
   378       permissions.push(aPermissions);
   379     }
   380   }
   382   let obj = [];
   383   for (let perm of permissions) {
   384     obj.push({
   385       "type": perm,
   386       "allow": 1,
   387       "context": document,
   388     });
   389   }
   391   SpecialPowers.pushPermissions(obj, function() {
   392     ok(true, "permissions pushed: " + JSON.stringify(permissions));
   394     bluetoothManager = window.navigator.mozBluetooth;
   395     log("navigator.mozBluetooth is " +
   396         (bluetoothManager ? "available" : "unavailable"));
   398     if (bluetoothManager instanceof BluetoothManager) {
   399       deferred.resolve(bluetoothManager);
   400     } else {
   401       deferred.reject();
   402     }
   403   });
   405   return deferred.promise;
   406 }
   408 /**
   409  * Wait for one named BluetoothManager event.
   410  *
   411  * Resolve if that named event occurs.  Never reject.
   412  *
   413  * Fulfill params: the DOMEvent passed.
   414  *
   415  * @param aEventName
   416  *        The name of the EventHandler.
   417  *
   418  * @return A deferred promise.
   419  */
   420 function waitForManagerEvent(aEventName) {
   421   let deferred = Promise.defer();
   423   bluetoothManager.addEventListener(aEventName, function onevent(aEvent) {
   424     bluetoothManager.removeEventListener(aEventName, onevent);
   426     ok(true, "BluetoothManager event '" + aEventName + "' got.");
   427     deferred.resolve(aEvent);
   428   });
   430   return deferred.promise;
   431 }
   433 /**
   434  * Wait for one named BluetoothAdapter event.
   435  *
   436  * Resolve if that named event occurs.  Never reject.
   437  *
   438  * Fulfill params: the DOMEvent passed.
   439  *
   440  * @param aAdapter
   441  *        The BluetoothAdapter you want to use.
   442  * @param aEventName
   443  *        The name of the EventHandler.
   444  *
   445  * @return A deferred promise.
   446  */
   447 function waitForAdapterEvent(aAdapter, aEventName) {
   448   let deferred = Promise.defer();
   450   aAdapter.addEventListener(aEventName, function onevent(aEvent) {
   451     aAdapter.removeEventListener(aEventName, onevent);
   453     ok(true, "BluetoothAdapter event '" + aEventName + "' got.");
   454     deferred.resolve(aEvent);
   455   });
   457   return deferred.promise;
   458 }
   460 /**
   461  * Convenient function for setBluetoothEnabled and waitForManagerEvent
   462  * combined.
   463  *
   464  * Resolve if that named event occurs.  Reject if we can't set settings.
   465  *
   466  * Fulfill params: the DOMEvent passed.
   467  * Reject params: (none)
   468  *
   469  * @return A deferred promise.
   470  */
   471 function setBluetoothEnabledAndWait(aEnabled) {
   472   let promises = [];
   474   // Bug 969109 -  Intermittent test_dom_BluetoothManager_adapteradded.js
   475   //
   476   // Here we want to wait for two events coming up -- Bluetooth "settings-set"
   477   // event and one of "enabled"/"disabled" events.  Special care is taken here
   478   // to ensure that we can always receive that "enabled"/"disabled" event by
   479   // installing the event handler *before* we ever enable/disable Bluetooth. Or
   480   // we might just miss those events and get a timeout error.
   481   promises.push(waitForManagerEvent(aEnabled ? "enabled" : "disabled"));
   482   promises.push(setBluetoothEnabled(aEnabled));
   484   return Promise.all(promises);
   485 }
   487 /**
   488  * Get default adapter.
   489  *
   490  * Resolve if that default adapter is got, reject otherwise.
   491  *
   492  * Fulfill params: a BluetoothAdapter instance.
   493  * Reject params: a DOMError, or null if if there is no adapter ready yet.
   494  *
   495  * @return A deferred promise.
   496  */
   497 function getDefaultAdapter() {
   498   let deferred = Promise.defer();
   500   let request = bluetoothManager.getDefaultAdapter();
   501   request.onsuccess = function(aEvent) {
   502     let adapter = aEvent.target.result;
   503     if (!(adapter instanceof BluetoothAdapter)) {
   504       ok(false, "no BluetoothAdapter ready yet.");
   505       deferred.reject(null);
   506       return;
   507     }
   509     ok(true, "BluetoothAdapter got.");
   510     // TODO: We have an adapter instance now, but some of its attributes may
   511     // still remain unassigned/out-dated.  Here we waste a few seconds to
   512     // wait for the property changed events.
   513     //
   514     // See https://bugzilla.mozilla.org/show_bug.cgi?id=932914
   515     window.setTimeout(function() {
   516       deferred.resolve(adapter);
   517     }, 3000);
   518   };
   519   request.onerror = function(aEvent) {
   520     ok(false, "Failed to get default adapter.");
   521     deferred.reject(aEvent.target.error);
   522   };
   524   return deferred.promise;
   525 }
   527 /**
   528  * Flush permission settings and call |finish()|.
   529  */
   530 function cleanUp() {
   531   waitFor(function() {
   532     SpecialPowers.flushPermissions(function() {
   533       // Use ok here so that we have at least one test run.
   534       ok(true, "permissions flushed");
   536       finish();
   537     });
   538   }, function() {
   539     return pendingEmulatorCmdCount === 0;
   540   });
   541 }
   543 function startBluetoothTestBase(aPermissions, aTestCaseMain) {
   544   ensureBluetoothManager(aPermissions)
   545     .then(aTestCaseMain)
   546     .then(cleanUp, function() {
   547       ok(false, "Unhandled rejected promise.");
   548       cleanUp();
   549     });
   550 }
   552 function startBluetoothTest(aReenable, aTestCaseMain) {
   553   startBluetoothTestBase(["settings-read", "settings-write"], function() {
   554     let origEnabled, needEnable;
   556     return getBluetoothEnabled()
   557       .then(function(aEnabled) {
   558         origEnabled = aEnabled;
   559         needEnable = !aEnabled;
   560         log("Original 'bluetooth.enabled' is " + origEnabled);
   562         if (aEnabled && aReenable) {
   563           log("  Disable 'bluetooth.enabled' ...");
   564           needEnable = true;
   565           return setBluetoothEnabledAndWait(false);
   566         }
   567       })
   568       .then(function() {
   569         if (needEnable) {
   570           log("  Enable 'bluetooth.enabled' ...");
   572           // See setBluetoothEnabledAndWait().  We must install all event
   573           // handlers *before* enabling Bluetooth.
   574           let promises = [];
   575           promises.push(waitForManagerEvent("adapteradded"));
   576           promises.push(setBluetoothEnabledAndWait(true));
   577           return Promise.all(promises);
   578         }
   579       })
   580       .then(getDefaultAdapter)
   581       .then(aTestCaseMain)
   582       .then(function() {
   583         if (!origEnabled) {
   584           return setBluetoothEnabledAndWait(false);
   585         }
   586       });
   587   });
   588 }

mercurial