nearby: iOS connection error when advertising

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.

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 1
  • Comments: 15

Commits related to this issue

Most upvoted comments

@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 thanks a lot! This solves the issue

Yes looks like that fixes the issue thanks!

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

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

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.