More robust P4Runtime group handling

This patch solves the PENDING_UPDATE and PENDING_ADD_RETRY issue
observed on the ONS EU topology.

The P4Runtime action profile group handling has been re-implemented to
be robust against inconsistencies of the device mirror, which is now
periodically synchronized with the device state. Similarly, we implement
a routine in the P4RuntimeClient to cleanup unused action profile
members.

This patch includes also:
-  Refactor PI handle classes to allow creating handles without the
entity instance
- Use list instead of collections in P4RuntimeClient methods, as order
of updates sent and/or entities received from the device is important

Change-Id: I2e7964ce90f43d66680131b47ab52aca32ab55d2
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
index 85a87fe..d0acdee 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
@@ -140,6 +140,11 @@
                 streamEntries(), streamDefaultEntries())
                 // Ignore entries from constant tables.
                 .filter(e -> !tableIsConstant(e.table()))
+                // Device implementation might return duplicate entries. For
+                // example if reading only default ones is not supported and
+                // non-default entries are returned, by using distinct() we are
+                // robust against that possibility.
+                .distinct()
                 .collect(Collectors.toList());
 
         if (deviceEntries.isEmpty()) {
@@ -148,7 +153,6 @@
 
         // Synchronize mirror with the device state.
         syncMirror(deviceEntries);
-        // Read table direct counters for non default-entries (if any).
         // TODO: ONOS-7596 read counters with table entries
         final Map<PiTableEntry, PiCounterCellData> counterCellMap =
                 readEntryCounters(deviceEntries);
@@ -229,7 +233,12 @@
             log.warn("Table entry handle not found in translation store: {}", handle);
             return null;
         }
-
+        if (!translatedEntity.get().translated().equals(entry)) {
+            log.warn("Table entry obtained from device {} is different from " +
+                             "one in in translation store: device={}, store={}",
+                     deviceId, entry, translatedEntity.get().translated());
+            return null;
+        }
         if (timedEntry == null) {
             log.warn("Table entry handle not found in device mirror: {}", handle);
             return null;
@@ -460,6 +469,7 @@
             cellDatas = Collections.emptyList();
         } else {
             Set<PiCounterCellId> cellIds = tableEntries.stream()
+                    // Ignore counter for default entry.
                     .filter(e -> !e.isDefaultAction())
                     .filter(e -> tableHasCounter(e.table()))
                     .map(PiCounterCellId::ofDirect)