Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS connection error when advertising #1702

Closed
IgVelasco opened this issue May 21, 2023 · 15 comments
Closed

iOS connection error when advertising #1702

IgVelasco opened this issue May 21, 2023 · 15 comments
Labels
lang: swift An issue with a Swift client library P0 Priority 0 issue (such as a build break, regression or customer blocking) platform: apple An issue with the macOS or iOS implementation project: connections An issue with the Connections project type: bug Something is broken or not working as intended

Comments

@IgVelasco
Copy link

IgVelasco commented May 21, 2023

Project

Nearby Connections

Language

Swift

OS Platform

Apple

What happened?

When using an iOS device as an advertiser, the connection fails after being accepted (via code automatically or using the iOS Example provided in the repository) due to a parsing error in the secure message.

What did you expect to happen?

I expected the connection to be accepted. When using the iOS device as a discoverer, I do not encounter any problems when connecting to an Android device that is advertising.

How can we reproduce it (as minimally and precisely as possible)?

I have run multiple scenarios, all resulting in the same output:

Devices:

iPhone running iOS 16.5
iPad running iOS 16.5
Steps:

(using cluster/star strategy)

  1. Start advertising on the iPad.
  2. Start discovering on the iPhone.
  3. The connection request is automatically sent and accepted.
  4. An error occurs, preventing the message from being sent, and the connection is rejected by the advertiser's side.

I have also tried other scenarios using the iOS device as an advertiser and tested the iOS Example provided in the repository. In all cases, the same error occurred. Occasionally, the connection was approved, but I was unable to determine the reason why.

How often does this bug happen?

Often

Standalone code to reproduce the issue

The error was able to be produce with the iOS example, let me know if you are not being able to reproduce it.

Relevant log output

<PATH>/SourcePackages/checkouts/nearby/third_party/protobuf/src/google/protobuf/message_lite.cc:134] Can't parse message of type "securemessage.SecureMessage" because it is missing required fields: (cannot determine missing fields for lite message)
[ERROR] VerifyDecryptPayload: error parsing SecureMessage.
[ERROR] DecodeMessageFromPeer: Failed to verify message.

Anything else we need to know?

  • Using a star strategy with the android as a advertiser and iOS devices with discovery worked perfectly.
  • When using breakpoints in the didReceiveConnectionRequestFrom and didReceive methods, with hardcoded values set to true, the connection process proceeds without any issues.
@IgVelasco IgVelasco added needs-triage Issue still needs to be assigned, labeled and deduplicated type: bug Something is broken or not working as intended labels May 21, 2023
@DonatoJP
Copy link

Hey!

I am also having problems related to this. But, in my case, it also happens when both devices are configured to follow a "Point to Point" strategy. It seems that it does not depend on the connection strategy selected.

Following the same scenario that @IgVelasco described above, I ALWAYS receive the "libprotobuf ERROR" on the advertiser's side.

I can also see this, just behind the relevant log described above:

[ERROR] DecodeMessageFromPeer: Failed to verify message.
2023-05-21 19:10:40.851 iOS Example[927/0x16b9cf000] [lvl=2] base_endpoint_channel.cc:158() Read: Read unencrypted KEEP_ALIVE on encrypted channel.

And, in terms of the UI from the example, the advertiser shows the buttons to "Send bytes", but the discoverer device shows the "Accept/Reject" buttons in a disabled state.

@bourdakos1 bourdakos1 added P0 Priority 0 issue (such as a build break, regression or customer blocking) platform: apple An issue with the macOS or iOS implementation lang: swift An issue with a Swift client library project: connections An issue with the Connections project and removed needs-triage Issue still needs to be assigned, labeled and deduplicated labels May 22, 2023
@bourdakos1
Copy link
Collaborator

@IgVelasco and @DonatoJP thanks for reporting! I am able to reproduce the issue and I'm looking into a fix

@bourdakos1
Copy link
Collaborator

Do you mind trying #1703 and see if that resolves the issue?

copybara-service bot pushed a commit that referenced this issue May 22, 2023
Objective-C objects cannot be captured by reference in C++ lambdas and must always be captured by value.

