Snap for 7080740 from 68a365af6dc8419d6fbd0dbb7a3d9e55dbf9b826 to mainline-networkstack-release

Change-Id: Id23f1a09c061aa4fecfd2761f693a5610b4a8de4
diff --git a/apps/test/chqts/src/busy_startup/busy_startup.cc b/apps/test/chqts/src/busy_startup/busy_startup.cc
index cfd8137..96623fe 100644
--- a/apps/test/chqts/src/busy_startup/busy_startup.cc
+++ b/apps/test/chqts/src/busy_startup/busy_startup.cc
@@ -146,7 +146,8 @@
       checkTimerEvent(intData);
     } else if (eventType == CHRE_EVENT_SENSOR_ACCELEROMETER_DATA) {
       checkSensorEvent(eventData);
-    } else if (eventType == CHRE_EVENT_SENSOR_SAMPLING_CHANGE) {
+    } else if (eventType == CHRE_EVENT_SENSOR_SAMPLING_CHANGE ||
+               eventType == CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO) {
       // This could have been generated when we configured the
       // sensor.  We just ignore it.
     } else {
diff --git a/apps/test/chqts/src/general_test/basic_sensor_test_base.cc b/apps/test/chqts/src/general_test/basic_sensor_test_base.cc
index 4253866..9371a8f 100644
--- a/apps/test/chqts/src/general_test/basic_sensor_test_base.cc
+++ b/apps/test/chqts/src/general_test/basic_sensor_test_base.cc
@@ -163,9 +163,6 @@
 void BasicSensorTestBase::startTest() {
   mState = State::kPreConfigure;
   if (!chreSensorFindDefault(getSensorType(), &mSensorHandle)) {
-    if (isRequiredSensor()) {
-      sendFatalFailureToHost("Sensor is required, but no default found.");
-    }
     sendStringToHost(MessageType::kSkipped,
                      "No default sensor found for optional sensor.");
     return;
@@ -369,6 +366,8 @@
     expectedSensorType = CHRE_SENSOR_TYPE_GYROSCOPE;
   } else if (eventType == CHRE_EVENT_SENSOR_GEOMAGNETIC_FIELD_BIAS_INFO) {
     expectedSensorType = CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD;
+  } else if (eventType == CHRE_EVENT_SENSOR_ACCELEROMETER_BIAS_INFO) {
+    expectedSensorType = CHRE_SENSOR_TYPE_ACCELEROMETER;
   } else {
     sendInternalFailureToHost("Illegal eventType in handleBiasEvent", &eType);
   }
diff --git a/apps/test/chqts/src/general_test/basic_sensor_test_base.h b/apps/test/chqts/src/general_test/basic_sensor_test_base.h
index 1df3059..5e69aa7 100644
--- a/apps/test/chqts/src/general_test/basic_sensor_test_base.h
+++ b/apps/test/chqts/src/general_test/basic_sensor_test_base.h
@@ -49,16 +49,6 @@
   virtual uint8_t getSensorType() const = 0;
 
   /**
-   * Abstract method indicating if this sensor is required for a valid
-   * CHRE implementation.
-   *
-   * A CHRE isn't useful without certain sensors available.
-   *
-   * @returns true if this is sensor is required; false otherwise.
-   */
-  virtual bool isRequiredSensor() const = 0;
-
-  /**
    * Abstract method indicating if this is an on-change sensor.
    *
    * @returns true if this sensor is on-change; false otherwise.
diff --git a/apps/test/chqts/src/general_test/basic_sensor_tests.h b/apps/test/chqts/src/general_test/basic_sensor_tests.h
index 7638eff..8fd8257 100644
--- a/apps/test/chqts/src/general_test/basic_sensor_tests.h
+++ b/apps/test/chqts/src/general_test/basic_sensor_tests.h
@@ -31,9 +31,6 @@
   uint8_t getSensorType() const override {
     return CHRE_SENSOR_TYPE_ACCELEROMETER;
   }
-  bool isRequiredSensor() const override {
-    return true;
-  }
   bool isOnChangeSensor() const override {
     return false;
   }
@@ -51,9 +48,6 @@
   uint8_t getSensorType() const override {
     return CHRE_SENSOR_TYPE_INSTANT_MOTION_DETECT;
   }
-  bool isRequiredSensor() const override {
-    return true;
-  }
   bool isOnChangeSensor() const override {
     return false;
   }
@@ -71,9 +65,6 @@
   uint8_t getSensorType() const override {
     return CHRE_SENSOR_TYPE_STATIONARY_DETECT;
   }
-  bool isRequiredSensor() const override {
-    return true;
-  }
   bool isOnChangeSensor() const override {
     return false;
   }
@@ -91,9 +82,6 @@
   uint8_t getSensorType() const override {
     return CHRE_SENSOR_TYPE_GYROSCOPE;
   }
-  bool isRequiredSensor() const override {
-    return true;
-  }
   bool isOnChangeSensor() const override {
     return false;
   }
@@ -111,9 +99,6 @@
   uint8_t getSensorType() const override {
     return CHRE_SENSOR_TYPE_GEOMAGNETIC_FIELD;
   }
-  bool isRequiredSensor() const override {
-    return false;
-  }
   bool isOnChangeSensor() const override {
     return false;
   }
@@ -131,9 +116,6 @@
   uint8_t getSensorType() const override {
     return CHRE_SENSOR_TYPE_PRESSURE;
   }
-  bool isRequiredSensor() const override {
-    return false;
-  }
   bool isOnChangeSensor() const override {
     return false;
   }
@@ -151,9 +133,6 @@
   uint8_t getSensorType() const override {
     return CHRE_SENSOR_TYPE_LIGHT;
   }
-  bool isRequiredSensor() const override {
-    return false;
-  }
   bool isOnChangeSensor() const override {
     return true;
   }
@@ -171,9 +150,6 @@
   uint8_t getSensorType() const override {
     return CHRE_SENSOR_TYPE_PROXIMITY;
   }
-  bool isRequiredSensor() const override {
-    return false;
-  }
   bool isOnChangeSensor() const override {
     return true;
   }
diff --git a/apps/test/common/chre_cross_validator_sensor/inc/chre_cross_validator_sensor_manager.h b/apps/test/common/chre_cross_validator_sensor/inc/chre_cross_validator_sensor_manager.h
index 6b2229c..2b87162 100644
--- a/apps/test/common/chre_cross_validator_sensor/inc/chre_cross_validator_sensor_manager.h
+++ b/apps/test/common/chre_cross_validator_sensor/inc/chre_cross_validator_sensor_manager.h
@@ -207,9 +207,20 @@
   /**
    * Handle a start message from CHRE with the given data from host.
    *
+   * @param hostEndpoint The host endpoint the data was sent from.
    * @param hostData The data from host that has a start message.
    */
-  void handleStartMessage(const chreMessageFromHostData *hostData);
+  void handleStartMessage(uint16_t hostEndpoint,
+                          const chreMessageFromHostData *hostData);
+
+  /**
+   * Handle an info message from CHRE with the given data from host.
+   *
+   * @param hostEndpoint The host endpoint the data was sent from.
+   * @param hostData The data from host that has a start message.
+   */
+  void handleInfoMessage(uint16_t hostEndpoint,
+                         const chreMessageFromHostData *hostData);
 
   /**
    * Handle a message from the host.
@@ -276,11 +287,33 @@
   void handleProximityData(const chreSensorByteData *proximityDataFromChre);
 
   /**
-   * Encode and send data to be validated to host.
+   * Send data to be validated to the host.
    *
-   * @param data The data to encode and send.
+   * @param data The data to send.
    */
-  void encodeAndSendDataToHost(const chre_cross_validation_sensor_Data &data);
+  void sendDataToHost(const chre_cross_validation_sensor_Data &data);
+
+  /**
+   * Encode and send the info response to the host.
+   *
+   * @param hostEndpoint The endpoint to send the response to.
+   * @param infoResponse The info response to be encoded and sent.
+   */
+  void sendInfoResponse(
+      uint16_t hostEndpoint,
+      const chre_cross_validation_sensor_SensorInfoResponse &infoResponse);
+
+  /**
+   * Sends the provided message to the host.
+   *
+   * @param hostEndpoint The endpoint to send the message to.
+   * @param messageType The type of message being sent to the host.
+   * @param fields The fields of the provided struct that should be encoded.
+   * @param srcStruct The struct that should be encoded prior to sending to the
+   *     host.
+   */
+  void sendMessageToHost(uint16_t hostEndpoint, uint16_t messageType,
+                         const pb_field_t fields[], const void *srcStruct);
 
   /**
    * Determine if nanoapp is ready to process new sensor data.
diff --git a/apps/test/common/chre_cross_validator_sensor/src/chre_cross_validator_sensor_manager.cc b/apps/test/common/chre_cross_validator_sensor/src/chre_cross_validator_sensor_manager.cc
index 4d8be7a..02fa263 100644
--- a/apps/test/common/chre_cross_validator_sensor/src/chre_cross_validator_sensor_manager.cc
+++ b/apps/test/common/chre_cross_validator_sensor/src/chre_cross_validator_sensor_manager.cc
@@ -300,14 +300,9 @@
   return header.readingCount > 0 && isTimestampValid;
 }
 
-void Manager::handleStartMessage(const chreMessageFromHostData *hostData) {
+void Manager::handleStartMessage(uint16_t hostEndpoint,
+                                 const chreMessageFromHostData *hostData) {
   bool success = true;
-  uint16_t hostEndpoint;
-  if (hostData->hostEndpoint != CHRE_HOST_ENDPOINT_UNSPECIFIED) {
-    hostEndpoint = hostData->hostEndpoint;
-  } else {
-    hostEndpoint = CHRE_HOST_ENDPOINT_BROADCAST;
-  }
   // Default values for everything but hostEndpoint param
   mCrossValidatorState = CrossValidatorState(CrossValidatorType::SENSOR, 0, 0,
                                              0, hostEndpoint, false);
@@ -335,14 +330,48 @@
   }
 }
 
+void Manager::handleInfoMessage(uint16_t hostEndpoint,
+                                const chreMessageFromHostData *hostData) {
+  chre_cross_validation_sensor_SensorInfoResponse infoResponse =
+      chre_cross_validation_sensor_SensorInfoResponse_init_default;
+  pb_istream_t istream = pb_istream_from_buffer(
+      static_cast<const pb_byte_t *>(hostData->message), hostData->messageSize);
+  chre_cross_validation_sensor_SensorInfoCommand infoCommand =
+      chre_cross_validation_sensor_SensorInfoCommand_init_default;
+  if (!pb_decode(&istream,
+                 chre_cross_validation_sensor_SensorInfoCommand_fields,
+                 &infoCommand)) {
+    LOGE("Could not decode info command");
+  } else {
+    uint32_t handle;
+    infoResponse.has_chreSensorType = true;
+    infoResponse.chreSensorType = infoCommand.chreSensorType;
+    infoResponse.has_isAvailable = true;
+    infoResponse.isAvailable =
+        chreSensorFindDefault(infoResponse.chreSensorType, &handle);
+  }
+
+  sendInfoResponse(hostEndpoint, infoResponse);
+}
+
 void Manager::handleMessageFromHost(uint32_t senderInstanceId,
                                     const chreMessageFromHostData *hostData) {
   if (senderInstanceId != CHRE_INSTANCE_ID) {
     LOGE("Incorrect sender instance id: %" PRIu32, senderInstanceId);
   } else {
+    uint16_t hostEndpoint;
+    if (hostData->hostEndpoint != CHRE_HOST_ENDPOINT_UNSPECIFIED) {
+      hostEndpoint = hostData->hostEndpoint;
+    } else {
+      hostEndpoint = CHRE_HOST_ENDPOINT_BROADCAST;
+    }
+
     switch (hostData->messageType) {
       case chre_cross_validation_sensor_MessageType_CHRE_CROSS_VALIDATION_START:
-        handleStartMessage(hostData);
+        handleStartMessage(hostEndpoint, hostData);
+        break;
+      case chre_cross_validation_sensor_MessageType_CHRE_CROSS_VALIDATION_INFO:
+        handleInfoMessage(hostEndpoint, hostData);
         break;
       default:
         LOGE("Unknown message type %" PRIu32 " for host message",
@@ -416,7 +445,7 @@
   if (processSensorData(threeAxisDataFromChre->header, sensorType)) {
     chre_cross_validation_sensor_Data newData =
         makeSensorThreeAxisData(threeAxisDataFromChre, sensorType);
-    encodeAndSendDataToHost(newData);
+    sendDataToHost(newData);
   }
 }
 
@@ -425,7 +454,7 @@
   if (processSensorData(floatDataFromChre->header, sensorType)) {
     chre_cross_validation_sensor_Data newData =
         makeSensorFloatData(floatDataFromChre, sensorType);
-    encodeAndSendDataToHost(newData);
+    sendDataToHost(newData);
   }
 }
 
@@ -435,30 +464,43 @@
                         CHRE_SENSOR_TYPE_PROXIMITY)) {
     chre_cross_validation_sensor_Data newData =
         makeSensorProximityData(proximityDataFromChre);
-    encodeAndSendDataToHost(newData);
+    sendDataToHost(newData);
   }
 }
 
-void Manager::encodeAndSendDataToHost(
-    const chre_cross_validation_sensor_Data &data) {
+void Manager::sendDataToHost(const chre_cross_validation_sensor_Data &data) {
+  sendMessageToHost(
+      mCrossValidatorState->hostEndpoint,
+      chre_cross_validation_sensor_MessageType_CHRE_CROSS_VALIDATION_DATA,
+      chre_cross_validation_sensor_Data_fields, &data);
+}
+
+void Manager::sendInfoResponse(
+    uint16_t hostEndpoint,
+    const chre_cross_validation_sensor_SensorInfoResponse &infoResponse) {
+  sendMessageToHost(
+      hostEndpoint,
+      chre_cross_validation_sensor_MessageType_CHRE_CROSS_VALIDATION_INFO_RESPONSE,
+      chre_cross_validation_sensor_SensorInfoResponse_fields, &infoResponse);
+}
+
+void Manager::sendMessageToHost(uint16_t hostEndpoint, uint16_t messageType,
+                                const pb_field_t fields[],
+                                const void *srcStruct) {
   size_t encodedSize;
-  if (!pb_get_encoded_size(&encodedSize,
-                           chre_cross_validation_sensor_Data_fields, &data)) {
-    LOGE("Could not get encoded size of data proto message");
+  if (!pb_get_encoded_size(&encodedSize, fields, srcStruct)) {
+    LOGE("Could not get encoded size of proto message");
   } else {
     pb_byte_t *buffer = static_cast<pb_byte_t *>(chreHeapAlloc(encodedSize));
     if (buffer == nullptr) {
       LOG_OOM();
     } else {
       pb_ostream_t ostream = pb_ostream_from_buffer(buffer, encodedSize);
-      if (!pb_encode(&ostream, chre_cross_validation_sensor_Data_fields,
-                     &data)) {
-        LOGE("Could not encode data proto message");
-      } else if (
-          !chreSendMessageToHostEndpoint(
-              static_cast<void *>(buffer), encodedSize,
-              chre_cross_validation_sensor_MessageType_CHRE_CROSS_VALIDATION_DATA,
-              mCrossValidatorState->hostEndpoint, heapFreeMessageCallback)) {
+      if (!pb_encode(&ostream, fields, srcStruct)) {
+        LOGE("Could not encode proto message");
+      } else if (!chreSendMessageToHostEndpoint(
+                     static_cast<void *>(buffer), encodedSize, messageType,
+                     hostEndpoint, heapFreeMessageCallback)) {
         LOGE("Could not send message to host");
       }
     }
diff --git a/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h b/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h
index e1d5c27..0ca0a7e 100644
--- a/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h
+++ b/apps/test/common/chre_cross_validator_wifi/inc/chre_cross_validator_wifi_manager.h
@@ -107,13 +107,22 @@
       bool success, const char *errMessage = nullptr);
 
   /**
+   * @param capabilitiesFromChre The number with flags that represent the
+   *        different wifi capabilities.
+   * @return The wifi capabilities proto message for the host.
+   */
+  chre_cross_validation_wifi_WifiCapabilities makeWifiCapabilitiesMessage(
+      uint32_t capabilitiesFromChre);
+
+  /**
    * Encode the proto message and send to host.
    *
    * @param message The proto message struct pointer.
-   * @fields The fields descriptor of the proto message to encode.
+   * @param fields The fields descriptor of the proto message to encode.
+   * @param messageType The message type of the message.
    */
-  void encodeAndSendMessageToHost(const void *message,
-                                  const pb_field_t *fields);
+  void encodeAndSendMessageToHost(const void *message, const pb_field_t *fields,
+                                  uint32_t messageType);
   /**
    * Handle a wifi scan result data message sent from AP.
    *
diff --git a/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc b/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc
index a57768d..c23efed 100644
--- a/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc
+++ b/apps/test/common/chre_cross_validator_wifi/src/chre_cross_validator_wifi_manager.cc
@@ -19,6 +19,7 @@
 #include <cinttypes>
 #include <cstring>
 
+#include "chre/util/nanoapp/assert.h"
 #include "chre/util/nanoapp/callbacks.h"
 #include "chre/util/nanoapp/log.h"
 #include "chre_cross_validation_wifi.nanopb.h"
@@ -89,24 +90,35 @@
 
 void Manager::handleStepStartMessage(
     chre_cross_validation_wifi_StepStartCommand stepStartCommand) {
-  chre_test_common_TestResult testResult;
   switch (stepStartCommand.step) {
     case chre_cross_validation_wifi_Step_INIT:
-      testResult = makeTestResultProtoMessage(
-          false, "Received StepStartCommand for INIT step");
+      LOGE("Received StepStartCommand for INIT step");
+      CHRE_ASSERT(false);
       break;
-    case chre_cross_validation_wifi_Step_SETUP:
+    case chre_cross_validation_wifi_Step_CAPABILITIES: {
+      chre_cross_validation_wifi_WifiCapabilities wifiCapabilities =
+          makeWifiCapabilitiesMessage(chreWifiGetCapabilities());
+      encodeAndSendMessageToHost(
+          static_cast<void *>(&wifiCapabilities),
+          chre_cross_validation_wifi_WifiCapabilities_fields,
+          chre_cross_validation_wifi_MessageType_WIFI_CAPABILITIES);
+      break;
+    }
+    case chre_cross_validation_wifi_Step_SETUP: {
       if (!chreWifiConfigureScanMonitorAsync(true /* enable */,
                                              &kScanMonitoringCookie)) {
         LOGE("chreWifiConfigureScanMonitorAsync() failed");
-        testResult =
+        chre_test_common_TestResult testResult =
             makeTestResultProtoMessage(false, "setupWifiScanMonitoring failed");
-        encodeAndSendMessageToHost(static_cast<void *>(&testResult),
-                                   chre_test_common_TestResult_fields);
+        encodeAndSendMessageToHost(
+            static_cast<void *>(&testResult),
+            chre_test_common_TestResult_fields,
+            chre_cross_validation_wifi_MessageType_STEP_RESULT);
       } else {
         LOGD("chreWifiConfigureScanMonitorAsync() succeeded");
       }
       break;
+    }
     case chre_cross_validation_wifi_Step_VALIDATE:
       LOGE("start message received in VALIDATE phase");
       break;
@@ -174,8 +186,10 @@
         }
       }
     }
-    encodeAndSendMessageToHost(static_cast<const void *>(&testResult),
-                               chre_test_common_TestResult_fields);
+    encodeAndSendMessageToHost(
+        static_cast<const void *>(&testResult),
+        chre_test_common_TestResult_fields,
+        chre_cross_validation_wifi_MessageType_STEP_RESULT);
     chreHeapFree(errMsg);
   }
 }
@@ -208,8 +222,17 @@
   return testResult;
 }
 
+chre_cross_validation_wifi_WifiCapabilities
+Manager::makeWifiCapabilitiesMessage(uint32_t capabilitiesFromChre) {
+  chre_cross_validation_wifi_WifiCapabilities capabilities;
+  capabilities.has_wifiCapabilities = true;
+  capabilities.wifiCapabilities = capabilitiesFromChre;
+  return capabilities;
+}
+
 void Manager::encodeAndSendMessageToHost(const void *message,
-                                         const pb_field_t *fields) {
+                                         const pb_field_t *fields,
+                                         uint32_t messageType) {
   size_t encodedSize;
   if (!pb_get_encoded_size(&encodedSize, fields, message)) {
     LOGE("Could not get encoded size of test result message");
@@ -222,8 +245,7 @@
       if (!pb_encode(&ostream, fields, message)) {
         LOGE("Could not encode data proto message");
       } else if (!chreSendMessageToHostEndpoint(
-                     static_cast<void *>(buffer), encodedSize,
-                     chre_cross_validation_wifi_MessageType_STEP_RESULT,
+                     static_cast<void *>(buffer), encodedSize, messageType,
                      mCrossValidatorState.hostEndpoint,
                      heapFreeMessageCallback)) {
         LOGE("Could not send message to host");
@@ -254,8 +276,9 @@
     testResult = makeTestResultProtoMessage(
         false, "Unknown chre async result type received");
   }
-  encodeAndSendMessageToHost(static_cast<void *>(&testResult),
-                             chre_test_common_TestResult_fields);
+  encodeAndSendMessageToHost(
+      static_cast<void *>(&testResult), chre_test_common_TestResult_fields,
+      chre_cross_validation_wifi_MessageType_STEP_RESULT);
 }
 
 }  // namespace cross_validator_wifi
