Bumped supported commit of P4Runtime and BMv2

Includes fixes for:
- ONOS-7593: Support for indirect resource Index type
- ONOS-7595: Removed ID from direct resources
- P4Runtime requires unset bits to be 0 in ternary field matches
- Incorrect parsing of flow rule byte counters
- Full entity names in P4Info with top-level control block (fixed only
	for basic.p4, other programs need to be re-compiled and PI IDs in
	respective pipeconf changed)

Change-Id: Ia19aa949c02e363a550e692915c6d6516a2d13d7
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
index df4e72f..17bff76 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
@@ -19,27 +19,31 @@
 import org.onosproject.net.pi.model.PiCounterId;
 import org.onosproject.net.pi.model.PiCounterType;
 import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiTableId;
 import org.onosproject.net.pi.runtime.PiCounterCellData;
 import org.onosproject.net.pi.runtime.PiCounterCellId;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.slf4j.Logger;
+import p4.P4RuntimeOuterClass;
 import p4.P4RuntimeOuterClass.CounterData;
 import p4.P4RuntimeOuterClass.CounterEntry;
 import p4.P4RuntimeOuterClass.DirectCounterEntry;
 import p4.P4RuntimeOuterClass.Entity;
 
 import java.util.Collection;
-import java.util.Map;
+import java.util.Collections;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
 import static java.lang.String.format;