This also takes a copy of the data returned from the Wi-Fi LAN reader, to prevent potential intermittent empty packet issues.

Fixes: #1702
PiperOrigin-RevId: 533914865
@IgVelasco
Copy link
Author

Yes looks like that fixes the issue thanks!

@DonatoJP
Copy link

@bourdakos1 thanks a lot! This solves the issue

copybara-service bot pushed a commit that referenced this issue May 22, 2023
Objective-C objects cannot be captured by reference in C++ lambdas and must always be captured by value.

This also takes a copy of the data returned from the Wi-Fi LAN reader, to prevent potential intermittent empty packet issues.

Fixes: #1702
PiperOrigin-RevId: 533914865
copybara-service bot pushed a commit that referenced this issue May 22, 2023
Objective-C objects cannot be captured by reference in C++ lambdas and must always be captured by value.

This also takes a copy of the data returned from the Wi-Fi LAN reader, to prevent potential intermittent empty packet issues.

Fixes: #1702
PiperOrigin-RevId: 533925980
@IgVelasco
Copy link
Author

IgVelasco commented Mar 4, 2024

Hi @bourdakos1 , could it be that this is failing again? Maybe there was a change in the library

[libprotobuf ERROR /Users/Username/Library/Developer/Xcode/DerivedData/Runner-gnvxrhyhjhcjfeebrolzpfvccdjm/SourcePackages/checkouts/nearby/third_party/protobuf/src/google/protobuf/message_lite.cc:134] Can't parse message of type "securemessage.SecureMessage" because it is missing required fields: (cannot determine missing fields for lite message)
[ERROR] VerifyDecryptPayload: error parsing SecureMessage.
[ERROR] DecodeMessageFromPeer: Failed to verify message.

Pretty much the same error

@IgVelasco IgVelasco reopened this Mar 4, 2024
@bourdakos1
Copy link
Collaborator

Hmm I don’t see any recent changes that would break things, but I’ll take a closer look tomorrow and try to reproduce

@IgVelasco
Copy link
Author

Thanks, let me know if I can be of any help!

@IgVelasco
Copy link
Author

@bourdakos1 I've tried the example and is working correctly but im having a problem with my implementation were this error raises no matter what, is a simple implementation where I'm setting the connection handler and verification handler on true instantly

Do you have any idea what could be the issue?

I'll close this issue since in the IOS Example is working correctly

@bourdakos1
Copy link
Collaborator

Hmm I’m not sure. Are you able to share any of the code?

@DonatoJP
Copy link

DonatoJP commented Mar 8, 2024

Hey @bourdakos1, I am having the same issue. I am trying to connect one iOS device (iPhone 13 Pro iOS 17.4) as an advertiser with another iOS device (iPhone 15 Pro iOS 17.3.1) as a discoverer.

The problem starts when I send the connection request from the discoverer device (using discoverer.requestConnection as in the docs).

In the advertiser device I can see this:

2024-03-07 23:06:29.657 Runner[664/0x16f7f3000] [lvl=2] base_pcp_handler.cc:1571() Connection accepted on Medium:WIFI_LAN
2024-03-07 23:06:29.657 Runner[664/0x16f9a3000] [lvl=2] bwu_manager.cc:190() InitiateBwuForEndpoint for endpoint SCF2 with medium WIFI_LAN
2024-03-07 23:06:29.657 Runner[664/0x16f9a3000] [lvl=2] endpoint_channel_manager.cc:176() Found WIFI_LAN Medium for endpoint:SCF2
2024-03-07 23:06:29.657 Runner[664/0x16f9a3000] [lvl=2] bwu_manager.cc:1419() CancelRetryUpgradeAlarm for endpoint SCF2
2024-03-07 23:06:29.657 Runner[664/0x16f9a3000] [lvl=2] bwu_manager.cc:253() BwuManager ignoring the upgrade for endpoint SCF2 because it is already connected over medium WIFI_LAN

