Add native Thermal Throttling API to libandroid.

Add native thermal manager API of thermal mananger service into
libandroid. Export Thermal API as NDK library.

Bug: 137151587
Bug: 136285293
Test: build, atest thermalmanager-test atest CtsThermalTestCases

Change-Id: Ia49fb2133624ffcd6168af804ae612ef2bb190f2
diff --git a/Android.bp b/Android.bp
index 2534140..df71601 100644
--- a/Android.bp
+++ b/Android.bp
@@ -453,6 +453,18 @@
     path: "core/java",
 }
 
+filegroup {
+    name: "libpowermanager_aidl",
+    srcs: [
+        "core/java/android/os/Temperature.aidl",
+        "core/java/android/os/CoolingDevice.aidl",
+        "core/java/android/os/IThermalEventListener.aidl",
+        "core/java/android/os/IThermalStatusListener.aidl",
+        "core/java/android/os/IThermalService.aidl",
+    ],
+    path: "core/java",
+}
+
 java_library {
     name: "framework-minus-apex",
     defaults: ["framework-defaults"],
diff --git a/core/java/android/os/CoolingDevice.aidl b/core/java/android/os/CoolingDevice.aidl
index 478e4bd..c6432fd3 100644
--- a/core/java/android/os/CoolingDevice.aidl
+++ b/core/java/android/os/CoolingDevice.aidl
@@ -16,4 +16,4 @@
 
 package android.os;
 
-parcelable CoolingDevice;
+parcelable CoolingDevice cpp_header "android/CoolingDevice.h";
diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl
index ad00233..c6c8adc 100644
--- a/core/java/android/os/IThermalService.aidl
+++ b/core/java/android/os/IThermalService.aidl
@@ -56,7 +56,7 @@
       * @return list of {@link android.os.Temperature}.
       * {@hide}
       */
-    List<Temperature> getCurrentTemperatures();
+    Temperature[] getCurrentTemperatures();
 
     /**
       * Get current temperature with its throttling status on given temperature type.
@@ -64,7 +64,7 @@
       * @return list of {@link android.os.Temperature}.
       * {@hide}
       */
-    List<Temperature> getCurrentTemperaturesWithType(in int type);
+    Temperature[] getCurrentTemperaturesWithType(in int type);
 
     /**
       * Register a listener for thermal status change.
@@ -94,7 +94,7 @@
       * @return list of {@link android.os.CoolingDevice}.
       * {@hide}
       */
-    List<CoolingDevice> getCurrentCoolingDevices();
+    CoolingDevice[] getCurrentCoolingDevices();
 
     /**
       * Get current cooling devices on given type.
@@ -102,7 +102,8 @@
       * @return list of {@link android.os.CoolingDevice}.
       * {@hide}
       */
-    List<CoolingDevice> getCurrentCoolingDevicesWithType(in int type);
+
+    CoolingDevice[] getCurrentCoolingDevicesWithType(in int type);
 
     /**
      * @param forecastSeconds how many seconds ahead to forecast the provided headroom
diff --git a/core/java/android/os/Temperature.aidl b/core/java/android/os/Temperature.aidl
index 708c08f..5268dd5 100644
--- a/core/java/android/os/Temperature.aidl
+++ b/core/java/android/os/Temperature.aidl
@@ -16,4 +16,4 @@
 
 package android.os;
 
-parcelable Temperature;
+parcelable Temperature cpp_header "android/Temperature.h";
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 257ae73..640861b 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -51,6 +51,7 @@
         "surface_control.cpp",
         "system_fonts.cpp",
         "trace.cpp",
+        "thermal.cpp"
     ],
 
     shared_libs: [
@@ -72,6 +73,7 @@
         "libxml2",
         "libEGL",
         "libGLESv2",
+        "libpowermanager",
         "android.hardware.configstore@1.0",
         "android.hardware.configstore-utils",
     ],
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index a8f1d2c..d56aa86 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -280,6 +280,11 @@
     android_res_nquery; # introduced=29
     android_res_nresult; # introduced=29
     android_res_nsend; # introduced=29
+    AThermal_acquireManager; # introduced=30
+    AThermal_releaseManager; # introduced=30
+    AThermal_getCurrentThermalStatus; # introduced=30
+    AThermal_registerThermalStatusListener; # introduced=30
+    AThermal_unregisterThermalStatusListener; # introduced=30
   local:
     *;
 };
diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp
new file mode 100644
index 0000000..545c423
--- /dev/null
+++ b/native/android/thermal.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "thermal"
+
+#include <cerrno>
+#include <thread>
+
+#include <android/thermal.h>
+#include <android/os/BnThermalStatusListener.h>
+#include <android/os/IThermalService.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+
+using android::sp;
+
+using namespace android;
+using namespace android::os;
+
+struct ThermalServiceListener : public BnThermalStatusListener {
+    public:
+        virtual binder::Status onStatusChange(int32_t status) override;
+        ThermalServiceListener(AThermalManager *manager) {mMgr = manager;}
+    private:
+        AThermalManager *mMgr;
+};
+
+struct ListenerCallback {
+    AThermal_StatusCallback callback;
+    void* data;
+};
+
+struct AThermalManager {
+   public:
+        static AThermalManager* createAThermalManager();
+        AThermalManager() = delete;
+        ~AThermalManager();
+        status_t notifyStateChange(int32_t status);
+        status_t getCurrentThermalStatus(int32_t *status);
+        status_t addListener(AThermal_StatusCallback, void *data);
+        status_t removeListener(AThermal_StatusCallback, void *data);
+   private:
+       AThermalManager(sp<IThermalService> service);
+       sp<IThermalService> mThermalSvc;
+       sp<ThermalServiceListener> mServiceListener;
+       std::vector<ListenerCallback> mListeners;
+       std::mutex mMutex;
+};
+
+binder::Status ThermalServiceListener::onStatusChange(int32_t status) {
+    if (mMgr != nullptr) {
+        mMgr->notifyStateChange(status);
+    }
+    return binder::Status::ok();
+}
+
+AThermalManager* AThermalManager::createAThermalManager() {
+    sp<IBinder> binder =
+            defaultServiceManager()->checkService(String16("thermalservice"));
+
+    if (binder == nullptr) {
+        ALOGE("%s: Thermal service is not ready ", __FUNCTION__);
+        return nullptr;
+    }
+    return new AThermalManager(interface_cast<IThermalService>(binder));
+}
+
+AThermalManager::AThermalManager(sp<IThermalService> service)
+    : mThermalSvc(service),
+      mServiceListener(nullptr) {
+}
+
+AThermalManager::~AThermalManager() {
+    std::unique_lock<std::mutex> lock(mMutex);
+
+    mListeners.clear();
+    if (mServiceListener != nullptr) {
+        bool success = false;
+        mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
+        mServiceListener = nullptr;
+    }
+}
+
+status_t AThermalManager::notifyStateChange(int32_t status) {
+    std::unique_lock<std::mutex> lock(mMutex);
+    AThermalStatus thermalStatus = static_cast<AThermalStatus>(status);
+
+    for (auto listener : mListeners) {
+        listener.callback(listener.data, thermalStatus);
+    }
+    return OK;
+}
+
+status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) {
+    std::unique_lock<std::mutex> lock(mMutex);
+
+    if (callback == nullptr) {
+        // Callback can not be nullptr
+        return EINVAL;
+    }
+    for (const auto& cb : mListeners) {
+        // Don't re-add callbacks.
+        if (callback == cb.callback && data == cb.data) {
+            return EINVAL;
+        }
+    }
+    mListeners.emplace_back(ListenerCallback{callback, data});
+
+    if (mServiceListener != nullptr) {
+        return OK;
+    }
+    bool success = false;
+    mServiceListener = new ThermalServiceListener(this);
+    if (mServiceListener == nullptr) {
+        return ENOMEM;
+    }
+    auto ret = mThermalSvc->registerThermalStatusListener(mServiceListener, &success);
+    if (!success || !ret.isOk()) {
+        ALOGE("Failed in registerThermalStatusListener %d", success);
+        if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
+            return EPERM;
+        }
+        return EPIPE;
+    }
+    return OK;
+}
+
+status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) {
+    std::unique_lock<std::mutex> lock(mMutex);
+
+    auto it = std::remove_if(mListeners.begin(),
+                             mListeners.end(),
+                             [&](const ListenerCallback& cb) {
+                                    return callback == cb.callback &&
+                                           data == cb.data;
+                             });
+    if (it == mListeners.end()) {
+        // If the listener and data pointer were not previously added.
+        return EINVAL;
+    }
+    mListeners.erase(it, mListeners.end());
+
+    if (!mListeners.empty()) {
+        return OK;
+    }
+    if (mServiceListener == nullptr) {
+        return OK;
+    }
+    bool success = false;
+    auto ret = mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success);
+    if (!success || !ret.isOk()) {
+        ALOGE("Failed in unregisterThermalStatusListener %d", success);
+        if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
+            return EPERM;
+        }
+        return EPIPE;
+    }
+    mServiceListener = nullptr;
+    return OK;
+}
+
+status_t AThermalManager::getCurrentThermalStatus(int32_t *status) {
+    binder::Status ret = mThermalSvc->getCurrentThermalStatus(status);
+
+    if (!ret.isOk()) {
+        if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
+            return EPERM;
+        }
+        return EPIPE;
+    }
+    return OK;
+}
+
+/**
+  * Acquire an instance of the thermal manager. This must be freed using
+  * {@link AThermal_releaseManager}.
+  *
+  * @return manager instance on success, nullptr on failure.
+ */
+AThermalManager* AThermal_acquireManager() {
+    auto manager = AThermalManager::createAThermalManager();
+
+    return manager;
+}
+
+/**
+ * Release the thermal manager pointer acquired by
+ * {@link AThermal_acquireManager}.
+ *
+ * @param manager The manager to be released.
+ *
+ */
+void AThermal_releaseManager(AThermalManager *manager) {
+    delete manager;
+}
+
+/**
+  * Gets the current thermal status.
+  *
+  * @param manager The manager instance to use to query the thermal status,
+  * acquired by {@link AThermal_acquireManager}.
+  *
+  * @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
+*/
+AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) {
+    int32_t status = 0;
+    status_t ret = manager->getCurrentThermalStatus(&status);
+    if (ret != OK) {
+        return AThermalStatus::ATHERMAL_STATUS_ERROR;
+    }
+    return static_cast<AThermalStatus>(status);
+}
+
+/**
+ * Register the thermal status listener for thermal status change.
+ *
+ * @param manager The manager instance to use to register.
+ * acquired by {@link AThermal_acquireManager}.
+ * @param callback The callback function to be called when thermal status updated.
+ * @param data The data pointer to be passed when callback is called.
+ *
+ * @return 0 on success
+ *         EINVAL if the listener and data pointer were previously added and not removed.
+ *         EPERM if the required permission is not held.
+ *         EPIPE if communication with the system service has failed.
+ */
+int AThermal_registerThermalStatusListener(AThermalManager *manager,
+        AThermal_StatusCallback callback, void *data) {
+    return manager->addListener(callback, data);
+}
+
+/**
+ * Unregister the thermal status listener previously resgistered.
+ *
+ * @param manager The manager instance to use to unregister.
+ * acquired by {@link AThermal_acquireManager}.
+ * @param callback The callback function to be called when thermal status updated.
+ * @param data The data pointer to be passed when callback is called.
+ *
+ * @return 0 on success
+ *         EINVAL if the listener and data pointer were not previously added.
+ *         EPERM if the required permission is not held.
+ *         EPIPE if communication with the system service has failed.
+ */
+int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
+        AThermal_StatusCallback callback, void *data) {
+    return manager->removeListener(callback, data);
+}
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index da3cbf9..74c3a9e 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -370,30 +370,33 @@
         }
 
         @Override
