Add probeReachability API to the DeviceProvider

The new API is meant for probing the reachability of a device
and adds the ability to directly asses the result of the probe.

Change-Id: I310eba11b943208b5d6776fd8ccbc679d55dfb41
(cherry picked from commit d7cae13c11bdc6c9d344d362ecb71ad99df67367)
diff --git a/core/api/src/main/java/org/onosproject/net/device/DeviceProvider.java b/core/api/src/main/java/org/onosproject/net/device/DeviceProvider.java
index 771a8e4..8dfa730 100644
--- a/core/api/src/main/java/org/onosproject/net/device/DeviceProvider.java
+++ b/core/api/src/main/java/org/onosproject/net/device/DeviceProvider.java
@@ -20,6 +20,8 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.provider.Provider;
 
+import java.util.concurrent.CompletableFuture;
+
 /**
  * Abstraction of a device information provider.
  */
@@ -107,4 +109,20 @@
         throw new UnsupportedOperationException(id() + " does not implement this feature");
     }
 
+    /**
+     * Probe the reachability of the device from the provider perspective.
+     * <p>
+     * Implementations are encouraged to provide an async implementation.
+     * With the respect of triggerProbe, this method returns whether or
+     * not was possible to send a message to the device from this instance.
+     * Instead, isReachable asses only the capability to send a message from
+     * this node but does not imply sending a message to the device.
+     *
+     * @param deviceId device identifier
+     * @return completable future eventually true if reachable, false otherwise
+     */
+    default CompletableFuture<Boolean> probeReachability(DeviceId deviceId) {
+        return CompletableFuture.completedFuture(isReachable(deviceId));
+    }
+
 }
diff --git a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
index c91cfb2..6ce3269 100644
--- a/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
+++ b/providers/general/device/src/main/java/org/onosproject/provider/general/device/impl/GeneralDeviceProvider.java
@@ -421,6 +421,22 @@
         submitTask(deviceId, TaskType.CONNECTION_TEARDOWN);
     }
 
+    @Override
+    public CompletableFuture<Boolean> probeReachability(DeviceId deviceId) {
+        final DeviceHandshaker handshaker = getBehaviour(
+                deviceId, DeviceHandshaker.class);
+        if (handshaker == null) {
+            return CompletableFuture.completedFuture(false);
+        }
+        return handshaker.probeReachability();
+    }
+
+    private boolean probeReachabilitySync(DeviceId deviceId) {
+        // Wait 3/4 of the checkup interval
+        return Tools.futureGetOrElse(probeReachability(deviceId), (checkupInterval * 3000 / 4),
+                TimeUnit.MILLISECONDS, Boolean.FALSE);
+    }
+
     /**
      * Listener for configuration events.
      */
@@ -741,16 +757,12 @@
         // If here, device should be registered in the core.
         assertDeviceRegistered(deviceId);
 
-        if (!handshaker.isReachable()) {
+        if (!handshaker.isReachable() || !probeReachabilitySync(deviceId)) {
             // Device appears to be offline.
             markOfflineIfNeeded(deviceId);
-            // While we expect the protocol layer to implement some sort of
+            // We expect the protocol layer to implement some sort of
             // connection backoff mechanism and to signal availability via
-            // CHANNEL_OPEN events, we stimulate some channel activity now.
-            // Trigger probe over the network and forget about it (not waiting
-            // for future to complete). If channel is ready, we expect to come
-            // back here via a CHANNEL_OPEN event.
-            handshaker.probeReachability();
+            // CHANNEL_OPEN events.
             return;
         }