[libprotobuf ERROR /Users/Username/Library/Developer/Xcode/DerivedData/Runner-ffqbzmukvbeqzmcphtrsuhwtsrzo/SourcePackages/checkouts/nearby/third_party/protobuf/src/google/protobuf/message_lite.cc:134] Can't parse message of type "securemessage.SecureMessage" because it is missing required fields: (cannot determine missing fields for lite message)
[ERROR] VerifyDecryptPayload: error parsing SecureMessage.
[ERROR] DecodeMessageFromPeer: Failed to verify message.
2024-03-07 23:06:34.675 Runner[664/0x170837000] [lvl=2] base_endpoint_channel.cc:158() Read: Read unencrypted KEEP_ALIVE on encrypted channel.
2024-03-07 23:06:34.675 Runner[664/0x170837000] [lvl=2] endpoint_manager.cc:210() KeepAlive message for endpoint SCF2
2024-03-07 23:06:34.675 Runner[664/0x170837000] [lvl=2] endpoint_manager.cc:210()

The last 6 lines are repeated every 5 seconds. On the other hand, in my discoverer device I can see this:

2024-03-07 23:09:34.982 Runner[22052/0x16f7b3000] [lvl=2] endpoint_manager.cc:192() Failed to decode; endpoint=BXRE; channel=WIFI_LAN; skip
2024-03-07 23:09:34.982 Runner[22052/0x16f7b3000] [lvl=2] endpoint_manager.cc:192() 
2024-03-07 23:09:40.141 Runner[22052/0x16f7b3000] [lvl=2] endpoint_manager.cc:192() Failed to decode; endpoint=BXRE; channel=WIFI_LAN; skip
2024-03-07 23:09:40.141 Runner[22052/0x16f7b3000] [lvl=2] endpoint_manager.cc:192() 

Again, every 5-6 seconds. It seems they are trying to exchange some data but the library is having problems to decode those packets.

Is this example more accurate? Thanks in advance!

@IgVelasco
Copy link
Author

@bourdakos1 This is the code that im using, I also tried to use the code for the example enforcing the connection always to be accepted and failed. I also tried using the hash of the following commit and this did work. Im not sure if its something with a change in the library or something related to the direct accept

 class NCAdvertiser: Connector, AdvertiserDelegate {
     func advertiser(_ advertiser: Advertiser, didReceiveConnectionRequestFrom endpointID: EndpointID, with context: Data, connectionRequestHandler: @escaping (Bool) -> Void) {
         connectionRequestHandler(true)
         // TODO
     }
    
     var advertiser: Advertiser?
    
     init(serviceId: String,
          strategy: String,
          context: UIApplication,
          callbacks: AdvertiserCallbacks,
          userName: String = GeneralConstants.DEFAULT_USERNAME,
          manualAcceptConnections: Bool = false) {
         self.advertiser = nil
          super.init(serviceId: serviceId,
                    strategy: strategy,
                    context: context,
                    callbacks: callbacks,
                    userName: userName,
                    manualAcceptConnections: manualAcceptConnections)
        
         advertiser = Advertiser(connectionManager: connectionManager)
         advertiser!.delegate = self
     }
    
     func startAdvertising() {
         advertiser!.startAdvertising(using: userName)
     }
 }
class Connector: ConnectionManagerDelegate {
    func connectionManager(_ connectionManager: ConnectionManager, didReceive verificationCode: String, from endpointID: EndpointID, verificationHandler: @escaping (Bool) -> Void) {
        verificationHandler(true)
    }
   
    func connectionManager(_ connectionManager: ConnectionManager, didReceive data: Data, withID payloadID: PayloadID, from endpointID: EndpointID) {
        // TODO
    }
   
    func connectionManager(_ connectionManager: ConnectionManager, didReceive stream: InputStream, withID payloadID: PayloadID, from endpointID: EndpointID, cancellationToken token: CancellationToken) {
        // TODO
    }
   
    func connectionManager(_ connectionManager: ConnectionManager, didStartReceivingResourceWithID payloadID: PayloadID, from endpointID: EndpointID, at localURL: URL, withName name: String, cancellationToken token: CancellationToken) {
        // TODO
    }
   
    func connectionManager(_ connectionManager: ConnectionManager, didReceiveTransferUpdate update: TransferUpdate, from endpointID: EndpointID, forPayload payloadID: PayloadID) {
        // TODO
    }
   