-        public List<Temperature> getCurrentTemperatures() {
+        public Temperature[] getCurrentTemperatures() {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.DEVICE_POWER, null);
             final long token = Binder.clearCallingIdentity();
             try {
                 if (!mHalReady.get()) {
-                    return new ArrayList<>();
+                    return new Temperature[0];
                 }
-                return mHalWrapper.getCurrentTemperatures(false, 0 /* not used */);
+                final List<Temperature> curr = mHalWrapper.getCurrentTemperatures(
+                        false, 0 /* not used */);
+                return curr.toArray(new Temperature[curr.size()]);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override
-        public List<Temperature> getCurrentTemperaturesWithType(int type) {
+        public Temperature[] getCurrentTemperaturesWithType(int type) {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.DEVICE_POWER, null);
             final long token = Binder.clearCallingIdentity();
             try {
                 if (!mHalReady.get()) {
-                    return new ArrayList<>();
+                    return new Temperature[0];
                 }
-                return mHalWrapper.getCurrentTemperatures(true, type);
+                final List<Temperature> curr = mHalWrapper.getCurrentTemperatures(true, type);
+                return curr.toArray(new Temperature[curr.size()]);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -443,30 +446,34 @@
         }
 
         @Override
-        public List<CoolingDevice> getCurrentCoolingDevices() {
+        public CoolingDevice[] getCurrentCoolingDevices() {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.DEVICE_POWER, null);
             final long token = Binder.clearCallingIdentity();
             try {
                 if (!mHalReady.get()) {
-                    return new ArrayList<>();
+                    return new CoolingDevice[0];
                 }
-                return mHalWrapper.getCurrentCoolingDevices(false, 0);
+                final List<CoolingDevice> devList = mHalWrapper.getCurrentCoolingDevices(
+                        false, 0);
+                return devList.toArray(new CoolingDevice[devList.size()]);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override
-        public List<CoolingDevice> getCurrentCoolingDevicesWithType(int type) {
+        public CoolingDevice[] getCurrentCoolingDevicesWithType(int type) {
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.DEVICE_POWER, null);
             final long token = Binder.clearCallingIdentity();
             try {
                 if (!mHalReady.get()) {
-                    return new ArrayList<>();
+                    return new CoolingDevice[0];
                 }
-                return mHalWrapper.getCurrentCoolingDevices(true, type);
+                final List<CoolingDevice> devList = mHalWrapper.getCurrentCoolingDevices(
+                        true, type);
+                return devList.toArray(new CoolingDevice[devList.size()]);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 47a26f5..2b111f7 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1515,7 +1515,7 @@
         }
         final long callingToken = Binder.clearCallingIdentity();
         try {
-            List<Temperature> temperatures = thermalService.getCurrentTemperatures();
+            Temperature temperatures[] = thermalService.getCurrentTemperatures();
             for (Temperature temp : temperatures) {
                 StatsEvent e = StatsEvent.newBuilder()
                         .setAtomId(atomTag)
@@ -1553,7 +1553,7 @@
         }
         final long callingToken = Binder.clearCallingIdentity();
         try {
-            List<CoolingDevice> devices = thermalService.getCurrentCoolingDevices();
+            CoolingDevice devices[] = thermalService.getCurrentCoolingDevices();
             for (CoolingDevice device : devices) {
                 StatsEvent e = StatsEvent.newBuilder()
                         .setAtomId(atomTag)
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 624cb83..9e067304 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -297,9 +297,11 @@
     @Test
     public void testGetCurrentTemperatures() throws RemoteException {
         assertListEqualsIgnoringOrder(mFakeHal.getCurrentTemperatures(false, 0),
-                mService.mService.getCurrentTemperatures());
-        assertListEqualsIgnoringOrder(mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN),
-                mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN));
+                Arrays.asList(mService.mService.getCurrentTemperatures()));
+        assertListEqualsIgnoringOrder(
+                mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN),
+                Arrays.asList(mService.mService.getCurrentTemperaturesWithType(
+                        Temperature.TYPE_SKIN)));
     }
 
     @Test
@@ -331,21 +333,22 @@
         assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1));
         assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1));
         assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1));
