requestMtu method

Future<int> requestMtu(
  1. int desiredMtu, {
  2. double predelay = 0.35,
  3. int timeout = 15,
})

Request to change MTU (Android Only)

  • returns new MTU
  • predelay adds delay to avoid race conditions on some devices. see comments below.

Implementation

Future<int> requestMtu(int desiredMtu, {double predelay = 0.35, int timeout = 15}) async {
  // check android
  if (Platform.isAndroid == false) {
    throw FlutterBluePlusException(ErrorPlatform.fbp, "requestMtu", FbpErrorCode.androidOnly.index, "android-only");
  }

  // check connected
  if (isDisconnected) {
    throw FlutterBluePlusException(
        ErrorPlatform.fbp, "requestMtu", FbpErrorCode.deviceIsDisconnected.index, "device is not connected");
  }

  // Only allow a single ble operation to be underway at a time
  _Mutex mtx = _MutexFactory.getMutexForKey("global");
  await mtx.take();

  // predelay
  if (predelay > 0) {
    // hack: By adding delay before we call `requestMtu`, we can avoid
    // a race condition that can cause `discoverServices` to timeout or fail.
    //
    // Note: This hack is only needed for devices that automatically send an
    // MTU update right after connection. If your device does not do that,
    // you can set this delay to zero. Other people may need to increase it!
    //
    // The race condition goes like this:
    //  1. you call `requestMtu` right after connection
    //  2. some devices automatically send a new MTU right after connection, without being asked
    //  3. your call to `requestMtu` confuses the results from step 1 and step 2, and returns to early
    //  4. the user then calls `discoverServices`, thinking that `requestMtu` has finished
    //  5. in reality, `requestMtu` is still happening, and the call to `discoverServices` will fail/timeout
    //
    // Adding delay before we call `requestMtu` helps ensure
    // that the automatic mtu update has already happened.
    await Future.delayed(Duration(milliseconds: (predelay * 1000).toInt()));
  }

  var mtu = 0;

  try {
    var request = BmMtuChangeRequest(
      remoteId: remoteId,
      mtu: desiredMtu,
    );

    var responseStream = FlutterBluePlus._methodStream.stream
        .where((m) => m.method == "OnMtuChanged")
        .map((m) => m.arguments)
        .map((args) => BmMtuChangedResponse.fromMap(args))
        .where((p) => p.remoteId == remoteId)
        .map((p) => p.mtu);

    // Start listening now, before invokeMethod, to ensure we don't miss the response
    Future<int> futureResponse = responseStream.first;

    // invoke
    await FlutterBluePlus._invokeMethod('requestMtu', request.toMap());

    // wait for response
    mtu = await futureResponse
        .fbpEnsureAdapterIsOn("requestMtu")
        .fbpEnsureDeviceIsConnected(this, "requestMtu")
        .fbpTimeout(timeout, "requestMtu");
  } finally {
    mtx.give();
  }

  return mtu;
}