    func connectionManager(_ connectionManager: ConnectionManager, didChangeTo state: ConnectionState, for endpointID: EndpointID) {
        switch state {
          case .connecting:
            // Handle connecting state
            break
          case .connected:
            // Handle connected state
            break
          case .disconnected:
            // Handle disconnected state
            // Perform actions when the connection is
            break
          // Your code here
          case .rejected:
            // Handle rejected state
            break
          }
        // TODO
    }
   
    let serviceId: String
    let context: UIApplication
    let callbacks: ConnectionCallbacks
    let userName: Data
    let strategy: Strategy
    let connectionManager: ConnectionManager
    var manualAcceptConnections: Bool

    init(serviceId: String,
         strategy: String,
         context: UIApplication,
         callbacks: ConnectionCallbacks,
         userName: String = GeneralConstants.DEFAULT_USERNAME,
         manualAcceptConnections: Bool = false) {
        self.serviceId = serviceId
        self.context = context
        self.callbacks = callbacks
        self.userName = userName.data(using: .utf8) ?? GeneralConstants.DEFAULT_USERNAME.data(using: .utf8)!
        self.strategy = Connector.getStrategy(strategy)
        self.manualAcceptConnections = manualAcceptConnections
        connectionManager = ConnectionManager(serviceID: serviceId, strategy: self.strategy)
        connectionManager.delegate = self
    }
   
    static func getStrategy(_ strategy: String) -> Strategy {
        guard let connectionStrategy = ConnectionStrategies(rawValue: strategy) else {
            return .star // Default strategy if not found
        }
        switch connectionStrategy {
            case .P2P_CLUSTER:
                return .cluster
            case .P2P_STAR:
                return .star
            case .P2P_POINT_TO_POINT:
            return .pointToPoint
        }
    }
}

@bourdakos1
Copy link
Collaborator

bourdakos1 commented Mar 11, 2024

The advertiser and connection manager code looks fine to me, do you have a code snippet for the discoverer side?

@IgVelasco
Copy link
Author

The code is part of a flutter plugin we are working on, I started the code on the swift side since the android part is quite done. So Im doing the advertisement only for now, and discovering using an android device.

Maybe I could share the discovery code later :)

@IgVelasco
Copy link
Author

The code is pretty much the same since I instantiate a new Connector class which contains the verificationHandler set to true

class NCDiscoverer: Connector, DiscovererDelegate {
    var discoverer: Discoverer?

    init(serviceId: String,
         strategy: String,
         context: UIApplication,
         callbacks: DiscovererCallbacks,
         userName: String = GeneralConstants.DEFAULT_USERNAME) {
         self.discoverer = nil
         super.init(serviceId: serviceId,
                   strategy: strategy,
                   context: context,
                   callbacks: callbacks,
                   userName: userName)

        discoverer = Discoverer(connectionManager: connectionManager)
        discoverer!.delegate = self
    }

    func discoverer(
        _ discoverer: Discoverer, didFind endpointID: EndpointID, with context: Data) {
            // An endpoint was found.
            var endpointName = String(decoding: context, as: UTF8.self)
            (callbacks as! any DiscovererCallbacks as DiscovererCallbacks)
                .onEndpointFound(
                    endpointId: endpointID,
                    endpointName: endpointName
                )
    }

      func discoverer(_ discoverer: Discoverer, didLose endpointID: EndpointID) {
          // A previously discovered endpoint has gone away.
          (callbacks as! any DiscovererCallbacks as DiscovererCallbacks).onEndpointLost(endpointId: endpointID)
      }

    func connect(endpointId: String) {
        let completionHandler: (Error?) -> Void  = {(error) in
            print(error ?? "Requested connection to \(endpointId)")
        };

        discoverer?.requestConnection(to: endpointId, using: userName)
    }

    func startDiscovering() {
        let completionHandler: (Error?) -> Void  = {(error) in
            print(error ?? "Starting to discover devices in iOS")
        };

        discoverer!.startDiscovery(completionHandler: completionHandler);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lang: swift An issue with a Swift client library P0 Priority 0 issue (such as a build break, regression or customer blocking) platform: apple An issue with the macOS or iOS implementation project: connections An issue with the Connections project type: bug Something is broken or not working as intended
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants