Move MotionPredictor config to an XML file alongside the model.

Test: atest libinput_tests
Fixes: 266747937
Change-Id: Ic5ec548d2edc8bad5e8b88aaf8511cd297a89275
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 9ac1829..8a17d8a 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -215,6 +215,7 @@
         "libcutils",
         "liblog",
         "libPlatformProperties",
+        "libtinyxml2",
         "libvintf",
     ],
 
@@ -271,6 +272,7 @@
 
             required: [
                 "motion_predictor_model_prebuilt",
+                "motion_predictor_model_config",
             ],
         },
         host: {
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index 3037573..947a956 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -36,9 +36,6 @@
 namespace android {
 namespace {
 
-const int64_t PREDICTION_INTERVAL_NANOS =
-        12500000 / 3; // TODO(b/266747937): Get this from the model.
-
 /**
  * Log debug messages about predictions.
  * Enable this via "adb shell setprop log.tag.MotionPredictor DEBUG"
@@ -189,7 +186,7 @@
         // TODO(b/266747654): Stop predictions if predicted pressure is < some threshold.
         coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]);
 
-        predictionTime += PREDICTION_INTERVAL_NANOS;
+        predictionTime += mModel->predictionInterval();
         if (i == 0) {
             hasPredictions = true;
             prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(),
@@ -208,7 +205,6 @@
         axisFrom = axisTo;
         axisTo = point;
     }
-    // TODO(b/266747511): Interpolate to futureTime?
     if (!hasPredictions) {
         return nullptr;
     }
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index 85fa176..9f4aaa8 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -36,6 +36,7 @@
 #define ATRACE_TAG ATRACE_TAG_INPUT
 #include <cutils/trace.h>
 #include <log/log.h>
+#include <utils/Timers.h>
 
 #include "tensorflow/lite/core/api/error_reporter.h"
 #include "tensorflow/lite/core/api/op_resolver.h"
@@ -44,6 +45,8 @@
 #include "tensorflow/lite/model.h"
 #include "tensorflow/lite/mutable_op_resolver.h"
 
+#include "tinyxml2.h"
+
 namespace android {
 namespace {
 
@@ -72,16 +75,31 @@
 
 std::string getModelPath() {
 #if defined(__ANDROID__)
-    static const char* oemModel = "/vendor/etc/motion_predictor_model.fb";
+    static const char* oemModel = "/vendor/etc/motion_predictor_model.tflite";
     if (fileExists(oemModel)) {
         return oemModel;
     }
-    return "/system/etc/motion_predictor_model.fb";
+    return "/system/etc/motion_predictor_model.tflite";
 #else
-    return base::GetExecutableDirectory() + "/motion_predictor_model.fb";
+    return base::GetExecutableDirectory() + "/motion_predictor_model.tflite";
 #endif
 }
 
+std::string getConfigPath() {
+    // The config file should be alongside the model file.
+    return base::Dirname(getModelPath()) + "/motion_predictor_config.xml";
+}
+
+int64_t parseXMLInt64(const tinyxml2::XMLElement& configRoot, const char* elementName) {
+    const tinyxml2::XMLElement* element = configRoot.FirstChildElement(elementName);
+    LOG_ALWAYS_FATAL_IF(!element, "Could not find '%s' element", elementName);
+
+    int64_t value = 0;
+    LOG_ALWAYS_FATAL_IF(element->QueryInt64Text(&value) != tinyxml2::XML_SUCCESS,
+                        "Failed to parse %s: %s", elementName, element->GetText());
+    return value;
+}
+
 // A TFLite ErrorReporter that logs to logcat.
 class LoggingErrorReporter : public tflite::ErrorReporter {
 public:
@@ -246,13 +264,23 @@
         PLOG(FATAL) << "Failed to mmap model";
     }
 
+    const std::string configPath = getConfigPath();
+    tinyxml2::XMLDocument configDocument;
+    LOG_ALWAYS_FATAL_IF(configDocument.LoadFile(configPath.c_str()) != tinyxml2::XML_SUCCESS,
+                        "Failed to load config file from %s", configPath.c_str());
+
+    // Parse configuration file.
+    const tinyxml2::XMLElement* configRoot = configDocument.FirstChildElement("motion-predictor");
+    LOG_ALWAYS_FATAL_IF(!configRoot);
+    const nsecs_t predictionInterval = parseXMLInt64(*configRoot, "prediction-interval");
+
     return std::unique_ptr<TfLiteMotionPredictorModel>(
-            new TfLiteMotionPredictorModel(std::move(modelBuffer)));
+            new TfLiteMotionPredictorModel(std::move(modelBuffer), predictionInterval));
 }
 
 TfLiteMotionPredictorModel::TfLiteMotionPredictorModel(
-        std::unique_ptr<android::base::MappedFile> model)
-      : mFlatBuffer(std::move(model)) {
+        std::unique_ptr<android::base::MappedFile> model, nsecs_t predictionInterval)
+      : mFlatBuffer(std::move(model)), mPredictionInterval(predictionInterval) {
     CHECK(mFlatBuffer);
     mErrorReporter = std::make_unique<LoggingErrorReporter>();
     mModel = tflite::FlatBufferModel::VerifyAndBuildFromBuffer(mFlatBuffer->data(),
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 6aae25d..cadac88 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -64,12 +64,13 @@
         "libcutils",
         "liblog",
         "libPlatformProperties",
+        "libtinyxml2",
         "libutils",
         "libvintf",
     ],
     data: [
         "data/*",
-        ":motion_predictor_model.fb",
+        ":motion_predictor_model",
     ],
     test_options: {
         unit_test: true,