diff --git a/apps/test/common/proto/chre_cross_validation_sensor.proto b/apps/test/common/proto/chre_cross_validation_sensor.proto
index a7db174..94b9cde 100644
--- a/apps/test/common/proto/chre_cross_validation_sensor.proto
+++ b/apps/test/common/proto/chre_cross_validation_sensor.proto
@@ -18,6 +18,14 @@
   // was received from a CHRE API.
   // Payload must be Data message
   CHRE_CROSS_VALIDATION_DATA = 2;
+
+  // H2C: Host asking nanoapp for information about a given sensor.
+  // Payload must be SensorInfoCommand message.
+  CHRE_CROSS_VALIDATION_INFO = 3;
+
+  // C2H: Response to a information request for a sensor.
+  // Payload must be a SensorInfoResponse message.
+  CHRE_CROSS_VALIDATION_INFO_RESPONSE = 4;
 }
 
 message StartCommand {
@@ -37,6 +45,21 @@
   optional bool isContinuous = 4;
 }
 
+/*
+ * Asks for the nanoapp to provide stats about the provided CHRE sensor type.
+ */
+message SensorInfoCommand {
+  optional uint32 chreSensorType = 1;
+}
+
+/*
+ * Response to a SensorInfoCommand containing data about the requested sensor.
+ */
+message SensorInfoResponse {
+  optional uint32 chreSensorType = 1;
+  optional bool isAvailable = 2;
+}
+
 message Data {
   oneof data {
     SensorData sensorData = 1;
diff --git a/apps/test/common/proto/chre_cross_validation_wifi.proto b/apps/test/common/proto/chre_cross_validation_wifi.proto
index 8a400f4..d2fe7e0 100644
--- a/apps/test/common/proto/chre_cross_validation_wifi.proto
+++ b/apps/test/common/proto/chre_cross_validation_wifi.proto
@@ -22,6 +22,10 @@
   // H2C: Host passing down wifi scan result data to CHRE to validate.
   // There may be multiple messages with this type sent.
   SCAN_RESULT = 3;
+
+  // C2H: Nanoapp informing host about the wifi capabilities it has.
+  // The payload must be a WifiCapabilities message.
+  WIFI_CAPABILITIES = 4;
 }
 
 enum Step {
@@ -31,6 +35,8 @@
   SETUP = 1;
   // The validate step where the data is gathered and compared.
   VALIDATE = 2;
+  // The step where the wifi capabilities are gathered from the nanoapp.
+  CAPABILITIES = 3;
 }
 
 message StepStartCommand {
@@ -54,3 +60,11 @@
   // [0 - totalNumResults)
   optional uint32 resultIndex = 4;
 }
+
+/*
+ * The wifi capabilities listed in
+ * //system/chre/chre_api/include/chre_api/chre/wifi.h
+ */
+message WifiCapabilities {
+  optional uint32 wifiCapabilities = 1;
+}
diff --git a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorSensor.java b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorSensor.java
index eb35dda..4f8db72 100644
--- a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorSensor.java
+++ b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorSensor.java
@@ -37,10 +37,11 @@
 import org.junit.Assert;
 import org.junit.Assume;
 
-import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 public class ChreCrossValidatorSensor
@@ -65,11 +66,10 @@
 
     private static final long AWAIT_DATA_TIMEOUT_CONTINUOUS_IN_MS = 5000;
     private static final long AWAIT_DATA_TIMEOUT_ON_CHANGE_ONE_SHOT_IN_MS = 1000;
+    private static final long INFO_RESPONSE_TIMEOUT_MS = 1000;
 
     private static final long DEFAULT_SAMPLING_INTERVAL_IN_MS = 20;
 
-    // TODO(b/146052784): May need to account for differences in sampling rate and latency from
-    // AP side vs CHRE side
     private static final long SAMPLING_LATENCY_IN_MS = 0;
 
     private static final long MAX_TIMESTAMP_DIFF_NS = 10000000L;
@@ -86,6 +86,7 @@
     private Sensor mSensor;
 
     private long mSamplingIntervalInMs;
+    private boolean mChreSensorFound;
 
     private CrossValidatorSensorConfig mSensorConfig;
 
@@ -131,6 +132,7 @@
 
     @Override
     public void validate() throws AssertionError {
+        verifyChreSensorIsPresent();
         collectDataFromAp();
         collectDataFromChre();
         waitForDataSampling();
@@ -156,8 +158,59 @@
                 mNappBinary.getNanoAppId(), messageType, startCommand.toByteArray());
     }
 
