Allow sharing the same gRPC channel between clients

This change introduces a refactoring of the gRPC protocol subsystem that
allows the creation of a gRPC chanel independently of the client, while
allowing multiple clients to share the same channel (e.g. as in Stratum
where we use 3 clients).

Moreover, we refactor the P4RuntimeClient API to support multiple
P4Runtime-internal device ID using the same client. While before the
client was associated to one of such ID.

Finally, we provide an abstract implementation for gRPC-based driver
behaviors, reducing code duplication in P4Runtime, gNMI and gNOI drivers.

Change-Id: I1a46352bbbef1e0d24042f169ae8ba580202944f
diff --git a/core/api/src/main/java/org/onosproject/net/driver/DeviceConnect.java b/core/api/src/main/java/org/onosproject/net/driver/DeviceConnect.java
index 433be71..ffb7deb 100644
--- a/core/api/src/main/java/org/onosproject/net/driver/DeviceConnect.java
+++ b/core/api/src/main/java/org/onosproject/net/driver/DeviceConnect.java
@@ -17,8 +17,6 @@
 
 import com.google.common.annotations.Beta;
 
-import java.util.concurrent.CompletableFuture;
-
 /**
  * Abstraction of handler behaviour used to set-up and tear-down connections
  * with a device. A connection is intended as the presence of state (e.g. a
@@ -31,22 +29,24 @@
     /**
      * Connects to the device, for example by opening the transport session that
      * will be later used to send control messages. Returns true if the
-     * connection was initiated successfully, false otherwise. The
-     * implementation might require probing the device over the network to
-     * initiate the connection.
+     * connection was created successfully, false otherwise.
+     * <p>
+     * The implementation should trigger without blocking any necessary
+     * handshake with the device to initialize the connection over the network,
+     * eventually generating a {@link org.onosproject.net.device.DeviceAgentEvent.Type#CHANNEL_OPEN}
+     * event when ready.
      * <p>
      * When calling this method while a connection to the device already exists,
      * the behavior is not defined. For example, some implementations might
      * require to first call {@link #disconnect()}, while other might behave as
      * a no-op.
      *
-     * @return CompletableFuture eventually true if a connection was created
-     * successfully, false otherwise
+     * @return true if a connection was created successfully, false otherwise
      * @throws IllegalStateException if a connection already exists and the
      *                               implementation requires to call {@link
      *                               #disconnect()} first.
      */
-    CompletableFuture<Boolean> connect() throws IllegalStateException;
+    boolean connect() throws IllegalStateException;
 
     /**
      * Returns true if a connection to the device exists, false otherwise. This
@@ -61,14 +61,19 @@
      *
      * @return true if the connection is open, false otherwise
      */
-    boolean isConnected();
+    boolean hasConnection();
 
     /**
-     * Disconnects from the device, for example closing the transport session
+     * Disconnects from the device, for example closing any transport session
      * previously opened.
      * <p>
      * Calling multiple times this method while a connection to the device is
      * already closed should result in a no-op.
+     * <p>
+     * If a connection to the device existed and it was open, the implementation
+     * is expected to generate a
+     * {@link org.onosproject.net.device.DeviceAgentEvent.Type#CHANNEL_CLOSED}
+     * event.
      */
     void disconnect();
 }
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfWatchdogManager.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfWatchdogManager.java
index 0503d9a..e76d673 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfWatchdogManager.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiPipeconfWatchdogManager.java
@@ -241,7 +241,7 @@
         log.debug("Starting watchdog task for {} ({})", device.id(), pipeconf.id());
         final PiPipelineProgrammable pipelineProg = device.as(PiPipelineProgrammable.class);
         final DeviceHandshaker handshaker = device.as(DeviceHandshaker.class);
-        if (!handshaker.isConnected()) {
+        if (!handshaker.hasConnection()) {
             return false;
         }
         if (Futures.getUnchecked(pipelineProg.isPipeconfSet(pipeconf))) {