Enable pairing for multiple devices (#3630)

diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h
index a115288..1e7ec69 100644
--- a/src/controller/CHIPDeviceController.h
+++ b/src/controller/CHIPDeviceController.h
@@ -28,28 +28,23 @@
 
 #pragma once
 
+#include <controller/CHIPDevice.h>
 #include <controller/CHIPPersistentStorageDelegate.h>
 #include <core/CHIPCore.h>
 #include <core/CHIPTLV.h>
 #include <support/DLLUtil.h>
+#include <support/SerializableIntegerSet.h>
 #include <transport/RendezvousSession.h>
 #include <transport/RendezvousSessionDelegate.h>
 #include <transport/SecureSessionMgr.h>
 #include <transport/raw/UDP.h>
 
 namespace chip {
-namespace DeviceController {
 
-constexpr uint16_t kPacketCacheMaxSize = 16;
+namespace Controller {
 
-class ChipDeviceController;
-
-typedef void (*NewConnectionHandler)(ChipDeviceController * deviceController, Transport::PeerConnectionState * state,
-                                     void * appReqState);
-typedef void (*CompleteHandler)(ChipDeviceController * deviceController, void * appReqState);
-typedef void (*ErrorHandler)(ChipDeviceController * deviceController, void * appReqState, CHIP_ERROR err,
-                             const Inet::IPPacketInfo * pktInfo);
-typedef void (*MessageReceiveHandler)(ChipDeviceController * deviceController, void * appReqState, System::PacketBuffer * payload);
+constexpr uint16_t kNumMaxActiveDevices = 64;
+constexpr uint16_t kNumMaxPairedDevices = 128;
 
 class DLL_EXPORT DevicePairingDelegate
 {
@@ -100,117 +95,57 @@
     virtual void OnPairingDeleted(CHIP_ERROR error) {}
 };
 
-class DLL_EXPORT ChipDeviceController : public SecureSessionMgrDelegate,
-                                        public RendezvousSessionDelegate,
-                                        public PersistentStorageResultDelegate
+/**
+ * @brief
+ *   Controller applications can use this class to communicate with already paired CHIP devices. The
+ *   application is required to provide access to the persistent storage, where the paired device information
+ *   is stored. This object of this class can be initialized with the data from the storage (List of devices,
+ *   and device pairing information for individual devices). Alternatively, this class can retrieve the
+ *   relevant information when the application tries to communicate with the device
+ */
+class DLL_EXPORT DeviceController : public SecureSessionMgrDelegate, public PersistentStorageResultDelegate
 {
-    friend class ChipDeviceControllerCallback;
-
 public:
-    ChipDeviceController();
-    ~ChipDeviceController();
-
-    void * AppState;
+    DeviceController();
+    virtual ~DeviceController() {}
 
     /**
      * Init function to be used when there exists a device layer that takes care of initializing
      * System::Layer and InetLayer.
      */
-    CHIP_ERROR Init(NodeId localDeviceId, DevicePairingDelegate * pairingDelegate = nullptr,
-                    PersistentStorageDelegate * storageDelegate = nullptr);
-    /**
-     * Init function to be used when already-initialized System::Layer and InetLayer are available.
-     */
-    CHIP_ERROR Init(NodeId localDeviceId, System::Layer * systemLayer, Inet::InetLayer * inetLayer,
-                    DevicePairingDelegate * pairingDelegate = nullptr, PersistentStorageDelegate * storageDelegate = nullptr);
-    CHIP_ERROR Shutdown();
+    CHIP_ERROR Init(NodeId localDeviceId, PersistentStorageDelegate * storageDelegate = nullptr,
+                    System::Layer * systemLayer = nullptr, Inet::InetLayer * inetLayer = nullptr);
 
-    CHIP_ERROR SetUdpListenPort(uint16_t listenPort);
-
-    // ----- Connection Management -----
-    /**
-     * @brief
-     *   Connect to a CHIP device with the provided Rendezvous connection parameters
-     *
-     * @param[in] remoteDeviceId        The remote device Id.
-     * @param[in] params                The Rendezvous connection parameters
-     * @param[in] appReqState           Application specific context to be passed back when a message is received or on error
-     * @param[in] onConnected           Callback for when the connection is established
-     * @param[in] onMessageReceived     Callback for when a message is received
-     * @param[in] onError               Callback for when an error occurs
-     * @param[in] devicePort            [Optional] The CHIP Device's port, defaults to CHIP_PORT
-     * @param[in] interfaceId           [Optional] The interface indicator to use
-     *
-     * @return CHIP_ERROR               The connection status
-     */
-    CHIP_ERROR ConnectDevice(NodeId remoteDeviceId, RendezvousParameters & params, void * appReqState,
-                             NewConnectionHandler onConnected, MessageReceiveHandler onMessageReceived, ErrorHandler onError,
-                             uint16_t devicePort = CHIP_PORT, Inet::InterfaceId interfaceId = INET_NULL_INTERFACEID);
+    virtual CHIP_ERROR Shutdown();
 
     /**
      * @brief
-     *   Connect to a CHIP device at a given address and an optional port. This is a test only API
-     *   that bypasses Rendezvous and Secure Pairing process.
+     *   This function deserializes the provided deviceInfo object, and initializes and outputs the
+     *   corresponding Device object. The lifetime of the output object is tied to that of the DeviceController
+     *   object. The caller must not use the Device object If they free the DeviceController object, or
+     *   after they call ReleaseDevice() on the returned device object.
      *
-     * @param[in] remoteDeviceId        The remote device Id.
-     * @param[in] deviceAddr            The IPAddress of the requested Device
-     * @param[in] appReqState           Application specific context to be passed back when a message is received or on error
-     * @param[in] onConnected           Callback for when the connection is established
-     * @param[in] onMessageReceived     Callback for when a message is received
-     * @param[in] onError               Callback for when an error occurs
-     * @param[in] devicePort            [Optional] The CHIP Device's port, defaults to CHIP_PORT
-     * @param[in] interfaceId           [Optional] The interface indicator to use
-     * @return CHIP_ERROR           The connection status
+     * @param[in] deviceId   Node ID for the CHIP device
+     * @param[in] deviceInfo Serialized device info for the device
+     * @param[out] device    The output device object
+     *
+     * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
      */
-    [[deprecated("Available until Rendezvous is implemented")]] CHIP_ERROR
-    ConnectDeviceWithoutSecurePairing(NodeId remoteDeviceId, const Inet::IPAddress & deviceAddr, void * appReqState,
-                                      NewConnectionHandler onConnected, MessageReceiveHandler onMessageReceived,
-                                      ErrorHandler onError, uint16_t devicePort = CHIP_PORT,
-                                      Inet::InterfaceId interfaceId = INET_NULL_INTERFACEID);
+    CHIP_ERROR GetDevice(NodeId deviceId, const SerializedDevice & deviceInfo, Device ** device);
 
     /**
      * @brief
-     *   Disconnect from a connected device
+     *   This function is similar to the other GetDevice object, except it reads the serialized object from
+     *   the persistent storage.
      *
-     * @return CHIP_ERROR   If the device was disconnected successfully
+     * @param[in] deviceId   Node ID for the CHIP device
+     * @param[out] device    The output device object
+     *
+     * @return CHIP_ERROR CHIP_NO_ERROR on success, or corresponding error code.
      */
-    CHIP_ERROR DisconnectDevice();
+    CHIP_ERROR GetDevice(NodeId deviceId, Device ** device);
 
-    /**
-     * @brief
-     *   Check if there's an active connection
-     *
-     * @return bool   If there is an active connection
-     */
-    bool IsConnected() const;
-
-    /**
-     * @brief
-     *   Check if the connection is active and security context is established
-     *
-     * @return bool   If the connection is active and security context is established
-     */
-    bool IsSecurelyConnected() const;
-
-    /**
-     * @brief
-     *   Get IP Address of the peer if the connection is active
-     *
-     * @return bool   If IP Address was returned
-     */
-    bool GetIpAddress(Inet::IPAddress & addr) const;
-
-    // ----- Messaging -----
-    /**
-     * @brief
-     *   Send a message to a connected CHIP device
-     *
-     * @param[in] appReqState   Application specific context to be passed back when a message is received or on error
-     * @param[in] buffer        The Data Buffer to trasmit to the device
-     * @param[in] peerDevice    Device ID of the peer device
-     * @return CHIP_ERROR   The return status
-     */
-    CHIP_ERROR SendMessage(void * appReqState, System::PacketBuffer * buffer, NodeId peerDevice = kUndefinedNodeId);
+    virtual void ReleaseDevice(Device * device);
 
     // ----- IO -----
     /**
@@ -220,14 +155,6 @@
      */
     CHIP_ERROR ServiceEvents();
 
-    // ----- Pairing -----
-    /**
-     * @brief
-     * Set device pairing delegate after init, pass nullptr remove device delegate.
-     * @return CHIP_ERROR   The return status
-     */
-    CHIP_ERROR SetDevicePairingDelegate(DevicePairingDelegate * pairingDelegate);
-
     /**
      * @brief
      *   Allow the CHIP Stack to process any pending events
@@ -236,86 +163,142 @@
      */
     CHIP_ERROR ServiceEventSignal();
 
+protected:
+    enum class State
+    {
+        NotInitialized,
+        Initialized
+    };
+
+    State mState;
+
+    /* A list of device objects that can be used for communicating with corresponding
+       CHIP devices. The list does not contain all the paired devices, but only the ones
+       which the controller application is currently accessing.
+    */
+    Device mActiveDevices[kNumMaxActiveDevices];
+
+    SerializableU64Set<kNumMaxPairedDevices> mPairedDevices;
+    bool mPairedDevicesInitialized;
+
+    NodeId mLocalDeviceId;
+    SecureSessionMgr<Transport::UDP> * mSessionManager;
+    PersistentStorageDelegate * mStorageDelegate;
+    Inet::InetLayer * mInetLayer;
+
+    uint16_t GetInactiveDeviceIndex();
+    uint16_t FindDeviceIndex(NodeId id);
+    void ReleaseDevice(uint16_t index);
+    CHIP_ERROR SetPairedDeviceList(const char * pairedDeviceSerializedSet);
+
+private:
+    //////////// SecureSessionMgrDelegate Implementation ///////////////
     void OnMessageReceived(const PacketHeader & header, const PayloadHeader & payloadHeader, Transport::PeerConnectionState * state,
                            System::PacketBuffer * msgBuf, SecureSessionMgrBase * mgr) override;
 
     void OnNewConnection(Transport::PeerConnectionState * state, SecureSessionMgrBase * mgr) override;
 
-    void OnAddressResolved(CHIP_ERROR error, NodeId nodeId, SecureSessionMgrBase * mgr) override;
+    //////////// PersistentStorageResultDelegate Implementation ///////////////
+    void OnValue(const char * key, const char * value) override;
+    void OnStatus(const char * key, Operation op, CHIP_ERROR err) override;
+
+    System::Layer * mSystemLayer;
+};
+
+/**
+ * @brief
+ *   The commissioner applications can use this class to pair new/unpaired CHIP devices. The application is
+ *   required to provide write access to the persistent storage, where the paired device information
+ *   will be stored.
+ */
+class DLL_EXPORT DeviceCommissioner : public DeviceController, public RendezvousSessionDelegate
+{
+public:
+    DeviceCommissioner();
+    ~DeviceCommissioner() {}
+
+    /**
+     * Init function to be used when there exists a device layer that takes care of initializing
+     * System::Layer and InetLayer.
+     */
+    CHIP_ERROR Init(NodeId localDeviceId, PersistentStorageDelegate * storageDelegate = nullptr,
+                    DevicePairingDelegate * pairingDelegate = nullptr, System::Layer * systemLayer = nullptr,
+                    Inet::InetLayer * inetLayer = nullptr);
+
+    void SetDevicePairingDelegate(DevicePairingDelegate * pairingDelegate) { mPairingDelegate = pairingDelegate; }
+
+    CHIP_ERROR Shutdown() override;
+
+    // ----- Connection Management -----
+    /**
+     * @brief
+     *   Pair a CHIP device with the provided Rendezvous connection parameters.
+     *   Use registered DevicePairingDelegate object to receive notifications on
+     *   pairing status updates.
+     *
+     *   Note: Pairing process requires that the caller has registered PersistentStorageDelegate
+     *         in the Init() call.
+     *
+     * @param[in] remoteDeviceId        The remote device Id.
+     * @param[in] params                The Rendezvous connection parameters
+     * @param[in] devicePort            [Optional] The CHIP Device's port, defaults to CHIP_PORT
+     * @param[in] interfaceId           [Optional] The local inet interface to use to communicate with the device.
+     *
+     * @return CHIP_ERROR               The connection status
+     */
+    CHIP_ERROR PairDevice(NodeId remoteDeviceId, RendezvousParameters & params, uint16_t devicePort = CHIP_PORT,
+                          Inet::InterfaceId interfaceId = INET_NULL_INTERFACEID);
+
+    [[deprecated("Available until Rendezvous is implemented")]] CHIP_ERROR
+    PairTestDeviceWithoutSecurity(NodeId remoteDeviceId, const Inet::IPAddress & deviceAddr, SerializedDevice & serialized,
+                                  uint16_t devicePort = CHIP_PORT, Inet::InterfaceId interfaceId = INET_NULL_INTERFACEID);
+
+    /**
+     * @brief
+     *   This function stops a pairing process that's in progress. It does not delete the pairing of a previously
+     *   paired device.
+     *
+     * @param[in] remoteDeviceId        The remote device Id.
+     *
+     * @return CHIP_ERROR               CHIP_NO_ERROR on success, or corresponding error
+     */
+    CHIP_ERROR StopPairing(NodeId remoteDeviceId);
+
+    /**
+     * @brief
+     *   Remove pairing for a paired device. If the device is currently being paired, it'll stop the pairing process.
+     *
+     * @param[in] remoteDeviceId        The remote device Id.
+     *
+     * @return CHIP_ERROR               CHIP_NO_ERROR on success, or corresponding error
+     */
+    CHIP_ERROR UnpairDevice(NodeId remoteDeviceId);
 
     //////////// RendezvousSessionDelegate Implementation ///////////////
     void OnRendezvousError(CHIP_ERROR err) override;
     void OnRendezvousComplete() override;
     void OnRendezvousStatusUpdate(RendezvousSessionDelegate::Status status, CHIP_ERROR err) override;
 
-    //////////// PersistentStorageResultDelegate Implementation ///////////////
-    void OnValue(const char * key, const char * value) override;
-    void OnStatus(const char * key, Operation op, CHIP_ERROR err) override;
+    void RendezvousCleanup(CHIP_ERROR status);
+
+    void ReleaseDevice(Device * device) override;
 
 private:
-    enum
-    {
-        kState_NotInitialized = 0,
-        kState_Initialized    = 1
-    } mState;
-
-    enum ConnectionState
-    {
-        kConnectionState_NotConnected     = 0,
-        kConnectionState_Connected        = 1,
-        kConnectionState_SecureConnecting = 2,
-        kConnectionState_SecureConnected  = 3,
-    };
-
-    System::Layer * mSystemLayer;
-    Inet::InetLayer * mInetLayer;
-
-    SecureSessionMgr<Transport::UDP> * mSessionManager;
+    DevicePairingDelegate * mPairingDelegate;
     RendezvousSession * mRendezvousSession;
 
-    ConnectionState mConState;
-    void * mAppReqState;
+    /* This field is an index in mActiveDevices list. The object at this index in the list
+       contains the device object that's tracking the state of the device that's being paired.
+       If no device is currently being paired, this value will be kNumMaxPairedDevices.  */
+    uint16_t mDeviceBeingPaired;
 
-    union
-    {
-        CompleteHandler General;
-        MessageReceiveHandler Response;
-    } mOnComplete;
+    /* This field is true when device pairing information changes, e.g. a new device is paired, or
+       the pairing for a device is removed. The DeviceCommissioner uses this to decide when to
+       persist the device list */
+    bool mPairedDevicesUpdated;
 
-    ErrorHandler mOnError;
-    NewConnectionHandler mOnNewConnection;
-    System::PacketBuffer * mCurReqMsg;
-
-    NodeId mLocalDeviceId;
-    uint16_t mListenPort;
-    Inet::IPAddress mDeviceAddr;
-    uint16_t mDevicePort;
-    Inet::InterfaceId mInterface;
-    Optional<NodeId> mRemoteDeviceId;
-
-    SecurePairingSession mPairingSession;
-    SecurePairingUsingTestSecret * mTestSecurePairingSecret = nullptr;
-
-    SecurePairingSession * mSecurePairingSession = nullptr;
-
-    DevicePairingDelegate * mPairingDelegate = nullptr;
-
-    PersistentStorageDelegate * mStorageDelegate;
-
-    System::PacketBuffer * mCachedPackets[kPacketCacheMaxSize];
-    uint16_t mNumCachedPackets;
-
-    void ClearRequestState();
-    void ClearOpState();
-
-    CHIP_ERROR EstablishSecureSession();
-    CHIP_ERROR TryEstablishingSecureSession(NodeId peer);
-    CHIP_ERROR ResumeSecureSession(NodeId peer);
-
-    CHIP_ERROR CachePacket(System::PacketBuffer * buffer);
-    CHIP_ERROR SendCachedPackets();
-    void DiscardCachedPackets();
+    void PersistDeviceList();
 };
 
-} // namespace DeviceController
+} // namespace Controller
 } // namespace chip