QSPA AIDL hal implementation

Change-Id: I5f486ab2ac9bb088274beec44bb963e962ad0827
diff --git a/qspaservice/Android.bp b/qspaservice/Android.bp
new file mode 100644
index 0000000..42af180
--- /dev/null
+++ b/qspaservice/Android.bp
@@ -0,0 +1,17 @@
+cc_binary{
+    name: "vendor.qti.qspa-service",
+    init_rc: ["vendor.qti.qspa-service.rc"],
+    srcs:[
+        "src/main.cpp",
+        "src/Qspa.cpp"
+    ],
+    vendor:true,
+    vintf_fragments: ["vendor.qti.qspa-service.xml"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libcutils",
+        "libbinder_ndk",
+        "vendor.qti.hardware.qspa-V1-ndk",
+   ],
+}
diff --git a/qspaservice/src/Qspa.cpp b/qspaservice/src/Qspa.cpp
new file mode 100644
index 0000000..919bc03
--- /dev/null
+++ b/qspaservice/src/Qspa.cpp
@@ -0,0 +1,249 @@
+/*
+  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+  * SPDX-License-Identifier: BSD-3-Clause-Clear
+*/
+
+#include "Qspa.h"
+#include <fstream>
+#include <regex>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <algorithm>
+
+using namespace std;
+
+namespace aidl {
+namespace vendor {
+namespace qti {
+namespace hardware {
+namespace qspa {
+
+ndk::ScopedAStatus Qspa::get_all_subparts(std::vector<PartInfo>* _aidl_return) {
+    ALOGI("Request received for api: get_all_subparts");
+    vector<PartInfo> partinfo_return;
+    for (int i = 0; i < defective_parts.size(); i++) {
+        string subpart_path(PART_INFO_PATH);
+        subpart_path.append(defective_parts[i]);
+        fstream fin;
+        fin.open(subpart_path, ios::in);
+        string line;
+        if (!fin) {
+            ALOGE("No device path found for Subpart- %s", defective_parts[i].c_str());
+            continue;
+        }
+        if (!getline(fin, line)) {
+            ALOGE("Unable to read file: %s", subpart_path.c_str());
+            return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+        }
+        ALOGI("part is: %s", defective_parts[i].c_str());
+        stringstream data(line);
+        string intermediate;
+        std::vector<int32_t> tokens;
+        PartInfo partinfo;
+        partinfo.part = defective_parts[i];
+        // Tokenizing w.r.t ','
+        while (getline(data, intermediate, ',')) {
+            ALOGI("data is %s", intermediate.c_str());
+            tokens.push_back(stoi(intermediate,0,16));
+        }
+        partinfo.subpart_data = tokens;
+        partinfo_return.push_back(partinfo);
+    }
+    *_aidl_return = partinfo_return;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Qspa::get_available_parts(std::vector<std::string>* _aidl_return) {
+    ALOGI("Request received for api: get_available_parts");
+    vector<std::string> parts;
+    for (int i = 0; i < defective_parts.size(); i++) {
+        string subpart_path(PART_INFO_PATH);
+        subpart_path.append(defective_parts[i]);
+        fstream fin;
+        fin.open(subpart_path, ios::in);
+        string line;
+        if (!fin) {
+            ALOGE("No device path found for Subpart- %s", defective_parts[i].c_str());
+            continue;
+        }
+        if (!getline(fin, line)) {
+            ALOGE("Unable to read file: %s", subpart_path.c_str());
+            return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+        }
+        ALOGI("part is: %s", defective_parts[i].c_str());
+        parts.push_back(defective_parts[i].c_str());
+    }
+    *_aidl_return = parts;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Qspa::get_subpart_info(const std::string& in_part,
+        std::vector<int32_t>* _aidl_return) {
+    ALOGI("Request received for api: get_subpart_info(%s)", in_part.c_str());
+    string subpart_path(PART_INFO_PATH);
+    subpart_path.append(in_part);
+    fstream fin;
+    fin.open(subpart_path, ios::in);
+    string line;
+    if (!fin || !getline(fin, line)) {
+        ALOGE("Unable to open or read file: %s", subpart_path.c_str());
+        return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+    }
+    stringstream data(line);
+    string intermediate;
+    std::vector<int32_t> tokens;
+    // Tokenizing w.r.t ','
+    while (getline(data, intermediate, ',')) {
+        ALOGI("data is %s", intermediate.c_str());
+        tokens.push_back(stoi(intermediate,0,16));
+    }
+    *_aidl_return = tokens;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Qspa::get_num_available_cpus(int32_t* _aidl_return) {
+    ALOGI("Request received for api: get_num_available_cpus");
+    fstream fin;
+    fin.open(CPU_INFO_PATH, ios::in);
+    string line;
+    if (!fin || !getline(fin, line)) {
+        ALOGE("Unable to open or read file: %s", CPU_INFO_PATH);
+        return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+    }
+    ALOGI("cpus Range: %s",line.c_str());
+    int32_t cpu = 0;
+    cpu = stoi(std::regex_replace(line, std::regex("0-"), ""));
+    ALOGI("Number of cpus: %d",cpu+1);
+    *_aidl_return = cpu+1;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Qspa::get_num_available_clusters(int32_t* _aidl_return) {
+    ALOGI("Request received for api: get_num_available_clusters");
+    DIR *clusters_dir = opendir(CLUSTER_INFO_PATH);
+    struct dirent *clusters_entry;
+    if (clusters_dir == NULL) {
+        ALOGE("Could not open directory: %s", CLUSTER_INFO_PATH);
+        return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+    }
+    int32_t clusters = 0;
+    while ((clusters_entry = readdir(clusters_dir))) {
+        string policyname = clusters_entry -> d_name;
+        regex str_expr ("policy[0-9]*");
+        if (std::regex_match(policyname.begin(), policyname.end(), str_expr)) {
+            ALOGI("policyname is %s",policyname.c_str());
+            clusters++;
+        }
+    }
+    ALOGI("Number of clusters: %d",clusters);
+    *_aidl_return = clusters;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Qspa::get_num_physical_clusters(int32_t* _aidl_return) {
+    ALOGI("Request received for api: get_num_physical_clusters");
+    DIR *clusters_dir = opendir(PHYCLUSTER_INFO_PATH);
+    struct dirent *clusters_entry;
+    if (clusters_dir == NULL) {
+        ALOGE("Could not open directory: %s", PHYCLUSTER_INFO_PATH);
+        return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+    }
+    int32_t clusters = 0;
+    while ((clusters_entry = readdir(clusters_dir))) {
+        string dir_name = clusters_entry -> d_name;
+        if (dir_name.rfind("cluster") == 0) {
+            ALOGI("%s", dir_name.c_str());;
+            clusters++;
+        }
+        cout<<endl;
+    }
+    ALOGI("Number of clusters: %d",clusters);
+    *_aidl_return = clusters;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Qspa::get_cpus_of_physical_clusters(int32_t in_physical_cluster_number,
+        std::vector<int32_t>* _aidl_return) {
+    ALOGI("Request received for api: get_cpus_of_physical_clusters(%d)", in_physical_cluster_number);
+    fstream fin;
+    fin.open(CPU_INFO_PATH, ios::in);
+    string line;
+    if (!fin || !getline(fin, line)) {
+        ALOGE("Unable to open or read file: %s", CPU_INFO_PATH);
+        return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+    }
+    ALOGI("cpus Range: %s",line.c_str());
+    int cpus = 0;
+    cpus = stoi(std::regex_replace(line, std::regex("0-"), "")) + 1;
+    ALOGI("Number of available cpus: %d", cpus);
+    int first_cpu_in_cluster = -1;
+    for (int i = 0; i < cpus; i++) {
+        string cpu_cluster_map_path = "/sys/devices/system/cpu/cpu" + to_string(i) + "/topology/";
+        struct stat sb;
+        string path1 = cpu_cluster_map_path.append("/physical_package_id");
+        string path2 = cpu_cluster_map_path.append("/cluster_id");
+        if (stat(path1.c_str(), &sb) == 0 && !(sb.st_mode & S_IFDIR)) {
+            fstream fin;
+            fin.open(path1, ios::in);
+            string cid;
+            if (!fin || !getline(fin, cid)) {
+                ALOGE("Unable to open or read file: %s", path1.c_str());
+                return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+            }
+            if (stoi(cid) == in_physical_cluster_number) {
+                first_cpu_in_cluster = i;
+                break;
+            }
+        }
+        else if (stat(path2.c_str(), &sb) == 0 && !(sb.st_mode & S_IFDIR)) {
+            ALOGI("cluster_id exists for cpu: %d", i);
+            fstream fin;
+            fin.open(path2, ios::in);
+            string cid;
+            if (!fin || !getline(fin, cid)) {
+                ALOGE("Unable to open or read file: %s", path2.c_str());
+                return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+            }
+            if (stoi(cid) == in_physical_cluster_number) {
+                first_cpu_in_cluster = i;
+                break;
+            }
+        }
+    }
+    ALOGI("First cpu in cluster is: %d", first_cpu_in_cluster);
+    std::vector<int32_t> tokens;
+    if (first_cpu_in_cluster != -1) {
+        string cpus_path(CLUSTER_INFO_PATH);
+        cpus_path.append("/policy" + to_string(first_cpu_in_cluster) + "/related_cpus");
+        fstream fi;
+        fi.open(cpus_path, ios::in);
+        string rel_cpus;
+        if (!fi || !getline(fi, rel_cpus)) {
+            ALOGE("Unable to open or read file: %s", cpus_path.c_str());
+            return ndk::ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
+        }
+        stringstream data(rel_cpus);
+        string intermediate;
+        // Tokenizing w.r.t ' '
+        while (getline(data, intermediate, ' ')) {
+            ALOGI("data is %s", intermediate.c_str());
+            tokens.push_back(stoi(intermediate,0,16));
+        }
+    }
+    else {
+        tokens.push_back(-1);
+    }
+    *_aidl_return = tokens;
+    return ndk::ScopedAStatus::ok();
+}
+
+Qspa::Qspa() {
+    ALOGI("Within Qspa constructor");
+}
+
+}
+}
+}
+}
+}
\ No newline at end of file
diff --git a/qspaservice/src/Qspa.h b/qspaservice/src/Qspa.h
new file mode 100644
index 0000000..651d97f
--- /dev/null
+++ b/qspaservice/src/Qspa.h
@@ -0,0 +1,55 @@
+/*
+  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+  * SPDX-License-Identifier: BSD-3-Clause-Clear
+*/
+
+#include <aidl/vendor/qti/hardware/qspa/BnQspa.h>
+#include <vector>
+#include <log/log.h>
+#include <iostream>
+#include <android-base/properties.h>
+#include<map>
+
+#define CPU_INFO_PATH "/sys/devices/system/cpu/possible"
+#define CLUSTER_INFO_PATH "/sys/devices/system/cpu/cpufreq"
+#define PHYCLUSTER_INFO_PATH "/proc/device-tree/cpus/cpu-map"
+#define PART_INFO_PATH "/sys/devices/soc0/"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "qspa-service"
+
+using namespace std;
+using aidl::vendor::qti::hardware::qspa::PartInfo;
+namespace aidl {
+namespace vendor {
+namespace qti {
+namespace hardware {
+namespace qspa {
+
+class Qspa : public BnQspa {
+    public:
+        Qspa();
+        ndk::ScopedAStatus get_all_subparts(std::vector<PartInfo>* _aidl_return) override;
+        ndk::ScopedAStatus get_available_parts(std::vector<std::string>* _aidl_return) override;
+        ndk::ScopedAStatus get_subpart_info(const std::string& in_part,
+                std::vector<int32_t>* _aidl_return) override;
+        ndk::ScopedAStatus get_num_available_cpus(int32_t* _aidl_return) override;
+        ndk::ScopedAStatus get_num_available_clusters(int32_t* _aidl_return) override;
+        ndk::ScopedAStatus get_num_physical_clusters(int32_t* _aidl_return) override;
+        ndk::ScopedAStatus get_cpus_of_physical_clusters(int32_t in_physical_cluster_number,
+                std::vector<int32_t>* _aidl_return) override;
+    private:
+        std::vector<std::string> defective_parts = {"gpu", "video", "camera", "display", "audio",
+                "modem", "wlan", "comp", "sensors", "npu", "spss", "nav", "comp1", "display1",
+                "nsp", "eva"};
+
+};
+
+}
+}
+}
+}
+}
+
diff --git a/qspaservice/src/main.cpp b/qspaservice/src/main.cpp
new file mode 100644
index 0000000..a90becc
--- /dev/null
+++ b/qspaservice/src/main.cpp
@@ -0,0 +1,33 @@
+/*
+  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+  * SPDX-License-Identifier: BSD-3-Clause-Clear
+*/
+
+#include "Qspa.h"
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::vendor::qti::hardware::qspa::Qspa;
+using namespace std;
+
+int main(){
+    ABinderProcess_startThreadPool();
+    std::shared_ptr<Qspa> qspa = ndk::SharedRefBase::make<Qspa>();
+    const std::string qspaName = std::string() + Qspa::descriptor + "/default";
+    if (qspa == nullptr) {
+        ALOGE("QSPA object is null, Failed to register !");
+    }
+    else {
+        if (qspa->asBinder() == nullptr) {
+           ALOGE("QSPA binder object is null!");
+        }
+        else {
+           binder_status_t status = AServiceManager_addService(qspa->asBinder().get(), qspaName.c_str());
+           CHECK_EQ(status, STATUS_OK);
+           ALOGI("QSPA service registered !");
+           ABinderProcess_joinThreadPool();
+        }
+    }
+    return EXIT_FAILURE;
+}
\ No newline at end of file
diff --git a/qspaservice/vendor.qti.qspa-service.rc b/qspaservice/vendor.qti.qspa-service.rc
new file mode 100644
index 0000000..799217f
--- /dev/null
+++ b/qspaservice/vendor.qti.qspa-service.rc
@@ -0,0 +1,7 @@
+# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-3-Clause-Clear
+
+service vendor.qspa-service /vendor/bin/vendor.qti.qspa-service
+   class hal
+   user system
+   group system
\ No newline at end of file
diff --git a/qspaservice/vendor.qti.qspa-service.xml b/qspaservice/vendor.qti.qspa-service.xml
new file mode 100644
index 0000000..f1939fe
--- /dev/null
+++ b/qspaservice/vendor.qti.qspa-service.xml
@@ -0,0 +1,14 @@
+<!-- Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+SPDX-License-Identifier: BSD-3-Clause-Clear
+-->
+
+<manifest version="1.0" type="device">
+    <hal format="aidl" optional="true">
+        <name>vendor.qti.hardware.qspa</name>
+        <version>1</version>
+        <interface>
+             <name>IQspa</name>
+             <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>