+    /**
+    * @return The nanoapp message used to retrieve info of a given CHRE sensor.
+    */
+    private NanoAppMessage makeInfoCommandMessage() {
+        int messageType = ChreCrossValidationSensor.MessageType.CHRE_CROSS_VALIDATION_INFO_VALUE;
+        ChreCrossValidationSensor.SensorInfoCommand infoCommand =
+                ChreCrossValidationSensor.SensorInfoCommand.newBuilder()
+                .setChreSensorType(getChreSensorType())
+                .build();
+        return NanoAppMessage.createMessageToNanoApp(
+                mNappBinary.getNanoAppId(), messageType, infoCommand.toByteArray());
+    }
+
     @Override
     protected void parseDataFromNanoAppMessage(NanoAppMessage message) {
+        if (message.getMessageType()
+                == ChreCrossValidationSensor.MessageType
+                        .CHRE_CROSS_VALIDATION_INFO_RESPONSE_VALUE) {
+            parseInfoResponseFromNanoappMessage(message);
+        } else if (message.getMessageType()
+                == ChreCrossValidationSensor.MessageType.CHRE_CROSS_VALIDATION_DATA_VALUE) {
+            parseSensorDataFromNanoappMessage(message);
+        } else {
+            Assert.fail("Received invalid message type from nanoapp " + message.getMessageType());
+        }
+    }
+
+    private void parseInfoResponseFromNanoappMessage(NanoAppMessage message) {
+        ChreCrossValidationSensor.SensorInfoResponse infoProto;
+        try {
+            infoProto = ChreCrossValidationSensor.SensorInfoResponse.parseFrom(
+                    message.getMessageBody());
+        } catch (InvalidProtocolBufferException e) {
+            setErrorStr("Error parsing protobuf: " + e);
+            return;
+        }
+        if (!infoProto.hasChreSensorType() || !infoProto.hasIsAvailable()) {
+            setErrorStr("Info response message isn't completely filled in");
+            return;
+        }
+
+        int apSensorType = chreToApSensorType(infoProto.getChreSensorType());
+        if (!isSensorTypeCurrent(apSensorType)) {
+            setErrorStr(String.format("Incorrect sensor type %d when expecting %d",
+                    apSensorType, mSensor.getType()));
+            return;
+        }
+
+        mChreSensorFound = infoProto.getIsAvailable();
+        mAwaitDataLatch.countDown();
+    }
+
+    private void parseSensorDataFromNanoappMessage(NanoAppMessage message) {
         final String kParseDataErrorPrefix = "While parsing data from nanoapp: ";
         ChreCrossValidationSensor.Data dataProto;
         try {
@@ -201,8 +254,6 @@
         Assert.assertTrue("Did not find any AP datapoints", mApDatapointsArray.length > 0);
         alignApAndChreDatapoints();
         // AP and CHRE datapoints will be same size
-        // TODO(b/146052784): Ensure that CHRE data is the same sampling rate as AP data for
-        // comparison
         for (int i = 0; i < mApDatapointsArray.length; i++) {
             assertSensorDatapointsSimilar(
                     (ApSensorDatapoint) mApDatapointsArray[i],
@@ -309,10 +360,14 @@
     /**
     * Start collecting data from CHRE
     */
-    private void collectDataFromChre() throws AssertionError {
+    private void collectDataFromChre() {
         // The info in the start message will inform the nanoapp of which type of
         // data to collect (accel, gyro, gnss, wifi, etc).
-        int result = mContextHubClient.sendMessageToNanoApp(makeStartNanoAppMessage());
+        sendMessageToNanoApp(makeStartNanoAppMessage());
+    }
+
+    private void sendMessageToNanoApp(NanoAppMessage message) {
+        int result = mContextHubClient.sendMessageToNanoApp(message);
         if (result != ContextHubTransaction.RESULT_SUCCESS) {
             Assert.fail("Collect data from CHRE failed with result "
                     + contextHubTransactionResultToString(result)
@@ -341,35 +396,35 @@
     }
 
     /*
-    * Align the AP and CHRE datapoints by finding the first pair that are similar comparing
-    * linearly from there. Also truncate the end if one list has more datapoints than the other
-    * after this. This is needed because AP and CHRE can start sending data and varying times to
-    * this validator and can also stop sending at various times.
+    * Align the AP and CHRE datapoints by finding all the timestamps that match up and discarding
+    * the rest of the datapoints.
     */
     private void alignApAndChreDatapoints() throws AssertionError {
-        int matchAp = 0, matchChre = 0;
-        int shorterDpLength = Math.min(mApDatapointsArray.length, mChreDatapointsArray.length);
-        if (mApDatapointsArray[0].timestamp < mChreDatapointsArray[0].timestamp) {
-            matchChre = 0;
-            matchAp = indexOfFirstClosestDatapoint((SensorDatapoint[]) mApDatapointsArray,
-                                                   mChreDatapointsArray[0]);
-        } else {
-            matchAp = 0;
-            matchChre = indexOfFirstClosestDatapoint((SensorDatapoint[]) mChreDatapointsArray,
-                                                     mApDatapointsArray[0]);
+        ArrayList<ApSensorDatapoint> newApSensorDatapoints = new ArrayList<ApSensorDatapoint>();
+        ArrayList<ChreSensorDatapoint> newChreSensorDatapoints =
+                new ArrayList<ChreSensorDatapoint>();
+        int apI = 0;
+        int chreI = 0;
+        while (apI < mApDatapointsArray.length && chreI < mChreDatapointsArray.length) {
+            ApSensorDatapoint apDp = mApDatapointsArray[apI];
+            ChreSensorDatapoint chreDp = mChreDatapointsArray[chreI];
+            if (datapointTimestampsAreSimilar(apDp, chreDp)) {
+                newApSensorDatapoints.add(apDp);
+                newChreSensorDatapoints.add(chreDp);
+                apI++;
+                chreI++;
+            } else if (apDp.timestamp < chreDp.timestamp) {
+                apI++;
+            } else {
+                chreI++;
+            }
         }
+        // TODO(b/175795665): Assert that an acceptable amount of datapoints pass the alignment
+        // phase.
         Assert.assertTrue("Did not find matching timestamps to align AP and CHRE datapoints.",
-                          (matchAp != -1 && matchChre != -1));
-        // Remove extraneous datapoints before matching datapoints
-        int apStartI = matchAp;
-        int chreStartI = matchChre;
-        int newApLength = mApDatapointsArray.length - apStartI;
-        int newChreLength = mChreDatapointsArray.length - chreStartI;
-        int minLength = Math.min(newApLength, newChreLength);
-        int chreEndI = chreStartI + minLength;
-        int apEndI = apStartI + minLength;
-        mApDatapointsArray = Arrays.copyOfRange(mApDatapointsArray, apStartI, apEndI);
-        mChreDatapointsArray = Arrays.copyOfRange(mChreDatapointsArray, chreStartI, chreEndI);
+                          !(newApSensorDatapoints.isEmpty() || newChreSensorDatapoints.isEmpty()));
+        mApDatapointsArray = newApSensorDatapoints.toArray(new ApSensorDatapoint[0]);
+        mChreDatapointsArray = newChreSensorDatapoints.toArray(new ChreSensorDatapoint[0]);
     }
 
     /**
@@ -402,10 +457,6 @@
                 String.format("AP and CHRE three axis datapoint values differ on index %d", index)
                 + "\nAP data -> " + apDp + "\nCHRE data -> "
                 + chreDp;
-        String timestampsAssertMsg =
-                String.format("AP and CHRE three axis timestamp values differ on index %d", index)
-                + "\nAP data -> " + apDp + "\nCHRE data -> "
-                + chreDp;
 
         // TODO(b/146052784): Log full list of datapoints to file on disk on assertion failure
         // so that there is more insight into the problem then just logging the one pair of
@@ -413,23 +464,6 @@
         Assert.assertTrue(datapointsAssertMsg,
                 datapointValuesAreSimilar(
                 apDp, chreDp, mSensorConfig.errorMargin));
-        Assert.assertTrue(timestampsAssertMsg,
-                datapointTimestampsAreSimilar(apDp, chreDp));
-    }
-
-    /**
-     * @param datapoints Array of dataoints to compare timestamps to laterDp
-     * @param laterDp SensorDatapoint whose timestamp will be compared to the datapoints in array
-     *    to find the first pair that match.
-     */
-    private int indexOfFirstClosestDatapoint(SensorDatapoint[] sensorDatapoints,
-                                             SensorDatapoint laterDp) {
-        for (int i = 0; i < sensorDatapoints.length; i++) {
-            if (datapointTimestampsAreSimilar(sensorDatapoints[i], laterDp)) {
-                return i;
-            }
-        }
-        return -1;
     }
 
     /**
@@ -448,6 +482,38 @@
         return AP_TO_CHRE_SENSOR_TYPE.get(mSensor.getType());
     }
 
+    /**
+     * Verify the CHRE sensor being evaluated is present on this device.
+     */
+    private void verifyChreSensorIsPresent() {
+        mCollectingData.set(true);
+        sendMessageToNanoApp(makeInfoCommandMessage());
+        waitForInfoResponse();
+        mCollectingData.set(false);
+        // All CHRE sensors are optional so skip this test if the required sensor isn't found.
+        Assume.assumeTrue(mChreSensorFound);
+    }
+
+    private void waitForInfoResponse() {
+        boolean success = false;
+        try {
+            success = mAwaitDataLatch.await(INFO_RESPONSE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Assert.fail("await data latch interrupted");
+        }
+
+        if (!success) {
+            Assert.fail("Timed out waiting for sensor info response");
+        }
+
+        if (mErrorStr.get() != null) {
+            Assert.fail(mErrorStr.get());
+        }
+
+        // Reset latch for use in waiting for sensor data.
+        mAwaitDataLatch = new CountDownLatch(1);
+    }
+
     private boolean sensorIsContinuous() {
         return mSensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS;
     }
diff --git a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorWifi.java b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorWifi.java
index bd08e81..a938288 100644
--- a/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorWifi.java
+++ b/java/test/cross_validation/src/com/google/android/chre/test/crossvalidator/ChreCrossValidatorWifi.java
@@ -38,6 +38,7 @@
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import org.junit.Assert;
+import org.junit.Assume;
 
 import java.math.BigInteger;
 import java.util.List;
@@ -52,6 +53,13 @@
 
     private static final long NANO_APP_ID = 0x476f6f6754000005L;
 
+    /**
+     * Wifi capabilities flags listed in
+     * //system/chre/chre_api/include/chre_api/chre/wifi.h
+     */
+    private static final int WIFI_CAPABILITIES_SCAN_MONITORING = 1;
+    private static final int WIFI_CAPABILITIES_ON_DEMAND_SCAN = 2;
+
     AtomicReference<Step> mStep = new AtomicReference<Step>(Step.INIT);
     AtomicBoolean mDidReceiveNanoAppMessage = new AtomicBoolean(false);
 
@@ -61,6 +69,9 @@
     private WifiManager mWifiManager;
     private BroadcastReceiver mWifiScanReceiver;
 
+    private AtomicReference<ChreCrossValidationWifi.WifiCapabilities> mWifiCapabilities =
+            new AtomicReference<ChreCrossValidationWifi.WifiCapabilities>(null);
+
     private AtomicBoolean mWifiScanResultsCompareFinalResult = new AtomicBoolean(false);
     private AtomicReference<String> mWifiScanResultsCompareFinalErrorMessage =
             new AtomicReference<String>(null);
@@ -90,17 +101,27 @@
     }
 
     @Override public void validate() throws AssertionError {
-        mStep.set(Step.SETUP);
-        sendStepStartMessage(Step.SETUP);
-        waitForStepResult();
+        mCollectingData.set(true);
+        sendStepStartMessage(Step.CAPABILITIES);
+        waitForMessageFromNanoapp();
+        mCollectingData.set(false);
+        Assume.assumeTrue("Chre wifi is not enabled",
+                          chreWifiHasCapabilities(mWifiCapabilities.get()));
 
+        mCollectingData.set(true);
+        sendStepStartMessage(Step.SETUP);
+        waitForMessageFromNanoapp();
+        mCollectingData.set(false);
+
+        mCollectingData.set(true);
         sendStepStartMessage(Step.VALIDATE);
 
         Assert.assertTrue("Wifi manager start scan failed", mWifiManager.startScan());
         waitForApScanResults();
         sendWifiScanResultsToChre();
 
-        waitForStepResult();
+        waitForMessageFromNanoapp();
+        mCollectingData.set(false);
     }
 
 
@@ -108,7 +129,7 @@
      * Send step start message to nanoapp.
      */
     private void sendStepStartMessage(Step step) {
-        mStep.set(Step.VALIDATE);
+        mStep.set(step);
         sendMessageToNanoApp(makeStepStartMessage(step));
     }
 
@@ -138,16 +159,14 @@
     }
 
     /**
-     * Wait for setup message from CHRE or CHRE_ERROR
+     * Wait for a messaage from the nanoapp.
      */
-    private void waitForStepResult() {
-        mCollectingData.set(true);
+    private void waitForMessageFromNanoapp() {
         try {
             mAwaitDataLatch.await(AWAIT_STEP_RESULT_MESSAGE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
         } catch (InterruptedException e) {
             Assert.fail("Interrupted while awaiting " + getCurrentStepName() + " step");
         }
-        mCollectingData.set(false);
         mAwaitDataLatch = new CountDownLatch(1);
         Assert.assertTrue("Timed out while waiting for step result in " + getCurrentStepName()
                 + " step", mDidReceiveNanoAppMessage.get());
@@ -157,6 +176,15 @@
         }
     }
 
+    /**
+     * @param capabilities The wifi capabilities message from CHRE.
+     * @return true if CHRE wifi has the necessary capabilities to run the test.
+     */
+    private boolean chreWifiHasCapabilities(ChreCrossValidationWifi.WifiCapabilities capabilities) {
+        return (capabilities.getWifiCapabilities() & WIFI_CAPABILITIES_SCAN_MONITORING) != 0
+            && (capabilities.getWifiCapabilities() & WIFI_CAPABILITIES_ON_DEMAND_SCAN) != 0;
+    }
+
     private void waitForApScanResults() {
         try {
             mAwaitApWifiSetupScan.await(AWAIT_WIFI_SCAN_RESULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
@@ -186,34 +214,48 @@
         return message;
     }
 
-
     @Override
     protected void parseDataFromNanoAppMessage(NanoAppMessage message) {
         mDidReceiveNanoAppMessage.set(true);
         if (message.getMessageType()
-                != ChreCrossValidationWifi.MessageType.STEP_RESULT_VALUE) {
-            setErrorStr(String.format("Received message with message type %d when expecting %d",
-                        message.getMessageType(),
-                        ChreCrossValidationWifi.MessageType.STEP_RESULT_VALUE));
-        }
-        ChreTestCommon.TestResult testResult = null;
-        try {
-            testResult = ChreTestCommon.TestResult.parseFrom(message.getMessageBody());
-        } catch (InvalidProtocolBufferException e) {
-            setErrorStr("Error parsing protobuff: " + e);
-            mAwaitDataLatch.countDown();
-            return;
-        }
-        boolean success = getSuccessFromTestResult(testResult);
-        if (mStep.get() == Step.SETUP || mStep.get() == Step.VALIDATE) {
-            if (success) {
-                Log.i(TAG, getCurrentStepName() + " step success");
-            } else {
-                setErrorStr(getCurrentStepName() + " step failed: "
-                        + testResult.getErrorMessage().toStringUtf8());
+                == ChreCrossValidationWifi.MessageType.STEP_RESULT_VALUE) {
+            ChreTestCommon.TestResult testResult = null;
+            try {
+                testResult = ChreTestCommon.TestResult.parseFrom(message.getMessageBody());
+            } catch (InvalidProtocolBufferException e) {
+                setErrorStr("Error parsing protobuff: " + e);
+                mAwaitDataLatch.countDown();
+                return;
             }
-        } else { // mStep.get() == Step.INIT
-            setErrorStr("Received a step result message when no phase set yet.");
+            boolean success = getSuccessFromTestResult(testResult);
+            if (mStep.get() == Step.SETUP || mStep.get() == Step.VALIDATE) {
+                if (success) {
+                    Log.i(TAG, getCurrentStepName() + " step success");
+                } else {
+                    setErrorStr(getCurrentStepName() + " step failed: "
+                            + testResult.getErrorMessage().toStringUtf8());
+                }
+            } else {
+                setErrorStr("Received a step result message during step " + getCurrentStepName());
+            }
+        } else if (message.getMessageType()
+                == ChreCrossValidationWifi.MessageType.WIFI_CAPABILITIES_VALUE) {
+            if (mStep.get() != Step.CAPABILITIES) {
+                setErrorStr("Received a capabilities message during step " + getCurrentStepName());
+            }
+            ChreCrossValidationWifi.WifiCapabilities capabilities = null;
+            try {
+                capabilities = ChreCrossValidationWifi.WifiCapabilities.parseFrom(
+                        message.getMessageBody());
+            } catch (InvalidProtocolBufferException e) {
+                setErrorStr("Error parsing protobuff: " + e);
+                mAwaitDataLatch.countDown();
+                return;
+            }
+            mWifiCapabilities.set(capabilities);
+        } else {
+            setErrorStr(String.format("Received message with unexpected type: %d",
+                                      message.getMessageType()));
         }
         // Each message should countdown the latch no matter success or fail
         mAwaitDataLatch.countDown();