-        assertEquals(0, mService.mService.getCurrentTemperatures().size());
-        assertEquals(0,
-                mService.mService.getCurrentTemperaturesWithType(Temperature.TYPE_SKIN).size());
+        assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperatures()).size());
+        assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperaturesWithType(
+                        Temperature.TYPE_SKIN)).size());
         assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentThermalStatus());
     }
 
     @Test
     public void testGetCurrentCoolingDevices() throws RemoteException {
         assertListEqualsIgnoringOrder(mFakeHal.getCurrentCoolingDevices(false, 0),
-                mService.mService.getCurrentCoolingDevices());
+                Arrays.asList(mService.mService.getCurrentCoolingDevices()));
         assertListEqualsIgnoringOrder(
                 mFakeHal.getCurrentCoolingDevices(false, CoolingDevice.TYPE_BATTERY),
-                mService.mService.getCurrentCoolingDevices());
+                Arrays.asList(mService.mService.getCurrentCoolingDevices()));
         assertListEqualsIgnoringOrder(
                 mFakeHal.getCurrentCoolingDevices(true, CoolingDevice.TYPE_CPU),
-                mService.mService.getCurrentCoolingDevicesWithType(CoolingDevice.TYPE_CPU));
+                Arrays.asList(mService.mService.getCurrentCoolingDevicesWithType(
+                        CoolingDevice.TYPE_CPU)));
     }
 }