+import static org.onosproject.p4runtime.ctl.P4RuntimeUtils.indexMsg;
 import static org.slf4j.LoggerFactory.getLogger;
 import static p4.P4RuntimeOuterClass.Entity.EntityCase.COUNTER_ENTRY;
 import static p4.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_COUNTER_ENTRY;
 
 /**
- * Encoder/decoder of PI counter IDs to counter entry protobuf messages, and vice versa.
+ * Encoder/decoder of PI counter IDs to counter entry protobuf messages, and
+ * vice versa.
  */
 final class CounterEntryCodec {
 
@@ -50,27 +54,32 @@
     }
 
     /**
-     * Returns a collection of P4Runtime entity protobuf messages describing both counter or direct counter entries,
-     * encoded from the given collection of PI counter cell identifiers, for the given pipeconf. If a PI counter cell
-     * identifier cannot be encoded, it is skipped, hence the returned collection might have different size than the
-     * input one.
-     * <p>
-     * This method takes as parameter also a map between numeric P4Info IDs and PI counter IDs, that will be populated
-     * during the process and that is then needed to aid in the decode process.
+     * Returns a collection of P4Runtime entity protobuf messages describing
+     * both counter or direct counter entries, encoded from the given collection
+     * of PI counter cell identifiers, for the given pipeconf. If a PI counter
+     * cell identifier cannot be encoded, it is skipped, hence the returned
+     * collection might have different size than the input one.
      *
-     * @param cellIds      counter cell identifiers
-     * @param counterIdMap counter ID map (empty, it will be populated during this method execution)
-     * @param pipeconf     pipeconf
-     * @return collection of entity messages describing both counter or direct counter entries
+     * @param cellIds  counter cell identifiers
+     * @param pipeconf pipeconf
+     * @return collection of entity messages describing both counter or direct
+     * counter entries
      */
     static Collection<Entity> encodePiCounterCellIds(Collection<PiCounterCellId> cellIds,
-                                                     Map<Integer, PiCounterId> counterIdMap,
                                                      PiPipeconf pipeconf) {
+
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
         return cellIds
                 .stream()
                 .map(cellId -> {
                     try {
-                        return encodePiCounterCellId(cellId, counterIdMap, pipeconf);
+                        return encodePiCounterCellId(cellId, pipeconf, browser);
                     } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
                         log.warn("Unable to encode PI counter cell id: {}", e.getMessage());
                         return null;
@@ -81,28 +90,33 @@
     }
 
     /**
-     * Returns a collection of PI counter cell data, decoded from the given P4Runtime entity protobuf messages
-     * describing both counter or direct counter entries, for the given counter ID map (populated by {@link
-     * #encodePiCounterCellIds(Collection, Map, PiPipeconf)}), and pipeconf. If an entity message cannot be encoded, it
-     * is skipped, hence the returned collection might have different size than the input one.
+     * Returns a collection of P4Runtime entity protobuf messages to be used in
+     * requests to read all cells from the given counter identifiers. Works for
+     * both indirect or direct counters. If a PI counter identifier cannot be
+     * encoded, it is skipped, hence the returned collection might have
+     * different size than the input one.
      *
-     * @param entities     P4Runtime entity messages
-     * @param counterIdMap counter ID map (previously populated)
-     * @param pipeconf     pipeconf
-     * @return collection of PI counter cell data
+     * @param counterIds counter identifiers
+     * @param pipeconf   pipeconf
+     * @return collection of entity messages
      */
-    static Collection<PiCounterCellData> decodeCounterEntities(Collection<Entity> entities,
-                                                               Map<Integer, PiCounterId> counterIdMap,
-                                                               PiPipeconf pipeconf) {
-        return entities
+    static Collection<Entity> readAllCellsEntities(Collection<PiCounterId> counterIds,
+                                                   PiPipeconf pipeconf) {
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
+        return counterIds
                 .stream()
-                .filter(entity -> entity.getEntityCase() == COUNTER_ENTRY ||
-                        entity.getEntityCase() == DIRECT_COUNTER_ENTRY)
-                .map(entity -> {
+                .map(counterId -> {
                     try {
-                        return decodeCounterEntity(entity, counterIdMap, pipeconf);
-                    } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
-                        log.warn("Unable to decode counter entity message: {}", e.getMessage());
+                        return readAllCellsEntity(counterId, pipeconf, browser);
+                    } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+                        log.warn("Unable to encode counter ID to read-all-cells entity: {}",
+                                 e.getMessage());
                         return null;
                     }
                 })
@@ -110,95 +124,160 @@
                 .collect(Collectors.toList());
     }
 
-    private static Entity encodePiCounterCellId(PiCounterCellId cellId, Map<Integer, PiCounterId> counterIdMap,
-                                                PiPipeconf pipeconf)
-            throws P4InfoBrowser.NotFoundException, EncodeException {
+    /**
+     * Returns a collection of PI counter cell data, decoded from the given
+     * P4Runtime entity protobuf messages describing both counter or direct
+     * counter entries, and pipeconf. If an entity message cannot be encoded, it
+     * is skipped, hence the returned collection might have different size than
+     * the input one.
+     *
+     * @param entities P4Runtime entity messages
+     * @param pipeconf pipeconf
+     * @return collection of PI counter cell data
+     */
+    static Collection<PiCounterCellData> decodeCounterEntities(Collection<Entity> entities,
+                                                               PiPipeconf pipeconf) {
 
         final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
 
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
+        return entities
+                .stream()
+                .filter(entity -> entity.getEntityCase() == COUNTER_ENTRY ||
+                        entity.getEntityCase() == DIRECT_COUNTER_ENTRY)
+                .map(entity -> {
+                    try {
+                        return decodeCounterEntity(entity, pipeconf, browser);
+                    } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
+                        log.warn("Unable to decode counter entity message: {}",
+                                 e.getMessage());
+                        return null;
+                    }
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    private static Entity encodePiCounterCellId(PiCounterCellId cellId,
+                                                PiPipeconf pipeconf,
+                                                P4InfoBrowser browser)
+            throws P4InfoBrowser.NotFoundException, EncodeException {
+
         int counterId;
         Entity entity;
         // Encode PI cell ID into entity message and add to read request.
         switch (cellId.counterType()) {
             case INDIRECT:
-                counterId = browser.counters().getByName(cellId.counterId().id()).getPreamble().getId();
-                entity = Entity.newBuilder().setCounterEntry(CounterEntry.newBuilder()
-                                                                     .setCounterId(counterId)
-                                                                     .setIndex(cellId.index())
-                                                                     .build())
+                counterId = browser.counters()
+                        .getByName(cellId.counterId().id())
+                        .getPreamble()
+                        .getId();
+                entity = Entity.newBuilder()
+                        .setCounterEntry(
+                                CounterEntry.newBuilder()
+                                        .setCounterId(counterId)
+                                        .setIndex(indexMsg(cellId.index()))
+                                        .build())
                         .build();
                 break;
             case DIRECT:
-                counterId = browser.directCounters().getByName(cellId.counterId().id()).getPreamble().getId();
-                DirectCounterEntry.Builder entryBuilder = DirectCounterEntry.newBuilder().setCounterId(counterId);
-                if (!cellId.tableEntry().equals(PiTableEntry.EMTPY)) {
-                    entryBuilder.setTableEntry(TableEntryEncoder.encode(cellId.tableEntry(), pipeconf));
-                }
-                entity = Entity.newBuilder().setDirectCounterEntry(entryBuilder.build()).build();
+                DirectCounterEntry.Builder entryBuilder = DirectCounterEntry.newBuilder();
+                entryBuilder.setTableEntry(
+                        TableEntryEncoder.encode(cellId.tableEntry(), pipeconf));
+                entity = Entity.newBuilder()
+                        .setDirectCounterEntry(entryBuilder.build())
+                        .build();
                 break;
             default:
-                throw new EncodeException(format("Unrecognized PI counter cell ID type '%s'", cellId.counterType()));
+                throw new EncodeException(format(
+                        "Unrecognized PI counter cell ID type '%s'",
+                        cellId.counterType()));
         }
-        counterIdMap.put(counterId, cellId.counterId());
 
         return entity;
     }
 
-    private static PiCounterCellData decodeCounterEntity(Entity entity, Map<Integer, PiCounterId> counterIdMap,
-                                                         PiPipeconf pipeconf)
+    private static Entity readAllCellsEntity(PiCounterId counterId,
+                                             PiPipeconf pipeconf,
+                                             P4InfoBrowser browser)
+            throws P4InfoBrowser.NotFoundException, EncodeException {
+
+        if (!pipeconf.pipelineModel().counter(counterId).isPresent()) {
+            throw new EncodeException(format(
+                    "not such counter '%s' in pipeline model", counterId));
+        }
+        final PiCounterType counterType = pipeconf.pipelineModel()
+                .counter(counterId).get().counterType();
+
+        switch (counterType) {
+            case INDIRECT:
+                final int p4InfoCounterId = browser.counters()
+                        .getByName(counterId.id())
+                        .getPreamble().getId();
+                return Entity.newBuilder().setCounterEntry(
+                        P4RuntimeOuterClass.CounterEntry.newBuilder()
+                                // Index unset to read all cells
+                                .setCounterId(p4InfoCounterId)
+                                .build())
+                        .build();
+            case DIRECT:
+                final PiTableId tableId = pipeconf.pipelineModel()
+                        .counter(counterId).get().table();
+                if (tableId == null) {
+                    throw new EncodeException(format(
+                            "null table for direct counter '%s'", counterId));
+                }
+                final int p4TableId = browser.tables().getByName(tableId.id())
+                        .getPreamble().getId();
+                return Entity.newBuilder().setDirectCounterEntry(
+                        P4RuntimeOuterClass.DirectCounterEntry.newBuilder()
+                                .setTableEntry(
+                                        // Match unset to read all cells
+                                        P4RuntimeOuterClass.TableEntry.newBuilder()
+                                                .setTableId(p4TableId)
+                                                .build())
+                                .build())
+                        .build();
+            default:
+                throw new EncodeException(format(
+                        "unrecognized PI counter type '%s'", counterType));
+        }
+    }
+
+    private static PiCounterCellData decodeCounterEntity(Entity entity,
+                                                         PiPipeconf pipeconf,
+                                                         P4InfoBrowser browser)
             throws EncodeException, P4InfoBrowser.NotFoundException {
 
-        int counterId;
         CounterData counterData;
-
-        if (entity.getEntityCase() == COUNTER_ENTRY) {
-            counterId = entity.getCounterEntry().getCounterId();
-            counterData = entity.getCounterEntry().getData();
-        } else {
-            counterId = entity.getDirectCounterEntry().getCounterId();
-            counterData = entity.getDirectCounterEntry().getData();
-        }
-
-        // Process only counter IDs that were requested in the first place.
-        if (!counterIdMap.containsKey(counterId)) {
-            throw new EncodeException(format("Unrecognized counter ID '%s'", counterId));
-        }
-
-        PiCounterId piCounterId = counterIdMap.get(counterId);
-
-        if (!pipeconf.pipelineModel().counter(piCounterId).isPresent()) {
-            throw new EncodeException(format(
-                    "Unable to find counter '%s' in pipeline model", counterId));
-        }
-        PiCounterType piCounterType = pipeconf.pipelineModel().counter(piCounterId).get().counterType();
-
-        // Compute PI cell ID.
         PiCounterCellId piCellId;
 
-        switch (piCounterType) {
-            case INDIRECT:
-                if (entity.getEntityCase() != COUNTER_ENTRY) {
-                    throw new EncodeException(format(
-                            "Counter ID '%s' is indirect, but processed entity is %s",
-                            piCounterId, entity.getEntityCase()));
-                }
-                piCellId = PiCounterCellId.ofIndirect(piCounterId,
-                                                      entity.getCounterEntry().getIndex());
-                break;
-            case DIRECT:
-                if (entity.getEntityCase() != DIRECT_COUNTER_ENTRY) {
-                    throw new EncodeException(format(
-                            "Counter ID '%s' is direct, but processed entity is %s",
-                            piCounterId, entity.getEntityCase()));
-                }
-                PiTableEntry piTableEntry = TableEntryEncoder.decode(entity.getDirectCounterEntry().getTableEntry(),
-                                                                     pipeconf);
-                piCellId = PiCounterCellId.ofDirect(piCounterId, piTableEntry);
-                break;
-            default:
-                throw new EncodeException(format("Unrecognized PI counter ID type '%s'", piCounterType));
+        if (entity.getEntityCase() == COUNTER_ENTRY) {
+            String counterName = browser.counters()
+                    .getById(entity.getCounterEntry().getCounterId())
+                    .getPreamble()
+                    .getName();
+            piCellId = PiCounterCellId.ofIndirect(
+                    PiCounterId.of(counterName),
+                    entity.getCounterEntry().getIndex().getIndex());
+            counterData = entity.getCounterEntry().getData();
+        } else if (entity.getEntityCase() == DIRECT_COUNTER_ENTRY) {
+            PiTableEntry piTableEntry = TableEntryEncoder.decode(
+                    entity.getDirectCounterEntry().getTableEntry(), pipeconf);
+            piCellId = PiCounterCellId.ofDirect(piTableEntry);
+            counterData = entity.getDirectCounterEntry().getData();
+        } else {
+            throw new EncodeException(format(
+                    "Unrecognized entity type '%s' in P4Runtime message",
+                    entity.getEntityCase().name()));
         }
 
-        return new PiCounterCellData(piCellId, counterData.getPacketCount(), counterData.getByteCount());
+        return new PiCounterCellData(piCellId,
+                                     counterData.getPacketCount(),
+                                     counterData.getByteCount());
     }
 }
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java
index 9019613..fc60dcd 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/MeterEntryCodec.java
@@ -16,30 +16,35 @@
 
 package org.onosproject.p4runtime.ctl;
 
+import org.onosproject.net.pi.model.PiMeterId;
 import org.onosproject.net.pi.model.PiMeterType;
 import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.model.PiTableId;
 import org.onosproject.net.pi.runtime.PiMeterBand;
 import org.onosproject.net.pi.runtime.PiMeterCellConfig;
 import org.onosproject.net.pi.runtime.PiMeterCellId;
-import org.onosproject.net.pi.model.PiMeterId;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.slf4j.Logger;
-import p4.P4RuntimeOuterClass.MeterConfig;
-import p4.P4RuntimeOuterClass.MeterEntry;
+import p4.P4RuntimeOuterClass;
 import p4.P4RuntimeOuterClass.DirectMeterEntry;
 import p4.P4RuntimeOuterClass.Entity;
+import p4.P4RuntimeOuterClass.MeterConfig;
+import p4.P4RuntimeOuterClass.MeterEntry;
 
 import java.util.Collection;
-import java.util.Map;
+import java.util.Collections;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
 import static java.lang.String.format;
+import static org.onosproject.p4runtime.ctl.P4RuntimeUtils.indexMsg;
 import static org.slf4j.LoggerFactory.getLogger;
-import static p4.P4RuntimeOuterClass.Entity.EntityCase.*;
+import static p4.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_METER_ENTRY;
+import static p4.P4RuntimeOuterClass.Entity.EntityCase.METER_ENTRY;
 
 /**
- * Encoder/decoder of PI meter cell configurations to meter entry protobuf messages, and vice versa.
+ * Encoder/decoder of PI meter cell configurations to meter entry protobuf
+ * messages, and vice versa.
  */
 final class MeterEntryCodec {
 
@@ -50,27 +55,31 @@
     }
 
     /**
-     * Returns a collection of P4Runtime entity protobuf messages describing both meter or direct meter entries,
-     * encoded from the given collection of PI meter cell configurations, for the given pipeconf. If a PI meter cell
-     * configurations cannot be encoded, it is skipped, hence the returned collection might have different size than the
-     * input one.
-     * <p>
-     * This method takes as parameter also a map between numeric P4Info IDs and PI meter IDs, that will be populated
-     * during the process and that is then needed to aid in the decode process.
+     * Returns a collection of P4Runtime entity protobuf messages describing
+     * both meter or direct meter entries, encoded from the given collection of
+     * PI meter cell configurations, for the given pipeconf. If a PI meter cell
+     * configurations cannot be encoded, it is skipped, hence the returned
+     * collection might have different size than the input one.
      *
-     * @param cellConfigs  meter cell configurations
-     * @param meterIdMap   meter ID map (empty, it will be populated during this method execution)
-     * @param pipeconf     pipeconf
-     * @return collection of entity messages describing both meter or direct meter entries
+     * @param cellConfigs meter cell configurations
+     * @param pipeconf    pipeconf
+     * @return collection of entity messages describing both meter or direct
+     * meter entries
      */
     static Collection<Entity> encodePiMeterCellConfigs(Collection<PiMeterCellConfig> cellConfigs,
-                                                       Map<Integer, PiMeterId> meterIdMap,
                                                        PiPipeconf pipeconf) {
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
         return cellConfigs
                 .stream()
                 .map(cellConfig -> {
                     try {
-                        return encodePiMeterCellConfig(cellConfig, meterIdMap, pipeconf);
+                        return encodePiMeterCellConfig(cellConfig, pipeconf, browser);
                     } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
                         log.warn("Unable to encode PI meter cell id: {}", e.getMessage());
                         log.debug("exception", e);
@@ -82,26 +91,67 @@
     }
 
     /**
-     * Returns a collection of PI meter cell configurations, decoded from the given P4Runtime entity protobuf messages
-     * describing both meter or direct meter entries, for the given meter ID map (populated by {@link
-     * #encodePiMeterCellConfigs(Collection, Map, PiPipeconf)}), and pipeconf. If an entity message cannot be encoded,
-     * it is skipped, hence the returned collection might have different size than the input one.
+     * Returns a collection of P4Runtime entity protobuf messages to be used in
+     * requests to read all cells from the given meter identifiers. Works for
+     * both indirect or direct meters. If a PI meter identifier cannot be
+     * encoded, it is skipped, hence the returned collection might have
+     * different size than the input one.
      *
-     * @param entities     P4Runtime entity messages
-     * @param meterIdMap   meter ID map (previously populated)
-     * @param pipeconf     pipeconf
+     * @param meterIds meter identifiers
+     * @param pipeconf pipeconf
+     * @return collection of entity messages
+     */
+    static Collection<Entity> readAllCellsEntities(Collection<PiMeterId> meterIds,
+                                                   PiPipeconf pipeconf) {
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
+        return meterIds
+                .stream()
+                .map(meterId -> {
+                    try {
+                        return readAllCellsEntity(meterId, pipeconf, browser);
+                    } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+                        log.warn("Unable to encode meter ID to read-all-cells entity: {}",
+                                 e.getMessage());
+                        return null;
+                    }
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Returns a collection of PI meter cell configurations, decoded from the
+     * given P4Runtime entity protobuf messages describing both meter or direct
+     * meter entries, and pipeconf. If an entity message cannot be encoded, it
+     * is skipped, hence the returned collection might have different size than
+     * the input one.
+     *
+     * @param entities P4Runtime entity messages
+     * @param pipeconf pipeconf
      * @return collection of PI meter cell data
      */
     static Collection<PiMeterCellConfig> decodeMeterEntities(Collection<Entity> entities,
-                                                               Map<Integer, PiMeterId> meterIdMap,
-                                                               PiPipeconf pipeconf) {
+                                                             PiPipeconf pipeconf) {
+        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+        if (browser == null) {
+            log.error("Unable to get a P4Info browser for pipeconf {}", pipeconf.id());
+            return Collections.emptyList();
+        }
+
         return entities
                 .stream()
                 .filter(entity -> entity.getEntityCase() == METER_ENTRY ||
                         entity.getEntityCase() == DIRECT_METER_ENTRY)
                 .map(entity -> {
                     try {
-                        return decodeMeterEntity(entity, meterIdMap, pipeconf);
+                        return decodeMeterEntity(entity, pipeconf, browser);
                     } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
                         log.warn("Unable to decode meter entity message: {}", e.getMessage());
                         return null;
@@ -111,23 +161,23 @@
                 .collect(Collectors.toList());
     }
 
-    private static Entity encodePiMeterCellConfig(PiMeterCellConfig config, Map<Integer, PiMeterId> meterIdMap,
-                                                  PiPipeconf pipeconf)
+    private static Entity encodePiMeterCellConfig(PiMeterCellConfig config,
+                                                  PiPipeconf pipeconf,
+                                                  P4InfoBrowser browser)
             throws P4InfoBrowser.NotFoundException, EncodeException {
 
-        final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
-
         int meterId;
         Entity entity;
-        //The band with bigger burst is peak if rate of them is equal,
-        //if bands are not specificed, using default value(0).
-        long cir = 0;
-        long cburst = 0;
-        long pir = 0;
-        long pburst = 0;
-        PiMeterBand[] bands = config.meterBands().toArray(new PiMeterBand[config.meterBands().size()]);
+        MeterConfig meterConfig;
+
+        PiMeterBand[] bands = config.meterBands()
+                .toArray(new PiMeterBand[config.meterBands().size()]);
         if (bands.length == 2) {
-            if (bands[0].rate() > bands[1].rate()) {
+            long cir, cburst, pir, pburst;
+            // The band with bigger burst is peak if rate of them is equal.
+            if (bands[0].rate() > bands[1].rate() ||
+                    (bands[0].rate() == bands[1].rate() &&
+                            bands[0].burst() >= bands[1].burst())) {
                 cir = bands[1].rate();
                 cburst = bands[1].burst();
                 pir = bands[0].rate();
@@ -138,105 +188,133 @@
                 pir = bands[1].rate();
                 pburst = bands[1].burst();
             }
+            meterConfig = MeterConfig.newBuilder()
+                    .setCir(cir)
+                    .setCburst(cburst)
+                    .setPir(pir)
+                    .setPburst(pburst)
+                    .build();
+        } else if (bands.length == 0) {
+            // When reading meter cells.
+            meterConfig = null;
+        } else {
+            throw new EncodeException("number of meter bands should be either 2 or 0");
         }
 
-        // Encode PI cell ID into entity message and add to read request.
         switch (config.cellId().meterType()) {
             case INDIRECT:
-                meterId = browser.meters().getByName(config.cellId().meterId().id()).getPreamble().getId();
-                entity = Entity.newBuilder().setMeterEntry(MeterEntry
-                                                                   .newBuilder().setMeterId(meterId)
-                                                                   .setIndex(config.cellId().index())
-                                                                   .setConfig(MeterConfig.newBuilder()
-                                                                                      .setCir(cir)
-                                                                                      .setCburst(cburst)
-                                                                                      .setPir(pir)
-                                                                                      .setPburst(pburst)
-                                                                                      .build())
-                                                                   .build())
-                        .build();
+                meterId = browser.meters()
+                        .getByName(config.cellId().meterId().id())
+                        .getPreamble().getId();
+                MeterEntry.Builder indEntryBuilder = MeterEntry.newBuilder()
+                        .setMeterId(meterId)
+                        .setIndex(indexMsg(config.cellId().index()));
+                if (meterConfig != null) {
+                    indEntryBuilder.setConfig(meterConfig);
+                }
+                entity = Entity.newBuilder()
+                        .setMeterEntry(indEntryBuilder.build()).build();
                 break;
             case DIRECT:
-                meterId = browser.directMeters().getByName(config.cellId().meterId().id()).getPreamble().getId();
-                DirectMeterEntry.Builder entryBuilder = DirectMeterEntry.newBuilder()
-                        .setMeterId(meterId)
-                        .setConfig(MeterConfig.newBuilder()
-                                           .setCir(cir)
-                                           .setCburst(cburst)
-                                           .setPir(pir)
-                                           .setPburst(pburst)
-                                           .build());
-
-                if (!config.cellId().tableEntry().equals(PiTableEntry.EMTPY)) {
-                    entryBuilder.setTableEntry(TableEntryEncoder.encode(config.cellId().tableEntry(), pipeconf));
+                DirectMeterEntry.Builder dirEntryBuilder = DirectMeterEntry.newBuilder()
+                        .setTableEntry(TableEntryEncoder.encode(
+                                config.cellId().tableEntry(), pipeconf));
+                if (meterConfig != null) {
+                    dirEntryBuilder.setConfig(meterConfig);
                 }
-                entity = Entity.newBuilder().setDirectMeterEntry(entryBuilder.build()).build();
+                entity = Entity.newBuilder()
+                        .setDirectMeterEntry(dirEntryBuilder.build()).build();
                 break;
             default:
-                throw new EncodeException(format("Unrecognized PI meter cell ID type '%s'",
+                throw new EncodeException(format("unrecognized PI meter type '%s'",
                                                  config.cellId().meterType()));
         }
-        meterIdMap.put(meterId, config.cellId().meterId());
 
         return entity;
     }
 
-    private static PiMeterCellConfig decodeMeterEntity(Entity entity, Map<Integer, PiMeterId> meterIdMap,
-                                                         PiPipeconf pipeconf)
+    private static Entity readAllCellsEntity(PiMeterId meterId,
+                                             PiPipeconf pipeconf,
+                                             P4InfoBrowser browser)
+            throws P4InfoBrowser.NotFoundException, EncodeException {
+
+        if (!pipeconf.pipelineModel().meter(meterId).isPresent()) {
+            throw new EncodeException(format(
+                    "not such meter '%s' in pipeline model", meterId));
+        }
+        final PiMeterType meterType = pipeconf.pipelineModel()
+                .meter(meterId).get().meterType();
+
+        switch (meterType) {
+            case INDIRECT:
+                final int p4InfoMeterId = browser.meters()
+                        .getByName(meterId.id())
+                        .getPreamble().getId();
+                return Entity.newBuilder().setMeterEntry(
+                        P4RuntimeOuterClass.MeterEntry.newBuilder()
+                                // Index unset to read all cells
+                                .setMeterId(p4InfoMeterId)
+                                .build())
+                        .build();
+            case DIRECT:
+                final PiTableId tableId = pipeconf.pipelineModel()
+                        .meter(meterId).get().table();
+                if (tableId == null) {
+                    throw new EncodeException(format(
+                            "null table for direct meter '%s'", meterId));
+                }
+                final int p4TableId = browser.tables().getByName(tableId.id())
+                        .getPreamble().getId();
+                return Entity.newBuilder().setDirectMeterEntry(
+                        P4RuntimeOuterClass.DirectMeterEntry.newBuilder()
+                                .setTableEntry(
+                                        // Match unset to read all cells
+                                        P4RuntimeOuterClass.TableEntry.newBuilder()
+                                                .setTableId(p4TableId)
+                                                .build())
+                                .build())
+                        .build();
+            default:
+                throw new EncodeException(format(
+                        "unrecognized PI meter type '%s'", meterType));
+        }
+    }
+
+    private static PiMeterCellConfig decodeMeterEntity(Entity entity,
+                                                       PiPipeconf pipeconf,
+                                                       P4InfoBrowser browser)
             throws EncodeException, P4InfoBrowser.NotFoundException {
 
-        int meterId;
         MeterConfig meterConfig;
-
-        if (entity.getEntityCase() == METER_ENTRY) {
-            meterId = entity.getMeterEntry().getMeterId();
-            meterConfig = entity.getMeterEntry().getConfig();
-        } else {
-            meterId = entity.getDirectMeterEntry().getMeterId();
-            meterConfig = entity.getDirectMeterEntry().getConfig();
-        }
-
-        // Process only meter IDs that were requested in the first place.
-        if (!meterIdMap.containsKey(meterId)) {
-            throw new EncodeException(format("Unrecognized meter ID '%s'", meterId));
-        }
-
-        PiMeterId piMeterId = meterIdMap.get(meterId);
-        if (!pipeconf.pipelineModel().meter(piMeterId).isPresent()) {
-            throw new EncodeException(format("Unable to find meter '%s' in pipeline model",  meterId));
-        }
-
-        PiMeterType piMeterType = pipeconf.pipelineModel().meter(piMeterId).get().meterType();
-        // Compute PI cell ID.
         PiMeterCellId piCellId;
 
-        switch (piMeterType) {
-            case INDIRECT:
-                if (entity.getEntityCase() != METER_ENTRY) {
-                    throw new EncodeException(format(
-                            "Meter ID '%s' is indirect, but processed entity is %s",
-                            piMeterId, entity.getEntityCase()));
-                }
-                piCellId = PiMeterCellId.ofIndirect(piMeterId, entity.getMeterEntry().getIndex());
-                break;
-            case DIRECT:
-                if (entity.getEntityCase() != DIRECT_METER_ENTRY) {
-                    throw new EncodeException(format(
-                            "Meter ID '%s' is direct, but processed entity is %s",
-                            piMeterId, entity.getEntityCase()));
-                }
-                PiTableEntry piTableEntry = TableEntryEncoder.decode(entity.getDirectMeterEntry().getTableEntry(),
-                                                                     pipeconf);
-                piCellId = PiMeterCellId.ofDirect(piMeterId, piTableEntry);
-                break;
-            default:
-                throw new EncodeException(format("Unrecognized PI meter ID type '%s'", piMeterType));
+        if (entity.getEntityCase() == METER_ENTRY) {
+            String meterName = browser.meters()
+                    .getById(entity.getCounterEntry().getCounterId())
+                    .getPreamble()
+                    .getName();
+            piCellId = PiMeterCellId.ofIndirect(
+                    PiMeterId.of(meterName),
+                    entity.getMeterEntry().getIndex().getIndex());
+            meterConfig = entity.getMeterEntry().getConfig();
+        } else if (entity.getEntityCase() == DIRECT_METER_ENTRY) {
+            PiTableEntry piTableEntry = TableEntryEncoder.decode(
+                    entity.getDirectMeterEntry().getTableEntry(),
+                    pipeconf);
+            piCellId = PiMeterCellId.ofDirect(piTableEntry);
+            meterConfig = entity.getDirectMeterEntry().getConfig();
+        } else {
+            throw new EncodeException(format(
+                    "unrecognized entity type '%s' in P4Runtime message",
+                    entity.getEntityCase().name()));
         }
 
-        PiMeterCellConfig.Builder builder = PiMeterCellConfig.builder();
-        builder.withMeterBand(new PiMeterBand(meterConfig.getCir(), meterConfig.getCburst()));
-        builder.withMeterBand(new PiMeterBand(meterConfig.getPir(), meterConfig.getPburst()));
-
-        return builder.withMeterCellId(piCellId).build();
+        return PiMeterCellConfig.builder()
+                .withMeterCellId(piCellId)
+                .withMeterBand(new PiMeterBand(meterConfig.getCir(),
+                                               meterConfig.getCburst()))
+                .withMeterBand(new PiMeterBand(meterConfig.getPir(),
+                                               meterConfig.getPburst()))
+                .build();
     }
-}
\ No newline at end of file
+}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
index 609c5c5..e8b4e21 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
@@ -21,7 +21,6 @@
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
 import com.google.protobuf.ByteString;
 import com.google.protobuf.InvalidProtocolBufferException;
 import io.grpc.Context;
@@ -36,7 +35,7 @@
 import org.onosproject.net.MastershipRole;
 import org.onosproject.net.pi.model.PiActionProfileId;
 import org.onosproject.net.pi.model.PiCounterId;
-import org.onosproject.net.pi.model.PiCounterType;
+import org.onosproject.net.pi.model.PiMeterId;
 import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.model.PiTableId;
 import org.onosproject.net.pi.runtime.PiActionGroup;
@@ -46,8 +45,6 @@
 import org.onosproject.net.pi.runtime.PiEntity;
 import org.onosproject.net.pi.runtime.PiMeterCellConfig;
 import org.onosproject.net.pi.runtime.PiMeterCellId;
-import org.onosproject.net.pi.model.PiMeterType;
-import org.onosproject.net.pi.model.PiMeterId;
 import org.onosproject.net.pi.runtime.PiPacketOperation;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.onosproject.net.pi.service.PiPipeconfService;
@@ -131,8 +128,6 @@
     private Map<Uint128, CompletableFuture<Boolean>> arbitrationUpdateMap = Maps.newConcurrentMap();
     protected Uint128 p4RuntimeElectionId;
 
-    private static final long DEFAULT_INDEX = 0;
-
     /**
      * Default constructor.
      *
@@ -219,39 +214,8 @@
     @Override
     public CompletableFuture<Collection<PiCounterCellData>> readAllCounterCells(Set<PiCounterId> counterIds,
                                                                                 PiPipeconf pipeconf) {
-
-        /*
-        From p4runtime.proto, the scope of a ReadRequest is defined as follows:
-        CounterEntry:
-            - All counter cells for all meters if counter_id = 0 (default).
-            - All counter cells for given counter_id if index = 0 (default).
-        DirectCounterEntry:
-            - All counter cells for all meters if counter_id = 0 (default).
-            - All counter cells for given counter_id if table_entry.match is empty.
-         */
-
-        Set<PiCounterCellId> cellIds = Sets.newHashSet();
-
-        for (PiCounterId counterId : counterIds) {
-            if (!pipeconf.pipelineModel().counter(counterId).isPresent()) {
-                log.warn("Unable to find counter '{}' in pipeline model",  counterId);
-                continue;
-            }
-            PiCounterType counterType = pipeconf.pipelineModel().counter(counterId).get().counterType();
-            switch (counterType) {
-                case INDIRECT:
-                    cellIds.add(PiCounterCellId.ofIndirect(counterId, 0));
-                    break;
-                case DIRECT:
-                    cellIds.add(PiCounterCellId.ofDirect(counterId, PiTableEntry.EMTPY));
-                    break;
-                default:
-                    log.warn("Unrecognized PI counter type '{}'", counterType);
-            }
-        }
-
-        return supplyInContext(() -> doReadCounterCells(cellIds, pipeconf),
-                               "readAllCounterCells-" + cellIds.hashCode());
+        return supplyInContext(() -> doReadAllCounterCells(counterIds, pipeconf),
+                               "readAllCounterCells-" + counterIds.hashCode());
     }
 
     @Override
@@ -297,34 +261,8 @@
     @Override
     public CompletableFuture<Collection<PiMeterCellConfig>> readAllMeterCells(Set<PiMeterId> meterIds,
                                                                                 PiPipeconf pipeconf) {
-
-        /*
-        From p4runtime.proto, the scope of a ReadRequest is defined as follows:
-        MeterEntry:
-            - All meter cells for all meters if meter_id = 0 (default).
-            - All meter cells for given meter_id if index = 0 (default).
-        DirectCounterEntry:
-            - All meter cells for all meters if meter_id = 0 (default).
-            - All meter cells for given meter_id if table_entry.match is empty.
-         */
-
-        Set<PiMeterCellId> cellIds = Sets.newHashSet();
-        for (PiMeterId meterId : meterIds) {
-            PiMeterType meterType = pipeconf.pipelineModel().meter(meterId).get().meterType();
-            switch (meterType) {
-                case INDIRECT:
-                    cellIds.add(PiMeterCellId.ofIndirect(meterId, DEFAULT_INDEX));
-                    break;
-                case DIRECT:
-                    cellIds.add(PiMeterCellId.ofDirect(meterId, PiTableEntry.EMTPY));
-                    break;
-                default:
-                    log.warn("Unrecognized PI meter type '{}'", meterType);
-            }
-        }
-
-        return supplyInContext(() -> doReadMeterCells(cellIds, pipeconf),
-                               "readAllMeterCells-" + cellIds.hashCode());
+        return supplyInContext(() -> doReadAllMeterCells(meterIds, pipeconf),
+                               "readAllMeterCells-" + meterIds.hashCode());
     }
 
     /* Blocking method implementations below */
@@ -416,7 +354,7 @@
             return true;
         }
 
-        Collection<Update> updateMsgs = null;
+        Collection<Update> updateMsgs;
         try {
             updateMsgs = TableEntryEncoder.encode(piTableEntries, pipeconf)
                     .stream()
@@ -566,20 +504,32 @@
         controller.postEvent(event);
     }
 
-    private Collection<PiCounterCellData> doReadCounterCells(Collection<PiCounterCellId> cellIds, PiPipeconf pipeconf) {
+    private Collection<PiCounterCellData> doReadAllCounterCells(
+            Collection<PiCounterId> counterIds, PiPipeconf pipeconf) {
+        return doReadCounterEntities(
+                CounterEntryCodec.readAllCellsEntities(counterIds, pipeconf),
+                pipeconf);
+    }
 
-        // We use this map to remember the original PI counter IDs of the returned response.
-        final Map<Integer, PiCounterId> counterIdMap = Maps.newHashMap();
+    private Collection<PiCounterCellData> doReadCounterCells(
+            Collection<PiCounterCellId> cellIds, PiPipeconf pipeconf) {
+        return doReadCounterEntities(
+                CounterEntryCodec.encodePiCounterCellIds(cellIds, pipeconf),
+                pipeconf);
+    }
+
+    private Collection<PiCounterCellData> doReadCounterEntities(
+            Collection<Entity> counterEntities, PiPipeconf pipeconf) {
+
+        if (counterEntities.size() == 0) {
+            return Collections.emptyList();
+        }
 
         final ReadRequest request = ReadRequest.newBuilder()
                 .setDeviceId(p4DeviceId)
-                .addAllEntities(CounterEntryCodec.encodePiCounterCellIds(cellIds, counterIdMap, pipeconf))
+                .addAllEntities(counterEntities)
                 .build();
 
-        if (request.getEntitiesList().size() == 0) {
-            return Collections.emptyList();
-        }
-
         final Iterable<ReadResponse> responses;
         try {
             responses = () -> blockingStub.read(request);
@@ -593,7 +543,7 @@
                 .flatMap(List::stream)
                 .collect(Collectors.toList());
 
-        return CounterEntryCodec.decodeCounterEntities(entities, counterIdMap, pipeconf);
+        return CounterEntryCodec.decodeCounterEntities(entities, pipeconf);
     }
 
     private boolean doWriteActionGroupMembers(PiActionGroup group, WriteOperationType opType, PiPipeconf pipeconf) {
@@ -789,47 +739,60 @@
         }
     }
 
-    private Collection<PiMeterCellConfig> doReadMeterCells(Collection<PiMeterCellId> cellIds, PiPipeconf pipeconf) {
+    private Collection<PiMeterCellConfig> doReadAllMeterCells(
+            Collection<PiMeterId> meterIds, PiPipeconf pipeconf) {
+        return doReadMeterEntities(MeterEntryCodec.readAllCellsEntities(
+                meterIds, pipeconf), pipeconf);
+    }
 
-        // We use this map to remember the original PI meter IDs of the returned response.
-        Map<Integer, PiMeterId> meterIdMap = Maps.newHashMap();
-        Collection<PiMeterCellConfig> piMeterCellConfigs = cellIds.stream()
+    private Collection<PiMeterCellConfig> doReadMeterCells(
+            Collection<PiMeterCellId> cellIds, PiPipeconf pipeconf) {
+
+        final Collection<PiMeterCellConfig> piMeterCellConfigs = cellIds.stream()
                 .map(cellId -> PiMeterCellConfig.builder()
-                        .withMeterCellId(cellId).build())
+                        .withMeterCellId(cellId)
+                        .build())
                 .collect(Collectors.toList());
 
+        return doReadMeterEntities(MeterEntryCodec.encodePiMeterCellConfigs(
+                piMeterCellConfigs, pipeconf), pipeconf);
+    }
+
+    private Collection<PiMeterCellConfig> doReadMeterEntities(
+            Collection<Entity> entitiesToRead, PiPipeconf pipeconf) {
+
+        if (entitiesToRead.size() == 0) {
+            return Collections.emptyList();
+        }
+
         final ReadRequest request = ReadRequest.newBuilder()
                 .setDeviceId(p4DeviceId)
-                .addAllEntities(MeterEntryCodec.encodePiMeterCellConfigs(piMeterCellConfigs, meterIdMap, pipeconf))
+                .addAllEntities(entitiesToRead)
                 .build();
 
-        if (request.getEntitiesList().size() == 0) {
-            return Collections.emptyList();
-        }
-
         final Iterable<ReadResponse> responses;
         try {
             responses = () -> blockingStub.read(request);
         } catch (StatusRuntimeException e) {
-            log.warn("Unable to read meters config: {}", e.getMessage());
+            log.warn("Unable to read meter cells: {}", e.getMessage());
             log.debug("exception", e);
             return Collections.emptyList();
         }
 
-        List<Entity> entities = StreamSupport.stream(responses.spliterator(), false)
+        List<Entity> responseEntities = StreamSupport
+                .stream(responses.spliterator(), false)
                 .map(ReadResponse::getEntitiesList)
                 .flatMap(List::stream)
                 .collect(Collectors.toList());
 
-        return MeterEntryCodec.decodeMeterEntities(entities, meterIdMap, pipeconf);
+        return MeterEntryCodec.decodeMeterEntities(responseEntities, pipeconf);
     }
 
     private boolean doWriteMeterCells(Collection<PiMeterCellConfig> cellIds, PiPipeconf pipeconf) {
 
-        final Map<Integer, PiMeterId> meterIdMap = Maps.newHashMap();
         WriteRequest.Builder writeRequestBuilder = WriteRequest.newBuilder();
 
-        Collection<Update> updateMsgs = MeterEntryCodec.encodePiMeterCellConfigs(cellIds, meterIdMap, pipeconf)
+        Collection<Update> updateMsgs = MeterEntryCodec.encodePiMeterCellConfigs(cellIds, pipeconf)
                 .stream()
                 .map(meterEntryMsg ->
                              Update.newBuilder()
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java
index 411e1fb..25ef153 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeUtils.java
@@ -17,6 +17,7 @@
 package org.onosproject.p4runtime.ctl;
 
 import com.google.protobuf.ByteString;
+import p4.P4RuntimeOuterClass;
 
 import static java.lang.String.format;
 
@@ -48,4 +49,8 @@
                     entityDescr, bitWidth, prefixLength));
         }
     }
+
+    static P4RuntimeOuterClass.Index indexMsg(long index) {
+        return P4RuntimeOuterClass.Index.newBuilder().setIndex(index).build();
+    }
 }