Merge branch 'lineage-21.0' of https://github.com/LineageOS/android_hardware_lineage_interfaces into leaf-3.2

Change-Id: I81236508379b1f17ff085a9b99fef03e478c5728
diff --git a/_backend/Android.bp b/_backend/Android.bp
new file mode 100644
index 0000000..4f274fd
--- /dev/null
+++ b/_backend/Android.bp
@@ -0,0 +1,4 @@
+hidl_package_root {
+    name: "lineage.hardware",
+    path: "hardware/lineage/interfaces/_backend",
+}
diff --git a/_backend/current.txt b/_backend/current.txt
new file mode 100644
index 0000000..f4a09f8
--- /dev/null
+++ b/_backend/current.txt
@@ -0,0 +1,13 @@
+7a3d616493acedda3b64c7f81ee407265fcb36a508ea889733196dcd942e3c76 lineage.hardware.radio.config@1.0::types
+c204fd7819843ae0d73b34e4eaa1d63d1cc17e9b304fae5d3fccef35e38f6756 lineage.hardware.radio.config@1.0::IRadioConfig
+2bcb03a75a3b53ba57d27e6f989dadd07712c9d7739758af654f57a0173cb5ba lineage.hardware.radio.config@1.0::IRadioConfigIndication
+ad06069c78380c6355c347f1350b3c7562d91a042d1952b4fe8c85348902c6ef lineage.hardware.radio.config@1.0::IRadioConfigResponse
+bfef144715c87c96af3a1066ed3960b39462f8026caa97d1dbd8ce6ed43b7b37 lineage.hardware.radio.config@1.1::types
+7c1384035569deb5f611f8eaa7807e8dbb0d23aaf435e3adcb4bda45889d5512 lineage.hardware.radio.config@1.1::IRadioConfig
+88d4d671849ac3239fe32f0bf8c4e0d215fb393f8c2aee62c18049392accdbaa lineage.hardware.radio.config@1.1::IRadioConfigIndication
+f1b68013c6e07f9407e39cc6b627f32a4578873da944b272dc211d14a2c09b25 lineage.hardware.radio.config@1.1::IRadioConfigResponse
+b9ee1aeb20a9508835ea723a36791eb0995df71d311dd55d499288bfb7b66ced lineage.hardware.radio.config@1.2::types
+31c9fcce62abb962cc2b645bb5f3190d0dba58eba8d411756ac5bcbb292cbef0 lineage.hardware.radio.config@1.2::IRadioConfigIndication
+e59d2481473837d1f44108865f82907f4b41c72eb9583ce3c04b5f9dc4f4f592 lineage.hardware.radio.config@1.2::IRadioConfigResponse
+a7b15a84a84ef71ee41ef86a8a6cb7dd35ab38b849ed405cf1ac2cbe98772f43 lineage.hardware.radio.config@1.3::IRadioConfig
+562021f3834002cced2db496576eb8dadc819b9ffe8eaca2102b141a5d4cf0da lineage.hardware.radio.config@1.3::IRadioConfigResponse
diff --git a/_backend/radio/config/1.0/Android.bp b/_backend/radio/config/1.0/Android.bp
new file mode 100644
index 0000000..5e0337e
--- /dev/null
+++ b/_backend/radio/config/1.0/Android.bp
@@ -0,0 +1,18 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "lineage.hardware.radio.config@1.0",
+    root: "lineage.hardware",
+    system_ext_specific: true,
+    srcs: [
+        "types.hal",
+        "IRadioConfig.hal",
+        "IRadioConfigIndication.hal",
+        "IRadioConfigResponse.hal",
+    ],
+    interfaces: [
+        "android.hardware.radio@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/_backend/radio/config/1.0/IRadioConfig.hal b/_backend/radio/config/1.0/IRadioConfig.hal
new file mode 100644
index 0000000..d1b802d
--- /dev/null
+++ b/_backend/radio/config/1.0/IRadioConfig.hal
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.0;
+
+import IRadioConfigResponse;
+import IRadioConfigIndication;
+
+/**
+ * This interface is used by telephony and telecom to talk to cellular radio for the purpose of
+ * radio configuration, and it is not associated with any specific modem or slot.
+ * All the functions have minimum one parameter:
+ * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
+ * duration of a method call. If clients provide colliding serials (including passing the same
+ * serial to different methods), multiple responses (one for each method call) must still be served.
+ */
+interface IRadioConfig {
+
+    /**
+     * Set response functions for radio config requests & radio config indications.
+     *
+     * @param radioConfigResponse Object containing radio config response functions
+     * @param radioConfigIndication Object containing radio config indications
+     */
+    setResponseFunctions(IRadioConfigResponse radioConfigResponse,
+            IRadioConfigIndication radioConfigIndication);
+
+    /**
+     * Get SIM Slot status.
+     *
+     * Request provides the slot status of all active and inactive SIM slots and whether card is
+     * present in the slots or not.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioConfigResponse.getSimSlotsStatusResponse()
+     */
+    oneway getSimSlotsStatus(int32_t serial);
+
+    /**
+     * Set SIM Slot mapping.
+
+     * Maps the logical slots to the physical slots. Logical slot is the slot that is seen by modem.
+     * Physical slot is the actual physical slot. Request maps the physical slot to logical slot.
+     * Logical slots that are already mapped to the requested physical slot are not impacted.
+     *
+     * Example no. of logical slots 1 and physical slots 2:
+     * The only logical slot (index 0) can be mapped to first physical slot (value 0) or second
+     * physical slot(value 1), while the other physical slot remains unmapped and inactive.
+     * slotMap[0] = 1 or slotMap[0] = 0
+     *
+     * Example no. of logical slots 2 and physical slots 2:
+     * First logical slot (index 0) can be mapped to physical slot 1 or 2 and other logical slot
+     * can be mapped to other physical slot. Each logical slot must be mapped to a physical slot.
+     * slotMap[0] = 0 and slotMap[1] = 1 or slotMap[0] = 1 and slotMap[1] = 0
+     *
+     * @param serial Serial number of request
+     * @param slotMap Logical to physical slot mapping, size == no. of radio instances. Index is
+     *        mapping to logical slot and value to physical slot, need to provide all the slots
+     *        mapping when sending request in case of multi slot device.
+     *        EX: uint32_t slotMap[logical slot] = physical slot
+     *        index 0 is the first logical_slot number of logical slots is equal to number of Radio
+     *        instances and number of physical slots is equal to size of slotStatus in
+     *        getSimSlotsStatusResponse
+     *
+     * Response callback is IRadioConfigResponse.setSimSlotsMappingResponse()
+     */
+    oneway setSimSlotsMapping(int32_t serial, vec<uint32_t> slotMap);
+};
diff --git a/_backend/radio/config/1.0/IRadioConfigIndication.hal b/_backend/radio/config/1.0/IRadioConfigIndication.hal
new file mode 100644
index 0000000..63c1031
--- /dev/null
+++ b/_backend/radio/config/1.0/IRadioConfigIndication.hal
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.0;
+
+import android.hardware.radio@1.0::RadioIndicationType;
+
+/**
+ * Interface declaring unsolicited radio config indications.
+ */
+interface IRadioConfigIndication {
+
+    /**
+     * Indicates SIM slot status change.
+
+     * This indication must be sent by the modem whenever there is any slot status change, even the
+     * slot is inactive. For example, this indication must be triggered if a SIM card is inserted
+     * into an inactive slot.
+     *
+     * @param type Type of radio indication
+     * @param slotStatus new slot status info with size equals to the number of physical slots on
+     *        the device
+     */
+    oneway simSlotsStatusChanged(RadioIndicationType type, vec<SimSlotStatus> slotStatus);
+};
diff --git a/_backend/radio/config/1.0/IRadioConfigResponse.hal b/_backend/radio/config/1.0/IRadioConfigResponse.hal
new file mode 100644
index 0000000..f84e12f
--- /dev/null
+++ b/_backend/radio/config/1.0/IRadioConfigResponse.hal
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.0;
+
+import android.hardware.radio@1.0::RadioResponseInfo;
+
+/**
+ * Interface declaring response functions to solicited radio config requests.
+ */
+interface IRadioConfigResponse {
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param slotStatus Sim slot struct containing all the physical SIM slots info with size
+      *       equals to the number of physical slots on the device
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:NO_MEMORY
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:MODEM_ERR
+     */
+    oneway getSimSlotsStatusResponse(RadioResponseInfo info, vec<SimSlotStatus> slotStatus);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:NO_MEMORY
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:MODEM_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     */
+    oneway setSimSlotsMappingResponse(RadioResponseInfo info);
+};
diff --git a/_backend/radio/config/1.0/types.hal b/_backend/radio/config/1.0/types.hal
new file mode 100644
index 0000000..e20cbd7
--- /dev/null
+++ b/_backend/radio/config/1.0/types.hal
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.0;
+
+import android.hardware.radio@1.0::CardState;
+
+enum SlotState : int32_t {
+    /**
+     * Physical slot is inactive
+     */
+    INACTIVE  = 0x00,
+    /**
+     * Physical slot is active
+     */
+    ACTIVE    = 0x01,
+};
+
+struct SimSlotStatus {
+    /**
+     * Card state in the physical slot
+     */
+    CardState cardState;
+    /**
+     * Slot state Active/Inactive
+     */
+    SlotState slotState;
+    /**
+     * An Answer To Reset (ATR) is a message output by a Smart Card conforming to ISO/IEC 7816
+     * standards, following electrical reset of the card's chip. The ATR conveys information about
+     * the communication parameters proposed by the card, and the card's nature and state.
+     *
+     * This data is applicable only when cardState is CardState:PRESENT.
+     */
+    string atr;
+    uint32_t logicalSlotId;
+    /**
+     * Integrated Circuit Card IDentifier (ICCID) is Unique Identifier of the SIM CARD. File is
+     * located in the SIM card at EFiccid (0x2FE2) as per ETSI 102.221. The ICCID is defined by
+     * the ITU-T recommendation E.118 ISO/IEC 7816.
+     *
+     * This data is applicable only when cardState is CardState:PRESENT.
+     */
+    string iccid;
+};
diff --git a/_backend/radio/config/1.1/.hidl_for_system_ext b/_backend/radio/config/1.1/.hidl_for_system_ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_backend/radio/config/1.1/.hidl_for_system_ext
diff --git a/_backend/radio/config/1.1/Android.bp b/_backend/radio/config/1.1/Android.bp
new file mode 100644
index 0000000..8e5d86e
--- /dev/null
+++ b/_backend/radio/config/1.1/Android.bp
@@ -0,0 +1,19 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "lineage.hardware.radio.config@1.1",
+    root: "lineage.hardware",
+    system_ext_specific: true,
+    srcs: [
+        "types.hal",
+        "IRadioConfig.hal",
+        "IRadioConfigIndication.hal",
+        "IRadioConfigResponse.hal",
+    ],
+    interfaces: [
+        "android.hardware.radio@1.0",
+        "android.hidl.base@1.0",
+        "lineage.hardware.radio.config@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/_backend/radio/config/1.1/IRadioConfig.hal b/_backend/radio/config/1.1/IRadioConfig.hal
new file mode 100644
index 0000000..efc0e9f
--- /dev/null
+++ b/_backend/radio/config/1.1/IRadioConfig.hal
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.1;
+
+import @1.0::IRadioConfig;
+import @1.1::IRadioConfigResponse;
+import @1.1::PhoneCapability;
+import @1.1::ModemsConfig;
+
+/**
+ * Note: IRadioConfig 1.1 is an intermediate layer between Android P and Android Q.
+ * It's specifically designed for CBRS related interfaces. All other interfaces
+ * for Q are added in IRadioConfig 1.2.
+ *
+ * This interface is used by telephony and telecom to talk to cellular radio for the purpose of
+ * radio configuration, and it is not associated with any specific modem or slot.
+ * All the functions have minimum one parameter:
+ * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
+ * duration of a method call. If clients provide colliding serials (including passing the same
+ * serial to different methods), multiple responses (one for each method call) must still be served.
+ */
+interface IRadioConfig extends @1.0::IRadioConfig {
+   /**
+     * Request current phone capability.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getPhoneCapabilityResponse() which
+     * will return <@1.1::PhoneCapability>.
+     */
+    oneway getPhoneCapability(int32_t serial);
+
+   /**
+     * Set preferred data modem Id.
+     * In a multi-SIM device, notify modem layer which logical modem will be used primarily
+     * for data. It helps modem with resource optimization and decisions of what data connections
+     * should be satisfied.
+     *
+     * @param serial Serial number of request.
+     * @param modem Id the logical modem ID, which should match one of modem IDs returned
+     * from getPhoneCapability().
+     *
+     * Response callback is IRadioConfigResponse.setPreferredDataModemResponse()
+     */
+    oneway setPreferredDataModem(int32_t serial, uint8_t modemId);
+
+   /**
+     * Set modems configurations by specifying the number of live modems (i.e modems that are
+     * enabled and actively working as part of a working telephony stack).
+     *
+     * Example: this interface can be used to switch to single/multi sim mode by specifying
+     * the number of live modems as 1, 2, etc
+     *
+     * Note: by setting the number of live modems in this API, that number of modems will
+     * subsequently get enabled/disabled
+     *
+     * @param serial serial number of request.
+     * @param modemsConfig ModemsConfig object including the number of live modems
+     *
+     * Response callback is IRadioResponse.setModemsConfigResponse()
+     */
+    oneway setModemsConfig(int32_t serial, ModemsConfig modemsConfig);
+
+   /**
+     * Get modems configurations. This interface is used to get modem configurations
+     * which includes the number of live modems (i.e modems that are
+     * enabled and actively working as part of a working telephony stack)
+     *
+     * Note: in order to get the overall number of modems available on the phone,
+     * refer to getPhoneCapability API
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getModemsConfigResponse() which
+     * will return <@1.1::ModemsConfig>.
+     */
+    oneway getModemsConfig(int32_t serial);
+};
diff --git a/_backend/radio/config/1.1/IRadioConfigIndication.hal b/_backend/radio/config/1.1/IRadioConfigIndication.hal
new file mode 100644
index 0000000..f72aada
--- /dev/null
+++ b/_backend/radio/config/1.1/IRadioConfigIndication.hal
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.1;
+
+import @1.0::IRadioConfigIndication;
+
+/**
+ * Interface declaring unsolicited radio config indications.
+ */
+interface IRadioConfigIndication extends @1.0::IRadioConfigIndication {
+};
diff --git a/_backend/radio/config/1.1/IRadioConfigResponse.hal b/_backend/radio/config/1.1/IRadioConfigResponse.hal
new file mode 100644
index 0000000..05afbb8
--- /dev/null
+++ b/_backend/radio/config/1.1/IRadioConfigResponse.hal
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.1;
+
+import @1.0::IRadioConfigResponse;
+import @1.1::PhoneCapability;
+import android.hardware.radio@1.0::RadioResponseInfo;
+import @1.1::ModemsConfig;
+
+/**
+ * Note: IRadioConfig 1.1 is an intermediate layer between Android P and Android Q.
+ * It's specifically designed for CBRS related interfaces. All other interfaces
+ * for Q are be added in IRadioConfig 1.2.
+ *
+ * Interface declaring response functions to solicited radio config requests.
+ */
+interface IRadioConfigResponse extends @1.0::IRadioConfigResponse {
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param phoneCapability <@1.1::PhoneCapability> it defines modem's capability for example
+     *        how many logical modems it has, how many data connections it supports.
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getPhoneCapabilityResponse(RadioResponseInfo info, PhoneCapability phoneCapability);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     */
+    oneway setPreferredDataModemResponse(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_ARGUMENTS
+     */
+    oneway setModemsConfigResponse(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param modemsConfig <@1.1::ModemsConfig> it defines all the modems' configurations
+     *        at this time, only the number of live modems
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     */
+    oneway getModemsConfigResponse(RadioResponseInfo info, ModemsConfig modemsConfig);
+};
diff --git a/_backend/radio/config/1.1/types.hal b/_backend/radio/config/1.1/types.hal
new file mode 100644
index 0000000..eb78053
--- /dev/null
+++ b/_backend/radio/config/1.1/types.hal
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.1;
+
+/**
+ * Note: IRadioConfig 1.1 is an intermediate layer between Android P and Android Q.
+ * It's specifically designed for CBRS related interfaces. All other interfaces
+ * for Q are be added in IRadioConfig 1.2.
+ */
+
+/**
+ * A field in PhoneCapability that has information of each logical modem.
+ */
+struct ModemInfo {
+    /**
+     * Logical modem ID.
+     */
+    uint8_t modemId;
+};
+
+/**
+ * Phone capability which describes the data connection capability of modem.
+ * It's used to evaluate possible phone config change, for example from single
+ * SIM device to multi-SIM device.
+ */
+struct PhoneCapability {
+    /**
+     * maxActiveData defines how many logical modems can have
+     * PS attached simultaneously. For example, for L+L modem it
+     * should be 2.
+     */
+    uint8_t maxActiveData;
+    /**
+     * maxActiveData defines how many logical modems can have
+     * internet PDN connections simultaneously. For example, for L+L
+     * DSDS modem it’s 1, and for DSDA modem it’s 2.
+     */
+    uint8_t maxActiveInternetData;
+    /**
+     * Whether modem supports both internet PDN up so
+     * that we can do ping test before tearing down the
+     * other one.
+     */
+    bool isInternetLingeringSupported;
+    /**
+     * List of logical modem information.
+     */
+    vec<ModemInfo> logicalModemList;
+};
+
+struct ModemsConfig {
+    /**
+     * variable to indicate the number of live modems i.e modems that are enabled
+     * and actively working as part of a working connectivity stack
+     */
+    uint8_t numOfLiveModems;
+};
\ No newline at end of file
diff --git a/_backend/radio/config/1.2/.hidl_for_system_ext b/_backend/radio/config/1.2/.hidl_for_system_ext
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/_backend/radio/config/1.2/.hidl_for_system_ext
diff --git a/_backend/radio/config/1.2/Android.bp b/_backend/radio/config/1.2/Android.bp
new file mode 100644
index 0000000..f04897a
--- /dev/null
+++ b/_backend/radio/config/1.2/Android.bp
@@ -0,0 +1,19 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "lineage.hardware.radio.config@1.2",
+    root: "lineage.hardware",
+    system_ext_specific: true,
+    srcs: [
+        "types.hal",
+        "IRadioConfigIndication.hal",
+        "IRadioConfigResponse.hal",
+    ],
+    interfaces: [
+        "android.hardware.radio@1.0",
+        "android.hidl.base@1.0",
+        "lineage.hardware.radio.config@1.0",
+        "lineage.hardware.radio.config@1.1",
+    ],
+    gen_java: true,
+}
diff --git a/_backend/radio/config/1.2/IRadioConfigIndication.hal b/_backend/radio/config/1.2/IRadioConfigIndication.hal
new file mode 100644
index 0000000..10c356c
--- /dev/null
+++ b/_backend/radio/config/1.2/IRadioConfigIndication.hal
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.2;
+
+import android.hardware.radio@1.0::RadioIndicationType;
+import @1.1::IRadioConfigIndication;
+
+/**
+ * Interface declaring unsolicited radio config indications.
+ */
+interface IRadioConfigIndication extends @1.1::IRadioConfigIndication {
+
+    /**
+     * Indicates SIM slot status change.
+     *
+     * This indication must be sent by the modem whenever there is any slot status change, even the
+     * slot is inactive. For example, this indication must be triggered if a SIM card is inserted
+     * into an inactive slot.
+     *
+     * @param type Type of radio indication
+     * @param slotStatus new slot status info with size equals to the number of physical slots on
+     *        the device
+     */
+    oneway simSlotsStatusChanged_1_2(RadioIndicationType type, vec<SimSlotStatus> slotStatus);
+};
\ No newline at end of file
diff --git a/_backend/radio/config/1.2/IRadioConfigResponse.hal b/_backend/radio/config/1.2/IRadioConfigResponse.hal
new file mode 100644
index 0000000..7bba6ea
--- /dev/null
+++ b/_backend/radio/config/1.2/IRadioConfigResponse.hal
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.2;
+
+import android.hardware.radio@1.0::RadioResponseInfo;
+import @1.1::IRadioConfigResponse;
+import @1.2::SimSlotStatus;
+
+/**
+ * Interface declaring response functions to solicited radio config requests.
+ */
+interface IRadioConfigResponse extends @1.1::IRadioConfigResponse {
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param slotStatus Sim slot struct containing all the physical SIM slots info with size
+     *        equal to the number of physical slots on the device
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:NO_MEMORY
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:MODEM_ERR
+     */
+    oneway getSimSlotsStatusResponse_1_2(RadioResponseInfo info, vec<SimSlotStatus> slotStatus);
+};
\ No newline at end of file
diff --git a/_backend/radio/config/1.2/types.hal b/_backend/radio/config/1.2/types.hal
new file mode 100644
index 0000000..02d0ff0
--- /dev/null
+++ b/_backend/radio/config/1.2/types.hal
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+package lineage.hardware.radio.config@1.2;
+
+import android.hardware.radio@1.0::CardState;
+import @1.0::SimSlotStatus;
+
+struct SimSlotStatus {
+    @1.0::SimSlotStatus base;
+    /**
+     * The EID is the eUICC identifier. The EID shall be stored within the ECASD and can be
+     * retrieved by the Device at any time using the standard GlobalPlatform GET DATA command.
+     *
+     * This data is mandatory and applicable only when cardState is CardState:PRESENT and SIM card
+     * supports eUICC.
+     */
+    string eid;
+};
\ No newline at end of file
diff --git a/_backend/radio/config/1.3/Android.bp b/_backend/radio/config/1.3/Android.bp
new file mode 100644
index 0000000..a0a00ea
--- /dev/null
+++ b/_backend/radio/config/1.3/Android.bp
@@ -0,0 +1,25 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "lineage.hardware.radio.config@1.3",
+    root: "lineage.hardware",
+    system_ext_specific: true,
+    srcs: [
+        "IRadioConfig.hal",
+        "IRadioConfigResponse.hal",
+    ],
+    interfaces: [
+        "android.hardware.radio@1.0",
+        "android.hardware.radio@1.1",
+        "android.hardware.radio@1.2",
+        "android.hardware.radio@1.4",
+        "android.hardware.radio@1.5",
+        "android.hardware.radio@1.6",
+        "android.hidl.base@1.0",
+        "android.hidl.safe_union@1.0",
+        "lineage.hardware.radio.config@1.0",
+        "lineage.hardware.radio.config@1.1",
+        "lineage.hardware.radio.config@1.2",
+    ],
+    gen_java: true,
+}
diff --git a/_backend/radio/config/1.3/IRadioConfig.hal b/_backend/radio/config/1.3/IRadioConfig.hal
new file mode 100644
index 0000000..5bcd222
--- /dev/null
+++ b/_backend/radio/config/1.3/IRadioConfig.hal
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ *
+ *
+ * This interface is used by telephony and telecom to talk to cellular radio for the purpose of
+ * radio configuration, and it is not associated with any specific modem or slot.
+ * All the functions have minimum one parameter:
+ * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
+ * duration of a method call. If clients provide colliding serials (including passing the same
+ * serial to different methods), multiple responses (one for each method call) must still be served.
+ */
+
+package lineage.hardware.radio.config@1.3;
+
+import @1.1::IRadioConfig;
+import IRadioConfigResponse;
+
+interface IRadioConfig extends @1.1::IRadioConfig {
+    /**
+     * Gets the available Radio Hal capabilities on the current device.
+     *
+     * This is called once per device boot up.
+     *
+     * @param serial Serial number of request
+     *
+     * Response callback is
+     * IRadioConfigResponse.getHalDeviceCapabilitiesResponse()
+     */
+    oneway getHalDeviceCapabilities(int32_t serial);
+};
diff --git a/_backend/radio/config/1.3/IRadioConfigResponse.hal b/_backend/radio/config/1.3/IRadioConfigResponse.hal
new file mode 100644
index 0000000..6952fce
--- /dev/null
+++ b/_backend/radio/config/1.3/IRadioConfigResponse.hal
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package lineage.hardware.radio.config@1.3;
+
+import android.hardware.radio@1.6::RadioResponseInfo;
+import @1.2::IRadioConfigResponse;
+
+/**
+ * Interface declaring response functions to solicited radio config requests.
+ */
+interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param modemReducedFeatureSet1 True indicates that the modem does NOT support the following
+     *        features.
+     *        - Providing either
+     *          android.hardware.radio@1.6::LinkCapacityEstimate:secondaryDownlinkCapacityKbps
+     *          or android.hardware.radio@1.6::LinkCapacityEstimate:secondaryUplinkCapacityKbps
+     *          when given from
+     *          android.hardware.radio@1.6::RadioIndication:currentLinkCapacityEstimate
+     *        - Calling android.hardware.radio@1.6::IRadio.setNrDualConnectivityState
+     *          or querying android.hardware.radio@1.6::IRadio.isNrDualConnectivityEnabled
+     *        - Requesting android.hardware.radio@1.6::IRadio.setDataThrottling()
+     *        - Providing android.hardware.radio@1.6::SlicingConfig through
+     *          android.hardware.radio@1.6::getSlicingConfig()
+     *        - Providing android.hardware.radio@1.6::PhysicalChannelConfig through
+     *          android.hardware.radio@1.6::IRadioIndication.currentPhysicalChannelConfigs_1_6()
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getHalDeviceCapabilitiesResponse(RadioResponseInfo info,
+        bool modemReducedFeatureSet1);
+};
diff --git a/power-libperfmgr/Android.bp b/power-libperfmgr/Android.bp
new file mode 100644
index 0000000..0839ddd
--- /dev/null
+++ b/power-libperfmgr/Android.bp
@@ -0,0 +1,58 @@
+//
+// Copyright (C) 2021 The LineageOS Project
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+
+soong_namespace {
+    imports: [
+        "hardware/google/interfaces",
+        "hardware/google/pixel",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.power-service.lineage-libperfmgr",
+    defaults: [
+        "power_libperfmgr_defaults",
+    ],
+    relative_install_path: "hw",
+    init_rc: ["aidl/android.hardware.power-service.lineage-libperfmgr.rc"],
+    vintf_fragments: ["aidl/android.hardware.power-service.lineage.xml"],
+    vendor: true,
+    cflags: [
+        "-Wno-unused-variable",
+        "-Wno-unused-parameter",
+    ],
+    shared_libs: [
+        "android.hardware.power-V4-ndk",
+        "libbase",
+        "libbinder_ndk",
+        "libcutils",
+        "libdl",
+        "liblog",
+        "libperfmgr",
+        "libprocessgroup",
+        "libutils",
+        "pixel-power-ext-V1-ndk",
+    ],
+    srcs: [
+        "aidl/service.cpp",
+        "aidl/InteractionHandler.cpp",
+        "aidl/Power.cpp",
+        "aidl/PowerExt.cpp",
+        "aidl/PowerHintSession.cpp",
+        "aidl/PowerSessionManager.cpp",
+    ],
+}
+
+cc_library_static {
+    name: "libperfmgr-ext",
+    vendor: true,
+    srcs: [
+        "aidl/PowerModeExtension.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.power-V4-ndk",
+    ],
+}
diff --git a/power-libperfmgr/aidl/InteractionHandler.cpp b/power-libperfmgr/aidl/InteractionHandler.cpp
new file mode 100644
index 0000000..a9f908b
--- /dev/null
+++ b/power-libperfmgr/aidl/InteractionHandler.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2018 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 ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
+#define LOG_TAG "powerhal-libperfmgr"
+
+#include "InteractionHandler.h"
+
+#include <android-base/properties.h>
+#include <fcntl.h>
+#include <perfmgr/HintManager.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+#include <time.h>
+#include <unistd.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include <array>
+#include <memory>
+
+#define MAX_LENGTH 64
+
+#define MSINSEC 1000L
+#define NSINMS 1000000L
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+namespace {
+
+static const bool kDisplayIdleSupport =
+        ::android::base::GetBoolProperty("vendor.powerhal.disp.idle_support", true);
+static const std::array<const char *, 2> kDispIdlePath = {"/sys/class/drm/card0/device/idle_state",
+                                                          "/sys/class/graphics/fb0/idle_state"};
+static const uint32_t kWaitMs =
+        ::android::base::GetUintProperty("vendor.powerhal.disp.idle_wait", /*default*/ 100U);
+static const uint32_t kMinDurationMs =
+        ::android::base::GetUintProperty("vendor.powerhal.interaction.min", /*default*/ 1400U);
+static const uint32_t kMaxDurationMs =
+        ::android::base::GetUintProperty("vendor.powerhal.interaction.max", /*default*/ 5650U);
+static const uint32_t kDurationOffsetMs =
+        ::android::base::GetUintProperty("vendor.powerhal.interaction.offset", /*default*/ 650U);
+
+static size_t CalcTimespecDiffMs(struct timespec start, struct timespec end) {
+    size_t diff_in_ms = 0;
+    diff_in_ms += (end.tv_sec - start.tv_sec) * MSINSEC;
+    diff_in_ms += (end.tv_nsec - start.tv_nsec) / NSINMS;
+    return diff_in_ms;
+}
+
+static int FbIdleOpen(void) {
+    int fd;
+    for (const auto &path : kDispIdlePath) {
+        fd = open(path, O_RDONLY);
+        if (fd >= 0)
+            return fd;
+    }
+    ALOGE("Unable to open fb idle state path (%d)", errno);
+    return -1;
+}
+
+}  // namespace
+
+using ::android::perfmgr::HintManager;
+
+InteractionHandler::InteractionHandler()
+    : mState(INTERACTION_STATE_UNINITIALIZED), mDurationMs(0) {}
+
+InteractionHandler::~InteractionHandler() {
+    Exit();
+}
+
+bool InteractionHandler::Init() {
+    std::lock_guard<std::mutex> lk(mLock);
+
+    if (mState != INTERACTION_STATE_UNINITIALIZED)
+        return true;
+
+    int fd = FbIdleOpen();
+    if (fd < 0)
+        return false;
+    mIdleFd = fd;
+
+    mEventFd = eventfd(0, EFD_NONBLOCK);
+    if (mEventFd < 0) {
+        ALOGE("Unable to create event fd (%d)", errno);
+        close(mIdleFd);
+        return false;
+    }
+
+    mState = INTERACTION_STATE_IDLE;
+    mThread = std::unique_ptr<std::thread>(new std::thread(&InteractionHandler::Routine, this));
+
+    return true;
+}
+
+void InteractionHandler::Exit() {
+    std::unique_lock<std::mutex> lk(mLock);
+    if (mState == INTERACTION_STATE_UNINITIALIZED)
+        return;
+
+    AbortWaitLocked();
+    mState = INTERACTION_STATE_UNINITIALIZED;
+    lk.unlock();
+
+    mCond.notify_all();
+    mThread->join();
+
+    close(mEventFd);
+    close(mIdleFd);
+}
+
+void InteractionHandler::PerfLock() {
+    ALOGV("%s: acquiring perf lock", __func__);
+    if (!HintManager::GetInstance()->DoHint("INTERACTION")) {
+        ALOGE("%s: do hint INTERACTION failed", __func__);
+    }
+}
+
+void InteractionHandler::PerfRel() {
+    ALOGV("%s: releasing perf lock", __func__);
+    if (!HintManager::GetInstance()->EndHint("INTERACTION")) {
+        ALOGE("%s: end hint INTERACTION failed", __func__);
+    }
+}
+
+void InteractionHandler::Acquire(int32_t duration) {
+    ATRACE_CALL();
+
+    std::lock_guard<std::mutex> lk(mLock);
+
+    int inputDuration = duration + kDurationOffsetMs;
+    int finalDuration;
+    if (inputDuration > kMaxDurationMs)
+        finalDuration = kMaxDurationMs;
+    else if (inputDuration > kMinDurationMs)
+        finalDuration = inputDuration;
+    else
+        finalDuration = kMinDurationMs;
+
+    // Fallback to do boost directly
+    // 1) override property is set OR
+    // 2) InteractionHandler not initialized
+    if (!kDisplayIdleSupport || mState == INTERACTION_STATE_UNINITIALIZED) {
+        HintManager::GetInstance()->DoHint("INTERACTION", std::chrono::milliseconds(finalDuration));
+        return;
+    }
+
+    struct timespec cur_timespec;
+    clock_gettime(CLOCK_MONOTONIC, &cur_timespec);
+    if (mState != INTERACTION_STATE_IDLE && finalDuration <= mDurationMs) {
+        size_t elapsed_time = CalcTimespecDiffMs(mLastTimespec, cur_timespec);
+        // don't hint if previous hint's duration covers this hint's duration
+        if (elapsed_time <= (mDurationMs - finalDuration)) {
+            ALOGV("%s: Previous duration (%d) cover this (%d) elapsed: %lld", __func__,
+                  static_cast<int>(mDurationMs), static_cast<int>(finalDuration),
+                  static_cast<long long>(elapsed_time));
+            return;
+        }
+    }
+    mLastTimespec = cur_timespec;
+    mDurationMs = finalDuration;
+
+    ALOGV("%s: input: %d final duration: %d", __func__, duration, finalDuration);
+
+    if (mState == INTERACTION_STATE_WAITING)
+        AbortWaitLocked();
+    else if (mState == INTERACTION_STATE_IDLE)
+        PerfLock();
+
+    mState = INTERACTION_STATE_INTERACTION;
+    mCond.notify_one();
+}
+
+void InteractionHandler::Release() {
+    std::lock_guard<std::mutex> lk(mLock);
+    if (mState == INTERACTION_STATE_WAITING) {
+        ATRACE_CALL();
+        PerfRel();
+        mState = INTERACTION_STATE_IDLE;
+    } else {
+        // clear any wait aborts pending in event fd
+        uint64_t val;
+        ssize_t ret = read(mEventFd, &val, sizeof(val));
+
+        ALOGW_IF(ret < 0, "%s: failed to clear eventfd (%zd, %d)", __func__, ret, errno);
+    }
+}
+
+// should be called while locked
+void InteractionHandler::AbortWaitLocked() {
+    uint64_t val = 1;
+    ssize_t ret = write(mEventFd, &val, sizeof(val));
+    if (ret != sizeof(val))
+        ALOGW("Unable to write to event fd (%zd)", ret);
+}
+
+void InteractionHandler::WaitForIdle(int32_t wait_ms, int32_t timeout_ms) {
+    char data[MAX_LENGTH];
+    ssize_t ret;
+    struct pollfd pfd[2];
+
+    ATRACE_CALL();
+
+    ALOGV("%s: wait:%d timeout:%d", __func__, wait_ms, timeout_ms);
+
+    pfd[0].fd = mEventFd;
+    pfd[0].events = POLLIN;
+    pfd[1].fd = mIdleFd;
+    pfd[1].events = POLLPRI | POLLERR;
+
+    ret = poll(pfd, 1, wait_ms);
+    if (ret > 0) {
+        ALOGV("%s: wait aborted", __func__);
+        return;
+    } else if (ret < 0) {
+        ALOGE("%s: error in poll while waiting", __func__);
+        return;
+    }
+
+    ret = pread(mIdleFd, data, sizeof(data), 0);
+    if (!ret) {
+        ALOGE("%s: Unexpected EOF!", __func__);
+        return;
+    }
+
+    if (!strncmp(data, "idle", 4)) {
+        ALOGV("%s: already idle", __func__);
+        return;
+    }
+
+    ret = poll(pfd, 2, timeout_ms);
+    if (ret < 0)
+        ALOGE("%s: Error on waiting for idle (%zd)", __func__, ret);
+    else if (ret == 0)
+        ALOGV("%s: timed out waiting for idle", __func__);
+    else if (pfd[0].revents)
+        ALOGV("%s: wait for idle aborted", __func__);
+    else if (pfd[1].revents)
+        ALOGV("%s: idle detected", __func__);
+}
+
+void InteractionHandler::Routine() {
+    pthread_setname_np(pthread_self(), "DispIdle");
+    std::unique_lock<std::mutex> lk(mLock, std::defer_lock);
+
+    while (true) {
+        lk.lock();
+        mCond.wait(lk, [&] { return mState != INTERACTION_STATE_IDLE; });
+        if (mState == INTERACTION_STATE_UNINITIALIZED)
+            return;
+        mState = INTERACTION_STATE_WAITING;
+        lk.unlock();
+
+        WaitForIdle(kWaitMs, mDurationMs);
+        Release();
+    }
+}
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/InteractionHandler.h b/power-libperfmgr/aidl/InteractionHandler.h
new file mode 100644
index 0000000..c88f7d4
--- /dev/null
+++ b/power-libperfmgr/aidl/InteractionHandler.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+enum InteractionState {
+    INTERACTION_STATE_UNINITIALIZED,
+    INTERACTION_STATE_IDLE,
+    INTERACTION_STATE_INTERACTION,
+    INTERACTION_STATE_WAITING,
+};
+
+class InteractionHandler {
+  public:
+    InteractionHandler();
+    ~InteractionHandler();
+    bool Init();
+    void Exit();
+    void Acquire(int32_t duration);
+
+  private:
+    void Release();
+    void WaitForIdle(int32_t wait_ms, int32_t timeout_ms);
+    void AbortWaitLocked();
+    void Routine();
+
+    void PerfLock();
+    void PerfRel();
+
+    enum InteractionState mState;
+    int mIdleFd;
+    int mEventFd;
+    int32_t mDurationMs;
+    struct timespec mLastTimespec;
+    std::unique_ptr<std::thread> mThread;
+    std::mutex mLock;
+    std::condition_variable mCond;
+};
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/Power.cpp b/power-libperfmgr/aidl/Power.cpp
new file mode 100644
index 0000000..f1f4a1e
--- /dev/null
+++ b/power-libperfmgr/aidl/Power.cpp
@@ -0,0 +1,240 @@
+/*
+ * 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 "powerhal-libperfmgr"
+
+#include "Power.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <perfmgr/HintManager.h>
+#include <utils/Log.h>
+
+#include <mutex>
+
+#include "PowerHintSession.h"
+#include "PowerSessionManager.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using ::aidl::google::hardware::power::impl::pixel::PowerHintSession;
+using ::android::perfmgr::HintManager;
+
+constexpr char kPowerHalStateProp[] = "vendor.powerhal.state";
+constexpr char kPowerHalAudioProp[] = "vendor.powerhal.audio";
+constexpr char kPowerHalRenderingProp[] = "vendor.powerhal.rendering";
+
+extern bool isDeviceSpecificModeSupported(Mode type, bool* _aidl_return);
+extern bool setDeviceSpecificMode(Mode type, bool enabled);
+
+Power::Power()
+    : mInteractionHandler(nullptr),
+      mSustainedPerfModeOn(false) {
+    mInteractionHandler = std::make_unique<InteractionHandler>();
+    mInteractionHandler->Init();
+
+    std::string state = ::android::base::GetProperty(kPowerHalStateProp, "");
+    if (state == "SUSTAINED_PERFORMANCE") {
+        LOG(INFO) << "Initialize with SUSTAINED_PERFORMANCE on";
+        HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE");
+        mSustainedPerfModeOn = true;
+    } else {
+        LOG(INFO) << "Initialize PowerHAL";
+    }
+
+    state = ::android::base::GetProperty(kPowerHalAudioProp, "");
+    if (state == "AUDIO_STREAMING_LOW_LATENCY") {
+        LOG(INFO) << "Initialize with AUDIO_LOW_LATENCY on";
+        HintManager::GetInstance()->DoHint(state);
+    }
+
+    state = ::android::base::GetProperty(kPowerHalRenderingProp, "");
+    if (state == "EXPENSIVE_RENDERING") {
+        LOG(INFO) << "Initialize with EXPENSIVE_RENDERING on";
+        HintManager::GetInstance()->DoHint("EXPENSIVE_RENDERING");
+    }
+}
+
+ndk::ScopedAStatus Power::setMode(Mode type, bool enabled) {
+    LOG(DEBUG) << "Power setMode: " << toString(type) << " to: " << enabled;
+    if (HintManager::GetInstance()->GetAdpfProfile() &&
+        HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
+        PowerSessionManager::getInstance()->updateHintMode(toString(type), enabled);
+    }
+    if (setDeviceSpecificMode(type, enabled)) {
+        return ndk::ScopedAStatus::ok();
+    }
+
+    switch (type) {
+        case Mode::SUSTAINED_PERFORMANCE:
+            if (enabled) {
+                HintManager::GetInstance()->DoHint("SUSTAINED_PERFORMANCE");
+            }
+            mSustainedPerfModeOn = true;
+            break;
+        case Mode::LAUNCH:
+            if (mSustainedPerfModeOn) {
+                break;
+            }
+            [[fallthrough]];
+        case Mode::DOUBLE_TAP_TO_WAKE:
+            [[fallthrough]];
+        case Mode::FIXED_PERFORMANCE:
+            [[fallthrough]];
+        case Mode::EXPENSIVE_RENDERING:
+            [[fallthrough]];
+        case Mode::INTERACTIVE:
+            [[fallthrough]];
+        case Mode::DEVICE_IDLE:
+            [[fallthrough]];
+        case Mode::DISPLAY_INACTIVE:
+            [[fallthrough]];
+        case Mode::AUDIO_STREAMING_LOW_LATENCY:
+            [[fallthrough]];
+        case Mode::GAME_LOADING:
+            [[fallthrough]];
+        default:
+            if (enabled) {
+                HintManager::GetInstance()->DoHint(toString(type));
+            } else {
+                HintManager::GetInstance()->EndHint(toString(type));
+            }
+            break;
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Power::isModeSupported(Mode type, bool *_aidl_return) {
+    if (isDeviceSpecificModeSupported(type, _aidl_return)) {
+        return ndk::ScopedAStatus::ok();
+    }
+
+    bool supported = HintManager::GetInstance()->IsHintSupported(toString(type));
+    LOG(INFO) << "Power mode " << toString(type) << " isModeSupported: " << supported;
+    *_aidl_return = supported;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Power::setBoost(Boost type, int32_t durationMs) {
+    LOG(DEBUG) << "Power setBoost: " << toString(type) << " duration: " << durationMs;
+    if (HintManager::GetInstance()->GetAdpfProfile() &&
+        HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
+        PowerSessionManager::getInstance()->updateHintBoost(toString(type), durationMs);
+    }
+    switch (type) {
+        case Boost::INTERACTION:
+            if (mSustainedPerfModeOn) {
+                break;
+            }
+            mInteractionHandler->Acquire(durationMs);
+            break;
+        case Boost::DISPLAY_UPDATE_IMMINENT:
+            [[fallthrough]];
+        case Boost::ML_ACC:
+            [[fallthrough]];
+        case Boost::AUDIO_LAUNCH:
+            [[fallthrough]];
+        default:
+            if (mSustainedPerfModeOn) {
+                break;
+            }
+            if (durationMs > 0) {
+                HintManager::GetInstance()->DoHint(toString(type),
+                                                   std::chrono::milliseconds(durationMs));
+            } else if (durationMs == 0) {
+                HintManager::GetInstance()->DoHint(toString(type));
+            } else {
+                HintManager::GetInstance()->EndHint(toString(type));
+            }
+            break;
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Power::isBoostSupported(Boost type, bool *_aidl_return) {
+    bool supported = HintManager::GetInstance()->IsHintSupported(toString(type));
+    LOG(INFO) << "Power boost " << toString(type) << " isBoostSupported: " << supported;
+    *_aidl_return = supported;
+    return ndk::ScopedAStatus::ok();
+}
+
+constexpr const char *boolToString(bool b) {
+    return b ? "true" : "false";
+}
+
+binder_status_t Power::dump(int fd, const char **, uint32_t) {
+    std::string buf(::android::base::StringPrintf(
+            "HintManager Running: %s\n"
+            "SustainedPerformanceMode: %s\n",
+            boolToString(HintManager::GetInstance()->IsRunning()),
+            boolToString(mSustainedPerfModeOn)));
+    // Dump nodes through libperfmgr
+    HintManager::GetInstance()->DumpToFd(fd);
+    PowerSessionManager::getInstance()->dumpToFd(fd);
+    if (!::android::base::WriteStringToFd(buf, fd)) {
+        PLOG(ERROR) << "Failed to dump state to fd";
+    }
+    fsync(fd);
+    return STATUS_OK;
+}
+
+ndk::ScopedAStatus Power::createHintSession(int32_t tgid, int32_t uid,
+                                            const std::vector<int32_t> &threadIds,
+                                            int64_t durationNanos,
+                                            std::shared_ptr<IPowerHintSession> *_aidl_return) {
+    if (!HintManager::GetInstance()->GetAdpfProfile() ||
+        HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs <= 0) {
+        *_aidl_return = nullptr;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+    if (threadIds.size() == 0) {
+        LOG(ERROR) << "Error: threadIds.size() shouldn't be " << threadIds.size();
+        *_aidl_return = nullptr;
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    std::shared_ptr<IPowerHintSession> session = ndk::SharedRefBase::make<PowerHintSession>(
+            tgid, uid, threadIds, durationNanos);
+    *_aidl_return = session;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Power::getHintSessionPreferredRate(int64_t *outNanoseconds) {
+    *outNanoseconds = HintManager::GetInstance()->GetAdpfProfile()
+                              ? HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs
+                              : 0;
+    if (*outNanoseconds <= 0) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/Power.h b/power-libperfmgr/aidl/Power.h
new file mode 100644
index 0000000..c5bb2a8
--- /dev/null
+++ b/power-libperfmgr/aidl/Power.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/power/BnPower.h>
+
+#include <atomic>
+#include <memory>
+#include <thread>
+
+#include "InteractionHandler.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using ::aidl::android::hardware::power::Boost;
+using ::aidl::android::hardware::power::IPowerHintSession;
+using ::aidl::android::hardware::power::Mode;
+
+class Power : public ::aidl::android::hardware::power::BnPower {
+  public:
+    Power();
+    ndk::ScopedAStatus setMode(Mode type, bool enabled) override;
+    ndk::ScopedAStatus isModeSupported(Mode type, bool *_aidl_return) override;
+    ndk::ScopedAStatus setBoost(Boost type, int32_t durationMs) override;
+    ndk::ScopedAStatus isBoostSupported(Boost type, bool *_aidl_return) override;
+    ndk::ScopedAStatus createHintSession(int32_t tgid, int32_t uid,
+                                         const std::vector<int32_t> &threadIds,
+                                         int64_t durationNanos,
+                                         std::shared_ptr<IPowerHintSession> *_aidl_return) override;
+    ndk::ScopedAStatus getHintSessionPreferredRate(int64_t *outNanoseconds) override;
+    binder_status_t dump(int fd, const char **args, uint32_t numArgs) override;
+
+  private:
+    std::unique_ptr<InteractionHandler> mInteractionHandler;
+    std::atomic<bool> mSustainedPerfModeOn;
+};
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/PowerExt.cpp b/power-libperfmgr/aidl/PowerExt.cpp
new file mode 100644
index 0000000..7299a5f
--- /dev/null
+++ b/power-libperfmgr/aidl/PowerExt.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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 "android.hardware.power-service.lineage.ext-libperfmgr"
+
+#include "PowerExt.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <perfmgr/HintManager.h>
+#include <utils/Log.h>
+
+#include <mutex>
+
+#include "PowerSessionManager.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using ::android::perfmgr::HintManager;
+
+ndk::ScopedAStatus PowerExt::setMode(const std::string &mode, bool enabled) {
+    LOG(DEBUG) << "PowerExt setMode: " << mode << " to: " << enabled;
+
+    if (enabled) {
+        HintManager::GetInstance()->DoHint(mode);
+    } else {
+        HintManager::GetInstance()->EndHint(mode);
+    }
+    if (HintManager::GetInstance()->GetAdpfProfile() &&
+        HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
+        PowerSessionManager::getInstance()->updateHintMode(mode, enabled);
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerExt::isModeSupported(const std::string &mode, bool *_aidl_return) {
+    bool supported = HintManager::GetInstance()->IsHintSupported(mode);
+    LOG(INFO) << "PowerExt mode " << mode << " isModeSupported: " << supported;
+    *_aidl_return = supported;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerExt::setBoost(const std::string &boost, int32_t durationMs) {
+    LOG(DEBUG) << "PowerExt setBoost: " << boost << " duration: " << durationMs;
+    if (HintManager::GetInstance()->GetAdpfProfile() &&
+        HintManager::GetInstance()->GetAdpfProfile()->mReportingRateLimitNs > 0) {
+        PowerSessionManager::getInstance()->updateHintBoost(boost, durationMs);
+    }
+
+    if (durationMs > 0) {
+        HintManager::GetInstance()->DoHint(boost, std::chrono::milliseconds(durationMs));
+    } else if (durationMs == 0) {
+        HintManager::GetInstance()->DoHint(boost);
+    } else {
+        HintManager::GetInstance()->EndHint(boost);
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerExt::isBoostSupported(const std::string &boost, bool *_aidl_return) {
+    bool supported = HintManager::GetInstance()->IsHintSupported(boost);
+    LOG(INFO) << "PowerExt boost " << boost << " isBoostSupported: " << supported;
+    *_aidl_return = supported;
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/PowerExt.h b/power-libperfmgr/aidl/PowerExt.h
new file mode 100644
index 0000000..bed12b5
--- /dev/null
+++ b/power-libperfmgr/aidl/PowerExt.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <thread>
+
+#include <aidl/google/hardware/power/extension/pixel/BnPowerExt.h>
+#include <perfmgr/HintManager.h>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+class PowerExt : public ::aidl::google::hardware::power::extension::pixel::BnPowerExt {
+  public:
+    PowerExt() {}
+    ndk::ScopedAStatus setMode(const std::string &mode, bool enabled) override;
+    ndk::ScopedAStatus isModeSupported(const std::string &mode, bool *_aidl_return) override;
+    ndk::ScopedAStatus setBoost(const std::string &boost, int32_t durationMs) override;
+    ndk::ScopedAStatus isBoostSupported(const std::string &boost, bool *_aidl_return) override;
+
+  private:
+};
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/PowerHintSession.cpp b/power-libperfmgr/aidl/PowerHintSession.cpp
new file mode 100644
index 0000000..eadc4cf
--- /dev/null
+++ b/power-libperfmgr/aidl/PowerHintSession.cpp
@@ -0,0 +1,529 @@
+/*
+ * Copyright 2021 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 "powerhal-libperfmgr"
+#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
+
+#include "PowerHintSession.h"
+
+#include <android-base/logging.h>
+#include <android-base/parsedouble.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <perfmgr/AdpfConfig.h>
+#include <private/android_filesystem_config.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <utils/Trace.h>
+
+#include <atomic>
+
+#include "PowerSessionManager.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using ::android::base::StringPrintf;
+using ::android::perfmgr::AdpfConfig;
+using ::android::perfmgr::HintManager;
+using std::chrono::duration_cast;
+using std::chrono::nanoseconds;
+
+namespace {
+
+static inline int64_t ns_to_100us(int64_t ns) {
+    return ns / 100000;
+}
+
+}  // namespace
+
+int64_t PowerHintSession::convertWorkDurationToBoostByPid(
+        const std::vector<WorkDuration> &actualDurations) {
+    std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
+    const nanoseconds &targetDuration = mDescriptor->duration;
+    int64_t &integral_error = mDescriptor->integral_error;
+    int64_t &previous_error = mDescriptor->previous_error;
+    uint64_t samplingWindowP = adpfConfig->mSamplingWindowP;
+    uint64_t samplingWindowI = adpfConfig->mSamplingWindowI;
+    uint64_t samplingWindowD = adpfConfig->mSamplingWindowD;
+    int64_t targetDurationNanos = (int64_t)targetDuration.count();
+    int64_t length = actualDurations.size();
+    int64_t p_start =
+            samplingWindowP == 0 || samplingWindowP > length ? 0 : length - samplingWindowP;
+    int64_t i_start =
+            samplingWindowI == 0 || samplingWindowI > length ? 0 : length - samplingWindowI;
+    int64_t d_start =
+            samplingWindowD == 0 || samplingWindowD > length ? 0 : length - samplingWindowD;
+    int64_t dt = ns_to_100us(targetDurationNanos);
+    int64_t err_sum = 0;
+    int64_t derivative_sum = 0;
+    for (int64_t i = std::min({p_start, i_start, d_start}); i < length; i++) {
+        int64_t actualDurationNanos = actualDurations[i].durationNanos;
+        if (std::abs(actualDurationNanos) > targetDurationNanos * 20) {
+            ALOGW("The actual duration is way far from the target (%" PRId64 " >> %" PRId64 ")",
+                  actualDurationNanos, targetDurationNanos);
+        }
+        // PID control algorithm
+        int64_t error = ns_to_100us(actualDurationNanos - targetDurationNanos);
+        if (i >= d_start) {
+            derivative_sum += error - previous_error;
+        }
+        if (i >= p_start) {
+            err_sum += error;
+        }
+        if (i >= i_start) {
+            integral_error += error * dt;
+            integral_error = std::min(adpfConfig->getPidIHighDivI(), integral_error);
+            integral_error = std::max(adpfConfig->getPidILowDivI(), integral_error);
+        }
+        previous_error = error;
+    }
+    int64_t pOut = static_cast<int64_t>((err_sum > 0 ? adpfConfig->mPidPo : adpfConfig->mPidPu) *
+                                        err_sum / (length - p_start));
+    int64_t iOut = static_cast<int64_t>(adpfConfig->mPidI * integral_error);
+    int64_t dOut =
+            static_cast<int64_t>((derivative_sum > 0 ? adpfConfig->mPidDo : adpfConfig->mPidDu) *
+                                 derivative_sum / dt / (length - d_start));
+
+    int64_t output = pOut + iOut + dOut;
+    if (ATRACE_ENABLED()) {
+        traceSessionVal("pid.err", err_sum / (length - p_start));
+        traceSessionVal("pid.integral", integral_error);
+        traceSessionVal("pid.derivative", derivative_sum / dt / (length - d_start));
+        traceSessionVal("pid.pOut", pOut);
+        traceSessionVal("pid.iOut", iOut);
+        traceSessionVal("pid.dOut", dOut);
+        traceSessionVal("pid.output", output);
+    }
+    return output;
+}
+
+PowerHintSession::PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
+                                   int64_t durationNanos)
+    : mStaleTimerHandler(sp<StaleTimerHandler>::make(this)),
+      mBoostTimerHandler(sp<BoostTimerHandler>::make(this)) {
+    mDescriptor = new AppHintDesc(tgid, uid, threadIds);
+    mDescriptor->duration = std::chrono::nanoseconds(durationNanos);
+    mIdString = StringPrintf("%" PRId32 "-%" PRId32 "-%" PRIxPTR, mDescriptor->tgid,
+                             mDescriptor->uid, reinterpret_cast<uintptr_t>(this) & 0xffff);
+
+    mPowerManagerHandler = PowerSessionManager::getInstance();
+    mLastUpdatedTime.store(std::chrono::steady_clock::now());
+    if (ATRACE_ENABLED()) {
+        traceSessionVal("target", mDescriptor->duration.count());
+        traceSessionVal("active", mDescriptor->is_active.load());
+    }
+    PowerSessionManager::getInstance()->addPowerSession(this);
+    // init boost
+    sendHint(SessionHint::CPU_LOAD_RESET);
+    ALOGV("PowerHintSession created: %s", mDescriptor->toString().c_str());
+}
+
+PowerHintSession::~PowerHintSession() {
+    close();
+    ALOGV("PowerHintSession deleted: %s", mDescriptor->toString().c_str());
+    if (ATRACE_ENABLED()) {
+        traceSessionVal("target", 0);
+        traceSessionVal("actl_last", 0);
+        traceSessionVal("active", 0);
+    }
+    delete mDescriptor;
+}
+
+void PowerHintSession::traceSessionVal(char const *identifier, int64_t val) const {
+    ATRACE_INT(StringPrintf("adpf.%s-%s", mIdString.c_str(), identifier).c_str(), val);
+}
+
+bool PowerHintSession::isAppSession() {
+    // Check if uid is in range reserved for applications
+    return mDescriptor->uid >= AID_APP_START;
+}
+
+void PowerHintSession::updateUniveralBoostMode() {
+    if (!isAppSession()) {
+        return;
+    }
+    if (ATRACE_ENABLED()) {
+        const std::string tag = StringPrintf("%s:updateUniveralBoostMode()", mIdString.c_str());
+        ATRACE_BEGIN(tag.c_str());
+    }
+    PowerHintMonitor::getInstance()->getLooper()->sendMessage(mPowerManagerHandler, NULL);
+    if (ATRACE_ENABLED()) {
+        ATRACE_END();
+    }
+}
+
+void PowerHintSession::tryToSendPowerHint(std::string hint) {
+    if (!mSupportedHints[hint].has_value()) {
+        mSupportedHints[hint] = HintManager::GetInstance()->IsHintSupported(hint);
+    }
+    if (mSupportedHints[hint].value()) {
+        HintManager::GetInstance()->DoHint(hint);
+    }
+}
+
+int PowerHintSession::setSessionUclampMin(int32_t min, bool resetStale) {
+    {
+        std::lock_guard<std::mutex> guard(mSessionLock);
+        mDescriptor->current_min = min;
+    }
+    if (min != 0 && resetStale) {
+        mStaleTimerHandler->updateTimer();
+    }
+    PowerSessionManager::getInstance()->setUclampMin(this, min);
+
+    if (ATRACE_ENABLED()) {
+        traceSessionVal("min", min);
+    }
+    return 0;
+}
+
+int PowerHintSession::getUclampMin() {
+    return mDescriptor->current_min;
+}
+
+void PowerHintSession::dumpToStream(std::ostream &stream) {
+    stream << "ID.Min.Act.Timeout(" << mIdString;
+    stream << ", " << mDescriptor->current_min;
+    stream << ", " << mDescriptor->is_active.load();
+    stream << ", " << isTimeout() << ")";
+}
+
+ndk::ScopedAStatus PowerHintSession::pause() {
+    if (mSessionClosed) {
+        ALOGE("Error: session is dead");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (!mDescriptor->is_active.load())
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    // Reset to default uclamp value.
+    mDescriptor->is_active.store(false);
+    setStale();
+    if (ATRACE_ENABLED()) {
+        traceSessionVal("active", mDescriptor->is_active.load());
+    }
+    updateUniveralBoostMode();
+    PowerSessionManager::getInstance()->removeThreadsFromPowerSession(this);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::resume() {
+    if (mSessionClosed) {
+        ALOGE("Error: session is dead");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (mDescriptor->is_active.load())
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    mDescriptor->is_active.store(true);
+    PowerSessionManager::getInstance()->addThreadsFromPowerSession(this);
+    // resume boost
+    setSessionUclampMin(mDescriptor->current_min);
+    if (ATRACE_ENABLED()) {
+        traceSessionVal("active", mDescriptor->is_active.load());
+    }
+    updateUniveralBoostMode();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::close() {
+    bool sessionClosedExpectedToBe = false;
+    if (!mSessionClosed.compare_exchange_strong(sessionClosedExpectedToBe, true)) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    // Remove the session from PowerSessionManager first to avoid racing.
+    PowerSessionManager::getInstance()->removePowerSession(this);
+    mStaleTimerHandler->setSessionDead();
+    mBoostTimerHandler->setSessionDead();
+    setSessionUclampMin(0);
+    mDescriptor->is_active.store(false);
+    updateUniveralBoostMode();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::updateTargetWorkDuration(int64_t targetDurationNanos) {
+    if (mSessionClosed) {
+        ALOGE("Error: session is dead");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (targetDurationNanos <= 0) {
+        ALOGE("Error: targetDurationNanos(%" PRId64 ") should bigger than 0", targetDurationNanos);
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    targetDurationNanos =
+            targetDurationNanos * HintManager::GetInstance()->GetAdpfProfile()->mTargetTimeFactor;
+    ALOGV("update target duration: %" PRId64 " ns", targetDurationNanos);
+
+    mDescriptor->duration = std::chrono::nanoseconds(targetDurationNanos);
+    if (ATRACE_ENABLED()) {
+        traceSessionVal("target", mDescriptor->duration.count());
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::reportActualWorkDuration(
+        const std::vector<WorkDuration> &actualDurations) {
+    if (mSessionClosed) {
+        ALOGE("Error: session is dead");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (mDescriptor->duration.count() == 0LL) {
+        ALOGE("Expect to call updateTargetWorkDuration() first.");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (actualDurations.size() == 0) {
+        ALOGE("Error: duration.size() shouldn't be %zu.", actualDurations.size());
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    if (!mDescriptor->is_active.load()) {
+        ALOGE("Error: shouldn't report duration during pause state.");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
+    mDescriptor->update_count++;
+    bool isFirstFrame = isTimeout();
+    if (ATRACE_ENABLED()) {
+        traceSessionVal("batch_size", actualDurations.size());
+        traceSessionVal("actl_last", actualDurations.back().durationNanos);
+        traceSessionVal("target", mDescriptor->duration.count());
+        traceSessionVal("hint.count", mDescriptor->update_count);
+        traceSessionVal("hint.overtime",
+                        actualDurations.back().durationNanos - mDescriptor->duration.count() > 0);
+    }
+
+    mLastUpdatedTime.store(std::chrono::steady_clock::now());
+    if (isFirstFrame) {
+        if (isAppSession()) {
+            tryToSendPowerHint("ADPF_FIRST_FRAME");
+        }
+        updateUniveralBoostMode();
+    }
+
+    disableTemporaryBoost();
+
+    if (!adpfConfig->mPidOn) {
+        setSessionUclampMin(adpfConfig->mUclampMinHigh);
+        return ndk::ScopedAStatus::ok();
+    }
+
+    int64_t output = convertWorkDurationToBoostByPid(actualDurations);
+
+    /* apply to all the threads in the group */
+    int next_min = std::min(static_cast<int>(adpfConfig->mUclampMinHigh),
+                            mDescriptor->current_min + static_cast<int>(output));
+    next_min = std::max(static_cast<int>(adpfConfig->mUclampMinLow), next_min);
+    setSessionUclampMin(next_min);
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::sendHint(SessionHint hint) {
+    if (mSessionClosed) {
+        ALOGE("Error: session is dead");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    disableTemporaryBoost();
+    std::shared_ptr<AdpfConfig> adpfConfig = HintManager::GetInstance()->GetAdpfProfile();
+    switch (hint) {
+        case SessionHint::CPU_LOAD_UP:
+            mNextUclampMin.store(mDescriptor->current_min);
+            mBoostTimerHandler->updateTimer(mDescriptor->duration * 2);
+            setSessionUclampMin(adpfConfig->mUclampMinHigh);
+            break;
+        case SessionHint::CPU_LOAD_DOWN:
+            setSessionUclampMin(adpfConfig->mUclampMinLow);
+            break;
+        case SessionHint::CPU_LOAD_RESET:
+            mNextUclampMin.store(std::max(adpfConfig->mUclampMinInit,
+                                          static_cast<uint32_t>(mDescriptor->current_min)));
+            mBoostTimerHandler->updateTimer(duration_cast<nanoseconds>(
+                    mDescriptor->duration * adpfConfig->mStaleTimeFactor / 2.0));
+            setSessionUclampMin(adpfConfig->mUclampMinHigh);
+            break;
+        case SessionHint::CPU_LOAD_RESUME:
+            setSessionUclampMin(mDescriptor->current_min);
+            break;
+        default:
+            ALOGE("Error: hint is invalid");
+            return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+    tryToSendPowerHint(toString(hint));
+    mLastUpdatedTime.store(std::chrono::steady_clock::now());
+    if (ATRACE_ENABLED()) {
+        mLastHintSent = static_cast<int>(hint);
+        traceSessionVal("session_hint", static_cast<int>(hint));
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerHintSession::setThreads(const std::vector<int32_t> &threadIds) {
+    if (mSessionClosed) {
+        ALOGE("Error: session is dead");
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    if (threadIds.size() == 0) {
+        LOG(ERROR) << "Error: threadIds.size() shouldn't be " << threadIds.size();
+        return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+    }
+
+    PowerSessionManager::getInstance()->removeThreadsFromPowerSession(this);
+    mDescriptor->threadIds.resize(threadIds.size());
+    std::copy(threadIds.begin(), threadIds.end(), back_inserter(mDescriptor->threadIds));
+    PowerSessionManager::getInstance()->addThreadsFromPowerSession(this);
+    // init boost
+    setSessionUclampMin(HintManager::GetInstance()->GetAdpfProfile()->mUclampMinInit);
+    return ndk::ScopedAStatus::ok();
+}
+
+std::string AppHintDesc::toString() const {
+    std::string out =
+            StringPrintf("session %" PRIxPTR "\n", reinterpret_cast<uintptr_t>(this) & 0xffff);
+    const int64_t durationNanos = duration.count();
+    out.append(StringPrintf("  duration: %" PRId64 " ns\n", durationNanos));
+    out.append(StringPrintf("  uclamp.min: %d \n", current_min));
+    out.append(StringPrintf("  uid: %d, tgid: %d\n", uid, tgid));
+
+    out.append("  threadIds: [");
+    bool first = true;
+    for (int tid : threadIds) {
+        if (!first) {
+            out.append(", ");
+        }
+        out.append(std::to_string(tid));
+        first = false;
+    }
+    out.append("]\n");
+    return out;
+}
+
+bool PowerHintSession::isActive() {
+    return mDescriptor->is_active.load();
+}
+
+bool PowerHintSession::isTimeout() {
+    auto now = std::chrono::steady_clock::now();
+    time_point<steady_clock> staleTime =
+            mLastUpdatedTime.load() +
+            nanoseconds(static_cast<int64_t>(
+                    mDescriptor->duration.count() *
+                    HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
+    return now >= staleTime;
+}
+
+const std::vector<int32_t> &PowerHintSession::getTidList() const {
+    return mDescriptor->threadIds;
+}
+
+bool PowerHintSession::disableTemporaryBoost() {
+    if (ATRACE_ENABLED()) {
+        if (mLastHintSent != -1) {
+            mLastHintSent = -1;
+            traceSessionVal("session_hint", -1);
+        }
+    }
+
+    // replace temporary uclamp_min value with true min
+    std::optional<int> trueMin = mNextUclampMin.load();
+    if (trueMin.has_value()) {
+        std::lock_guard<std::mutex> guard(mSessionLock);
+        mDescriptor->current_min = *trueMin;
+        mNextUclampMin.store(std::nullopt);
+        return true;
+    }
+
+    return false;
+}
+
+void PowerHintSession::setStale() {
+    // Make sure any temporary boost is disabled
+    disableTemporaryBoost();
+    // Reset to default uclamp value.
+    PowerSessionManager::getInstance()->setUclampMin(this, 0);
+    // Deliver a task to check if all sessions are inactive.
+    updateUniveralBoostMode();
+    if (ATRACE_ENABLED()) {
+        traceSessionVal("min", 0);
+    }
+}
+
+void PowerHintSession::SessionTimerHandler::updateTimer(nanoseconds delay) {
+    mTimeout.store(steady_clock::now() + delay);
+    {
+        std::lock_guard<std::mutex> guard(mMessageLock);
+        sp<MessageHandler> selfPtr = sp<MessageHandler>::fromExisting(this);
+        PowerHintMonitor::getInstance()->getLooper()->removeMessages(selfPtr);
+        PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(delay.count(), selfPtr,
+                                                                         NULL);
+    }
+    if (ATRACE_ENABLED()) {
+        mSession->traceSessionVal(("timer." + mName).c_str(), 0);
+    }
+}
+
+void PowerHintSession::SessionTimerHandler::handleMessage(const Message &) {
+    std::lock_guard<std::mutex> guard(mClosedLock);
+    if (mIsSessionDead) {
+        return;
+    }
+    time_point now = steady_clock::now();
+    int64_t next = (mTimeout.load() - now).count();
+    if (next > 0) {
+        // Schedule for the stale timeout check.
+        std::lock_guard<std::mutex> guard(mMessageLock);
+        sp<MessageHandler> selfPtr = sp<MessageHandler>::fromExisting(this);
+        PowerHintMonitor::getInstance()->getLooper()->removeMessages(selfPtr);
+        PowerHintMonitor::getInstance()->getLooper()->sendMessageDelayed(next, selfPtr, NULL);
+    } else {
+        onTimeout();
+    }
+    if (ATRACE_ENABLED()) {
+        mSession->traceSessionVal(("timer." + mName).c_str(), next > 0 ? 0 : 1);
+    }
+}
+
+void PowerHintSession::SessionTimerHandler::setSessionDead() {
+    std::lock_guard<std::mutex> guard(mClosedLock);
+    mIsSessionDead = true;
+    PowerHintMonitor::getInstance()->getLooper()->removeMessages(
+            sp<MessageHandler>::fromExisting(this));
+}
+
+void PowerHintSession::StaleTimerHandler::updateTimer() {
+    SessionTimerHandler::updateTimer(duration_cast<nanoseconds>(
+            mSession->mDescriptor->duration *
+            HintManager::GetInstance()->GetAdpfProfile()->mStaleTimeFactor));
+}
+
+void PowerHintSession::StaleTimerHandler::onTimeout() {
+    mSession->setStale();
+}
+
+void PowerHintSession::BoostTimerHandler::onTimeout() {
+    if (mSession->disableTemporaryBoost()) {
+        mSession->setSessionUclampMin(mSession->getUclampMin(), false);
+    }
+}
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/PowerHintSession.h b/power-libperfmgr/aidl/PowerHintSession.h
new file mode 100644
index 0000000..e1c2523
--- /dev/null
+++ b/power-libperfmgr/aidl/PowerHintSession.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/power/BnPowerHintSession.h>
+#include <aidl/android/hardware/power/SessionHint.h>
+#include <aidl/android/hardware/power/WorkDuration.h>
+#include <utils/Looper.h>
+#include <utils/Thread.h>
+
+#include <mutex>
+#include <unordered_map>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using aidl::android::hardware::power::BnPowerHintSession;
+using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::WorkDuration;
+using ::android::Message;
+using ::android::MessageHandler;
+using ::android::sp;
+using std::chrono::milliseconds;
+using std::chrono::nanoseconds;
+using std::chrono::steady_clock;
+using std::chrono::time_point;
+
+struct AppHintDesc {
+    AppHintDesc(int32_t tgid, int32_t uid, std::vector<int32_t> threadIds)
+        : tgid(tgid),
+          uid(uid),
+          threadIds(std::move(threadIds)),
+          duration(0LL),
+          current_min(0),
+          is_active(true),
+          update_count(0),
+          integral_error(0),
+          previous_error(0) {}
+    std::string toString() const;
+    const int32_t tgid;
+    const int32_t uid;
+    std::vector<int32_t> threadIds;
+    nanoseconds duration;
+    int current_min;
+    // status
+    std::atomic<bool> is_active;
+    // pid
+    uint64_t update_count;
+    int64_t integral_error;
+    int64_t previous_error;
+};
+
+class PowerHintSession : public BnPowerHintSession {
+  public:
+    explicit PowerHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t> &threadIds,
+                              int64_t durationNanos);
+    ~PowerHintSession();
+    ndk::ScopedAStatus close() override;
+    ndk::ScopedAStatus pause() override;
+    ndk::ScopedAStatus resume() override;
+    ndk::ScopedAStatus updateTargetWorkDuration(int64_t targetDurationNanos) override;
+    ndk::ScopedAStatus reportActualWorkDuration(
+            const std::vector<WorkDuration> &actualDurations) override;
+    ndk::ScopedAStatus sendHint(SessionHint hint) override;
+    ndk::ScopedAStatus setThreads(const std::vector<int32_t> &threadIds) override;
+    bool isActive();
+    bool isTimeout();
+    void setStale();
+    // Is this hint session for a user application
+    bool isAppSession();
+    const std::vector<int> &getTidList() const;
+    int getUclampMin();
+    void dumpToStream(std::ostream &stream);
+
+    // Disable any temporary boost and return to normal operation. It does not
+    // reset the actual uclamp value, and relies on the caller to do so, to
+    // prevent double-setting. Returns true if it actually disabled an active boost
+    bool disableTemporaryBoost();
+
+  private:
+    class SessionTimerHandler : public MessageHandler {
+      public:
+        SessionTimerHandler(PowerHintSession *session, std::string name)
+            : mSession(session), mIsSessionDead(false), mName(name) {}
+        void updateTimer(nanoseconds delay);
+        void handleMessage(const Message &message) override;
+        void setSessionDead();
+        virtual void onTimeout() = 0;
+
+      protected:
+        PowerHintSession *mSession;
+        std::mutex mClosedLock;
+        std::mutex mMessageLock;
+        std::atomic<time_point<steady_clock>> mTimeout;
+        bool mIsSessionDead;
+        const std::string mName;
+    };
+
+    class StaleTimerHandler : public SessionTimerHandler {
+      public:
+        StaleTimerHandler(PowerHintSession *session) : SessionTimerHandler(session, "stale") {}
+        void updateTimer();
+        void onTimeout() override;
+    };
+
+    class BoostTimerHandler : public SessionTimerHandler {
+      public:
+        BoostTimerHandler(PowerHintSession *session) : SessionTimerHandler(session, "boost") {}
+        void onTimeout() override;
+    };
+
+  private:
+    void updateUniveralBoostMode();
+    int setSessionUclampMin(int32_t min, bool resetStale = true);
+    void tryToSendPowerHint(std::string hint);
+    int64_t convertWorkDurationToBoostByPid(const std::vector<WorkDuration> &actualDurations);
+    void traceSessionVal(char const *identifier, int64_t val) const;
+    AppHintDesc *mDescriptor = nullptr;
+    sp<StaleTimerHandler> mStaleTimerHandler;
+    sp<BoostTimerHandler> mBoostTimerHandler;
+    std::atomic<time_point<steady_clock>> mLastUpdatedTime;
+    sp<MessageHandler> mPowerManagerHandler;
+    std::mutex mSessionLock;
+    std::atomic<bool> mSessionClosed = false;
+    std::string mIdString;
+    // Used when setting a temporary boost value to hold the true boost
+    std::atomic<std::optional<int>> mNextUclampMin;
+    // To cache the status of whether ADPF hints are supported.
+    std::unordered_map<std::string, std::optional<bool>> mSupportedHints;
+    // Last session hint sent, used for logging
+    int mLastHintSent = -1;
+};
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/PowerModeExtension.cpp b/power-libperfmgr/aidl/PowerModeExtension.cpp
new file mode 100644
index 0000000..50c5a70
--- /dev/null
+++ b/power-libperfmgr/aidl/PowerModeExtension.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The LineageOS 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.
+ */
+
+#include <aidl/android/hardware/power/BnPower.h>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using ::aidl::android::hardware::power::Mode;
+
+bool isDeviceSpecificModeSupported(Mode type, bool* _aidl_return) {
+    (void) type;
+    (void) _aidl_return;
+    return false;
+}
+
+bool setDeviceSpecificMode(Mode type, bool enabled) {
+    (void) type;
+    (void) enabled;
+    return false;
+}
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/PowerSessionManager.cpp b/power-libperfmgr/aidl/PowerSessionManager.cpp
new file mode 100644
index 0000000..2d2aad2
--- /dev/null
+++ b/power-libperfmgr/aidl/PowerSessionManager.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2021 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 "powerhal-libperfmgr"
+#define ATRACE_TAG (ATRACE_TAG_POWER | ATRACE_TAG_HAL)
+
+#include "PowerSessionManager.h"
+
+#include <android-base/file.h>
+#include <log/log.h>
+#include <perfmgr/HintManager.h>
+#include <processgroup/processgroup.h>
+#include <sys/syscall.h>
+#include <utils/Trace.h>
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using ::android::perfmgr::AdpfConfig;
+using ::android::perfmgr::HintManager;
+
+namespace {
+/* there is no glibc or bionic wrapper */
+struct sched_attr {
+    __u32 size;
+    __u32 sched_policy;
+    __u64 sched_flags;
+    __s32 sched_nice;
+    __u32 sched_priority;
+    __u64 sched_runtime;
+    __u64 sched_deadline;
+    __u64 sched_period;
+    __u32 sched_util_min;
+    __u32 sched_util_max;
+};
+
+static int sched_setattr(int pid, struct sched_attr *attr, unsigned int flags) {
+    if (!HintManager::GetInstance()->GetAdpfProfile()->mUclampMinOn) {
+        ALOGV("PowerSessionManager:%s: skip", __func__);
+        return 0;
+    }
+    return syscall(__NR_sched_setattr, pid, attr, flags);
+}
+
+static void set_uclamp_min(int tid, int min) {
+    static constexpr int32_t kMaxUclampValue = 1024;
+    min = std::max(0, min);
+    min = std::min(min, kMaxUclampValue);
+
+    sched_attr attr = {};
+    attr.size = sizeof(attr);
+
+    attr.sched_flags = (SCHED_FLAG_KEEP_ALL | SCHED_FLAG_UTIL_CLAMP_MIN);
+    attr.sched_util_min = min;
+
+    int ret = sched_setattr(tid, &attr, 0);
+    if (ret) {
+        if (errno == ESRCH) {
+            ALOGV("sched_setattr failed for thread %d, err=%d", tid, errno);
+        } else {
+            ALOGW("sched_setattr failed for thread %d, err=%d", tid, errno);
+        }
+    }
+}
+}  // namespace
+
+void PowerSessionManager::updateHintMode(const std::string &mode, bool enabled) {
+    ALOGV("PowerSessionManager::updateHintMode: mode: %s, enabled: %d", mode.c_str(), enabled);
+    if (enabled && mode.compare(0, 8, "REFRESH_") == 0) {
+        if (mode.compare("REFRESH_120FPS") == 0) {
+            mDisplayRefreshRate = 120;
+        } else if (mode.compare("REFRESH_90FPS") == 0) {
+            mDisplayRefreshRate = 90;
+        } else if (mode.compare("REFRESH_60FPS") == 0) {
+            mDisplayRefreshRate = 60;
+        }
+    }
+    if (HintManager::GetInstance()->GetAdpfProfile()) {
+        HintManager::GetInstance()->SetAdpfProfile(mode);
+    }
+}
+
+void PowerSessionManager::updateHintBoost(const std::string &boost, int32_t durationMs) {
+    ATRACE_CALL();
+    ALOGV("PowerSessionManager::updateHintBoost: boost: %s, durationMs: %d", boost.c_str(),
+          durationMs);
+}
+
+int PowerSessionManager::getDisplayRefreshRate() {
+    return mDisplayRefreshRate;
+}
+
+void PowerSessionManager::addPowerSession(PowerHintSession *session) {
+    std::lock_guard<std::mutex> guard(mLock);
+    mSessions.insert(session);
+    addThreadsFromPowerSessionLocked(session);
+}
+
+void PowerSessionManager::removePowerSession(PowerHintSession *session) {
+    std::lock_guard<std::mutex> guard(mLock);
+    mSessions.erase(session);
+    removeThreadsFromPowerSessionLocked(session);
+}
+
+void PowerSessionManager::addThreadsFromPowerSession(PowerHintSession *session) {
+    std::lock_guard<std::mutex> guard(mLock);
+    addThreadsFromPowerSessionLocked(session);
+}
+
+void PowerSessionManager::addThreadsFromPowerSessionLocked(PowerHintSession *session) {
+    for (auto t : session->getTidList()) {
+        if (mTidSessionListMap[t].empty()) {
+            if (!SetTaskProfiles(t, {"ResetUclampGrp"})) {
+                ALOGW("Failed to set ResetUclampGrp task profile for tid:%d", t);
+            }
+        }
+        mTidSessionListMap[t].insert(session);
+    }
+}
+
+void PowerSessionManager::removeThreadsFromPowerSession(PowerHintSession *session) {
+    std::lock_guard<std::mutex> guard(mLock);
+    removeThreadsFromPowerSessionLocked(session);
+}
+
+void PowerSessionManager::removeThreadsFromPowerSessionLocked(PowerHintSession *session) {
+    for (auto t : session->getTidList()) {
+        size_t cnt = mTidSessionListMap[t].erase(session);
+        if (cnt != 0 && mTidSessionListMap[t].empty()) {
+            if (!SetTaskProfiles(t, {"NoResetUclampGrp"})) {
+                ALOGW("Failed to set NoResetUclampGrp task profile for tid:%d", t);
+            }
+        }
+    }
+}
+
+void PowerSessionManager::setUclampMin(PowerHintSession *session, int val) {
+    std::lock_guard<std::mutex> guard(mLock);
+    setUclampMinLocked(session, val);
+}
+
+void PowerSessionManager::setUclampMinLocked(PowerHintSession *session, int val) {
+    for (auto t : session->getTidList()) {
+        // Get thex max uclamp.min across sessions which include the tid.
+        int tidMax = 0;
+        for (PowerHintSession *s : mTidSessionListMap[t]) {
+            if (!s->isActive() || s->isTimeout())
+                continue;
+            tidMax = std::max(tidMax, s->getUclampMin());
+        }
+        set_uclamp_min(t, std::max(val, tidMax));
+    }
+}
+
+std::optional<bool> PowerSessionManager::isAnyAppSessionActive() {
+    std::lock_guard<std::mutex> guard(mLock);
+    bool active = false;
+    for (PowerHintSession *s : mSessions) {
+        // session active and not stale is actually active.
+        if (s->isActive() && !s->isTimeout() && s->isAppSession()) {
+            active = true;
+            break;
+        }
+    }
+    if (active == mActive) {
+        return std::nullopt;
+    } else {
+        mActive = active;
+    }
+
+    return active;
+}
+
+void PowerSessionManager::handleMessage(const Message &) {
+    auto active = isAnyAppSessionActive();
+    if (!active.has_value()) {
+        return;
+    }
+    if (active.value()) {
+        disableSystemTopAppBoost();
+    } else {
+        enableSystemTopAppBoost();
+    }
+}
+
+void PowerSessionManager::dumpToFd(int fd) {
+    std::ostringstream dump_buf;
+    std::lock_guard<std::mutex> guard(mLock);
+    dump_buf << "========== Begin PowerSessionManager ADPF list ==========\n";
+    for (PowerHintSession *s : mSessions) {
+        s->dumpToStream(dump_buf);
+        dump_buf << " Tid:Ref[";
+        for (size_t i = 0, len = s->getTidList().size(); i < len; i++) {
+            int t = s->getTidList()[i];
+            dump_buf << t << ":" << mTidSessionListMap[t].size();
+            if (i < len - 1) {
+                dump_buf << ", ";
+            }
+        }
+        dump_buf << "]\n";
+    }
+    dump_buf << "========== End PowerSessionManager ADPF list ==========\n";
+    if (!::android::base::WriteStringToFd(dump_buf.str(), fd)) {
+        ALOGE("Failed to dump one of session list to fd:%d", fd);
+    }
+}
+
+void PowerSessionManager::enableSystemTopAppBoost() {
+    if (HintManager::GetInstance()->IsHintSupported(kDisableBoostHintName)) {
+        ALOGV("PowerSessionManager::enableSystemTopAppBoost!!");
+        HintManager::GetInstance()->EndHint(kDisableBoostHintName);
+    }
+}
+
+void PowerSessionManager::disableSystemTopAppBoost() {
+    if (HintManager::GetInstance()->IsHintSupported(kDisableBoostHintName)) {
+        ALOGV("PowerSessionManager::disableSystemTopAppBoost!!");
+        HintManager::GetInstance()->DoHint(kDisableBoostHintName);
+    }
+}
+
+// =========== PowerHintMonitor implementation start from here ===========
+void PowerHintMonitor::start() {
+    if (!isRunning()) {
+        run("PowerHintMonitor", ::android::PRIORITY_HIGHEST);
+    }
+}
+
+bool PowerHintMonitor::threadLoop() {
+    while (true) {
+        mLooper->pollOnce(-1);
+    }
+    return true;
+}
+
+sp<Looper> PowerHintMonitor::getLooper() {
+    return mLooper;
+}
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/PowerSessionManager.h b/power-libperfmgr/aidl/PowerSessionManager.h
new file mode 100644
index 0000000..7c74039
--- /dev/null
+++ b/power-libperfmgr/aidl/PowerSessionManager.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <android-base/properties.h>
+#include <perfmgr/HintManager.h>
+#include <utils/Looper.h>
+
+#include <mutex>
+#include <optional>
+#include <unordered_set>
+
+#include "PowerHintSession.h"
+
+namespace aidl {
+namespace google {
+namespace hardware {
+namespace power {
+namespace impl {
+namespace pixel {
+
+using ::android::Looper;
+using ::android::Message;
+using ::android::MessageHandler;
+using ::android::Thread;
+using ::android::perfmgr::HintManager;
+
+constexpr char kPowerHalAdpfDisableTopAppBoost[] = "vendor.powerhal.adpf.disable.hint";
+
+class PowerSessionManager : public MessageHandler {
+  public:
+    // current hint info
+    void updateHintMode(const std::string &mode, bool enabled);
+    void updateHintBoost(const std::string &boost, int32_t durationMs);
+    int getDisplayRefreshRate();
+    // monitoring session status
+    void addPowerSession(PowerHintSession *session);
+    void removePowerSession(PowerHintSession *session);
+    void addThreadsFromPowerSession(PowerHintSession *session);
+    void addThreadsFromPowerSessionLocked(PowerHintSession *session);
+    void removeThreadsFromPowerSession(PowerHintSession *session);
+    void removeThreadsFromPowerSessionLocked(PowerHintSession *session);
+    void setUclampMin(PowerHintSession *session, int min);
+    void setUclampMinLocked(PowerHintSession *session, int min);
+    void handleMessage(const Message &message) override;
+    void dumpToFd(int fd);
+
+    // Singleton
+    static sp<PowerSessionManager> getInstance() {
+        static sp<PowerSessionManager> instance = new PowerSessionManager();
+        return instance;
+    }
+
+  private:
+    std::optional<bool> isAnyAppSessionActive();
+    void disableSystemTopAppBoost();
+    void enableSystemTopAppBoost();
+    const std::string kDisableBoostHintName;
+
+    std::unordered_set<PowerHintSession *> mSessions;  // protected by mLock
+    std::unordered_map<int, std::unordered_set<PowerHintSession *>> mTidSessionListMap;
+    bool mActive;  // protected by mLock
+    /**
+     * mLock to pretect the above data objects opertions.
+     **/
+    std::mutex mLock;
+    int mDisplayRefreshRate;
+    // Singleton
+    PowerSessionManager()
+        : kDisableBoostHintName(::android::base::GetProperty(kPowerHalAdpfDisableTopAppBoost,
+                                                             "ADPF_DISABLE_TA_BOOST")),
+          mActive(false),
+          mDisplayRefreshRate(60) {}
+    PowerSessionManager(PowerSessionManager const &) = delete;
+    void operator=(PowerSessionManager const &) = delete;
+};
+
+class PowerHintMonitor : public Thread {
+  public:
+    void start();
+    bool threadLoop() override;
+    sp<Looper> getLooper();
+    // Singleton
+    static sp<PowerHintMonitor> getInstance() {
+        static sp<PowerHintMonitor> instance = new PowerHintMonitor();
+        return instance;
+    }
+    PowerHintMonitor(PowerHintMonitor const &) = delete;
+    void operator=(PowerHintMonitor const &) = delete;
+
+  private:
+    sp<Looper> mLooper;
+    // Singleton
+    PowerHintMonitor() : Thread(false), mLooper(new Looper(true)) {}
+};
+
+}  // namespace pixel
+}  // namespace impl
+}  // namespace power
+}  // namespace hardware
+}  // namespace google
+}  // namespace aidl
diff --git a/power-libperfmgr/aidl/android.hardware.power-service.lineage-libperfmgr.rc b/power-libperfmgr/aidl/android.hardware.power-service.lineage-libperfmgr.rc
new file mode 100644
index 0000000..150f4d5
--- /dev/null
+++ b/power-libperfmgr/aidl/android.hardware.power-service.lineage-libperfmgr.rc
@@ -0,0 +1,32 @@
+service vendor.power-hal-aidl /vendor/bin/hw/android.hardware.power-service.lineage-libperfmgr
+    class hal
+    user root
+    group system
+    priority -20
+
+on late-fs
+    start vendor.power-hal-aidl
+
+# Restart powerHAL when framework died
+on property:init.svc.zygote=restarting && property:vendor.powerhal.state=*
+    setprop vendor.powerhal.state ""
+    setprop vendor.powerhal.audio ""
+    setprop vendor.powerhal.rendering ""
+    restart vendor.power-hal-aidl
+
+# Clean up after b/163539793 resolved
+on property:vendor.powerhal.dalvik.vm.dex2oat-threads=*
+    setprop dalvik.vm.dex2oat-threads ${vendor.powerhal.dalvik.vm.dex2oat-threads}
+    setprop dalvik.vm.restore-dex2oat-threads ${vendor.powerhal.dalvik.vm.dex2oat-threads}
+
+on property:vendor.powerhal.dalvik.vm.dex2oat-cpu-set=*
+    setprop dalvik.vm.dex2oat-cpu-set ${vendor.powerhal.dalvik.vm.dex2oat-cpu-set}
+    setprop dalvik.vm.restore-dex2oat-cpu-set ${vendor.powerhal.dalvik.vm.dex2oat-cpu-set}
+
+# Restart powerHAL when debug property set
+on property:ro.debuggable=1 && property:vendor.powerhal.config.debug=*
+    restart vendor.power-hal-aidl
+
+on property:persist.vendor.powerhal.config.debug=*
+    setprop vendor.powerhal.config.debug ${persist.vendor.powerhal.config.debug}
+
diff --git a/power-libperfmgr/aidl/android.hardware.power-service.lineage.xml b/power-libperfmgr/aidl/android.hardware.power-service.lineage.xml
new file mode 100644
index 0000000..f5dd6b9
--- /dev/null
+++ b/power-libperfmgr/aidl/android.hardware.power-service.lineage.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.power</name>
+        <version>4</version>
+        <fqname>IPower/default</fqname>
+    </hal>
+</manifest>
diff --git a/power-libperfmgr/aidl/service.cpp b/power-libperfmgr/aidl/service.cpp
new file mode 100644
index 0000000..2fb2aad
--- /dev/null
+++ b/power-libperfmgr/aidl/service.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 "powerhal-libperfmgr"
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/binder_ibinder_platform.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <perfmgr/HintManager.h>
+
+#include <thread>
+
+#include "Power.h"
+#include "PowerExt.h"
+#include "PowerSessionManager.h"
+
+using aidl::google::hardware::power::impl::pixel::Power;
+using aidl::google::hardware::power::impl::pixel::PowerExt;
+using aidl::google::hardware::power::impl::pixel::PowerHintMonitor;
+using aidl::google::hardware::power::impl::pixel::PowerSessionManager;
+using ::android::perfmgr::HintManager;
+
+constexpr std::string_view kPowerHalInitProp("vendor.powerhal.init");
+
+int main() {
+    // Parse config but do not start the looper
+    std::shared_ptr<HintManager> hm = HintManager::GetInstance();
+    if (!hm) {
+        LOG(FATAL) << "HintManager Init failed";
+    }
+
+    // single thread
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+    // core service
+    std::shared_ptr<Power> pw = ndk::SharedRefBase::make<Power>();
+    ndk::SpAIBinder pwBinder = pw->asBinder();
+    AIBinder_setMinSchedulerPolicy(pwBinder.get(), SCHED_NORMAL, -20);
+
+    // extension service
+    std::shared_ptr<PowerExt> pwExt = ndk::SharedRefBase::make<PowerExt>();
+    auto pwExtBinder = pwExt->asBinder();
+    AIBinder_setMinSchedulerPolicy(pwExtBinder.get(), SCHED_NORMAL, -20);
+
+    // attach the extension to the same binder we will be registering
+    CHECK(STATUS_OK == AIBinder_setExtension(pwBinder.get(), pwExt->asBinder().get()));
+
+    const std::string instance = std::string() + Power::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(pw->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+    LOG(INFO) << "Lineage Power HAL AIDL Service with Extension is started.";
+
+    if (HintManager::GetInstance()->GetAdpfProfile()) {
+        PowerHintMonitor::getInstance()->start();
+    }
+
+    std::thread initThread([&]() {
+        ::android::base::WaitForProperty(kPowerHalInitProp.data(), "1");
+        HintManager::GetInstance()->Start();
+    });
+    initThread.detach();
+
+    ABinderProcess_joinThreadPool();
+
+    // should not reach
+    LOG(ERROR) << "Lineage Power HAL AIDL Service with Extension just died.";
+    return EXIT_FAILURE;
+}
diff --git a/radio/config/1.1-wrapper/Android.bp b/radio/config/1.1-wrapper/Android.bp
new file mode 100644
index 0000000..a2795f8
--- /dev/null
+++ b/radio/config/1.1-wrapper/Android.bp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The LineageOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+cc_binary {
+    name: "android.hardware.radio.config@1.1-service.wrapper",
+    owner: "lineage",
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["android.hardware.radio.config@1.1-service.wrapper.rc"],
+    vintf_fragments: ["android.hardware.radio.config@1.1-service.wrapper.xml"],
+    srcs: [
+        "RadioConfig.cpp",
+        "hidl-utils.cpp",
+        "service.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "libutils",
+        "android.hardware.radio@1.0",
+        "android.hardware.radio.config@1.0",
+        "android.hardware.radio.config@1.1",
+        "lineage.hardware.radio.config@1.0",
+        "lineage.hardware.radio.config@1.1",
+    ],
+}
diff --git a/radio/config/1.1-wrapper/RadioConfig.cpp b/radio/config/1.1-wrapper/RadioConfig.cpp
new file mode 100644
index 0000000..582712e
--- /dev/null
+++ b/radio/config/1.1-wrapper/RadioConfig.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2024 The LineageOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define LOG_TAG "RadioConfigWrapper"
+
+#include <android-base/logging.h>
+
+#include "RadioConfig.h"
+#include "hidl-utils.h"
+
+#include <vector>
+
+#define WRAP_V1_0_CALL(method, ...)                                            \
+    do {                                                                       \
+        auto realRadioConfig = mRealRadioConfig;                               \
+        if (realRadioConfig != nullptr) {                                      \
+            return realRadioConfig->method(__VA_ARGS__);                       \
+        }                                                                      \
+        LOG(ERROR) << __func__ << ": realRadioConfig is null";                 \
+        return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE); \
+    } while (0)
+
+#define MAYBE_WRAP_V1_1_CALL(method, ...)                    \
+    do {                                                     \
+        auto realRadioConfigV1_1 = mRealRadioConfigV1_1;     \
+        if (realRadioConfigV1_1 != nullptr) {                \
+            return realRadioConfigV1_1->method(__VA_ARGS__); \
+        }                                                    \
+    } while (0)
+
+namespace android::hardware::radio::config::implementation {
+
+using namespace ::android::hardware::hidl_utils;
+
+using ::android::hardware::radio::V1_0::IRadio;
+using ::android::hardware::radio::V1_0::RadioError;
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
+using ::android::hardware::radio::V1_0::RadioResponseType;
+
+RadioConfig::RadioConfig(
+        sp<::lineage::hardware::radio::config::V1_0::IRadioConfig> realRadioConfig,
+        sp<::lineage::hardware::radio::config::V1_1::IRadioConfig> realRadioConfigV1_1)
+    : mRealRadioConfig(realRadioConfig), mRealRadioConfigV1_1(realRadioConfigV1_1) {
+    android::base::SetLogger(android::base::LogdLogger(android::base::RADIO));
+}
+
+// Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
+Return<void> RadioConfig::setResponseFunctions(
+        const sp<::android::hardware::radio::config::V1_0::IRadioConfigResponse>&
+                radioConfigResponse,
+        const sp<::android::hardware::radio::config::V1_0::IRadioConfigIndication>&
+                radioConfigIndication) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mRadioConfigResponse = radioConfigResponse;
+    mRadioConfigResponseV1_1 =
+            ::android::hardware::radio::config::V1_1::IRadioConfigResponse::castFrom(
+                    mRadioConfigResponse)
+                    .withDefault(nullptr);
+
+    auto realRadioConfig = mRealRadioConfig;
+    if (realRadioConfig == nullptr) {
+        LOG(ERROR) << __func__ << ": realRadioConfig is null";
+        return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE);
+    }
+
+    return realRadioConfig->setResponseFunctions(
+            reinterpret_cast<
+                    const sp<::lineage::hardware::radio::config::V1_0::IRadioConfigResponse>&>(
+                    radioConfigResponse),
+            reinterpret_cast<
+                    const sp<::lineage::hardware::radio::config::V1_0::IRadioConfigIndication>&>(
+                    radioConfigIndication));
+}
+
+Return<void> RadioConfig::getSimSlotsStatus(int32_t serial) {
+    WRAP_V1_0_CALL(getSimSlotsStatus, serial);
+}
+
+Return<void> RadioConfig::setSimSlotsMapping(int32_t serial, const hidl_vec<uint32_t>& slotMap) {
+    WRAP_V1_0_CALL(setSimSlotsMapping, serial, slotMap);
+}
+
+// Methods from ::android::hardware::radio::config::V1_1::IRadioConfig follow.
+Return<void> RadioConfig::getPhoneCapability(int32_t serial) {
+    MAYBE_WRAP_V1_1_CALL(getPhoneCapability, serial);
+
+    auto radioConfigResponseV1_1 = mRadioConfigResponseV1_1;
+    if (radioConfigResponseV1_1 == nullptr) {
+        LOG(ERROR) << __func__ << ": radioConfigResponseV1_1 is null";
+        return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE);
+    }
+
+    RadioResponseInfo radioResponseInfo = {
+            RadioResponseType::SOLICITED,
+            serial,
+            RadioError::NONE,
+    };
+
+    /*
+     * Simulates android.hardware.radio.config@1.0 behavior
+     * Refer to convertHalPhoneCapability() on RILUtils.java
+     */
+    ::android::hardware::radio::config::V1_1::PhoneCapability phoneCapability = {
+            .maxActiveData = 0,
+            .maxActiveInternetData = 0,
+            .isInternetLingeringSupported = 0,
+            .logicalModemList = {},
+    };
+
+    radioConfigResponseV1_1->getPhoneCapabilityResponse(radioResponseInfo, phoneCapability);
+
+    return Void();
+}
+
+Return<void> RadioConfig::setPreferredDataModem(int32_t serial, uint8_t modemId) {
+    MAYBE_WRAP_V1_1_CALL(setPreferredDataModem, serial, modemId);
+
+    std::lock_guard<std::mutex> lock(mMutex);
+
+    std::vector<sp<IRadio>> radios;
+
+    uint8_t numSlots = 0;
+    while (true) {
+        auto radio = getRadioForModemId(numSlots);
+        if (radio == nullptr) {
+            break;
+        }
+
+        radios.push_back(radio);
+
+        numSlots++;
+    }
+
+    LOG(DEBUG) << __func__ << ": modemId = " << std::to_string(modemId)
+               << ", numSlots = " << std::to_string(numSlots);
+
+    RadioError radioError = RadioError::NONE;
+
+    if (radios.empty() || modemId >= radios.size()) {
+        LOG(ERROR) << __func__ << ": Invalid arguments";
+        radioError = RadioError::INVALID_ARGUMENTS;
+    } else {
+        for (uint8_t i = 0; i < radios.size(); i++) {
+            auto radio = radios[i];
+            if (radio == nullptr) {
+                LOG(ERROR) << __func__ << ": radios[" << std::to_string(i) << "] is null";
+                radioError = RadioError::INTERNAL_ERR;
+                continue;
+            }
+
+            radio->setDataAllowed(-1, i == modemId);
+        }
+    }
+
+    auto radioConfigResponseV1_1 = mRadioConfigResponseV1_1;
+    if (radioConfigResponseV1_1 == nullptr) {
+        LOG(ERROR) << __func__ << ": radioConfigResponseV1_1 is null";
+        return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE);
+    }
+
+    RadioResponseInfo radioResponseInfo = {
+            RadioResponseType::SOLICITED,
+            serial,
+            radioError,
+    };
+
+    radioConfigResponseV1_1->setPreferredDataModemResponse(radioResponseInfo);
+
+    return Void();
+}
+
+Return<void> RadioConfig::setModemsConfig(
+        int32_t serial,
+        const ::android::hardware::radio::config::V1_1::ModemsConfig& modemsConfig) {
+    // Cannot use MAYBE_WRAP_V1_1_CALL, needs reinterpret_cast
+    auto realRadioConfigV1_1 = mRealRadioConfigV1_1;
+    if (realRadioConfigV1_1 != nullptr) {
+        return realRadioConfigV1_1->setModemsConfig(
+                serial,
+                reinterpret_cast<const ::lineage::hardware::radio::config::V1_1::ModemsConfig&>(
+                        modemsConfig));
+    }
+
+    auto radioConfigResponseV1_1 = mRadioConfigResponseV1_1;
+    if (radioConfigResponseV1_1 == nullptr) {
+        LOG(ERROR) << __func__ << ": radioConfigResponseV1_1 is null";
+        return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE);
+    }
+
+    radioConfigResponseV1_1->setModemsConfigResponse(getUnimplementedResponseInfo(serial));
+
+    return Void();
+}
+
+Return<void> RadioConfig::getModemsConfig(int32_t serial) {
+    MAYBE_WRAP_V1_1_CALL(getModemsConfig, serial);
+
+    auto radioConfigResponseV1_1 = mRadioConfigResponseV1_1;
+    if (radioConfigResponseV1_1 == nullptr) {
+        LOG(ERROR) << __func__ << ": radioConfigResponseV1_1 is null";
+        return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE);
+    }
+
+    radioConfigResponseV1_1->getModemsConfigResponse(getUnimplementedResponseInfo(serial), {});
+
+    return Void();
+}
+
+// Helper methods follow.
+sp<IRadio> RadioConfig::getRadioForModemId(uint8_t modemId) {
+    if (mModemIdToRadioCache.find(modemId) == mModemIdToRadioCache.end() ||
+        mModemIdToRadioCache[modemId] == nullptr) {
+        mModemIdToRadioCache[modemId] = IRadio::getService("slot" + std::to_string(modemId + 1));
+        if (mModemIdToRadioCache[modemId] != nullptr) {
+            hidl_utils::linkDeathToDeath(mModemIdToRadioCache[modemId]);
+        }
+    }
+
+    return mModemIdToRadioCache[modemId];
+}
+
+RadioResponseInfo RadioConfig::getUnimplementedResponseInfo(int32_t serial) {
+    return {
+            RadioResponseType::SOLICITED,
+            serial,
+            RadioError::REQUEST_NOT_SUPPORTED,
+    };
+}
+
+}  // namespace android::hardware::radio::config::implementation
diff --git a/radio/config/1.1-wrapper/RadioConfig.h b/radio/config/1.1-wrapper/RadioConfig.h
new file mode 100644
index 0000000..e3f7f50
--- /dev/null
+++ b/radio/config/1.1-wrapper/RadioConfig.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 The LineageOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include <android/hardware/radio/1.0/IRadio.h>
+#include <android/hardware/radio/config/1.0/IRadioConfig.h>
+#include <android/hardware/radio/config/1.1/IRadioConfig.h>
+#include <android/hardware/radio/config/1.1/IRadioConfigResponse.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <lineage/hardware/radio/config/1.1/IRadioConfig.h>
+#include <lineage/hardware/radio/config/1.1/IRadioConfigIndication.h>
+#include <lineage/hardware/radio/config/1.1/IRadioConfigResponse.h>
+
+#include <map>
+#include <mutex>
+
+namespace android::hardware::radio::config::implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_array;
+using ::android::hardware::hidl_memory;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+struct RadioConfig : public V1_1::IRadioConfig {
+  public:
+    RadioConfig(sp<::lineage::hardware::radio::config::V1_0::IRadioConfig> realRadioConfig,
+                sp<::lineage::hardware::radio::config::V1_1::IRadioConfig> realRadioConfigV1_1);
+
+    // Methods from ::android::hardware::radio::config::V1_0::IRadioConfig follow.
+    Return<void> setResponseFunctions(
+            const sp<::android::hardware::radio::config::V1_0::IRadioConfigResponse>&
+                    radioConfigResponse,
+            const sp<::android::hardware::radio::config::V1_0::IRadioConfigIndication>&
+                    radioConfigIndication) override;
+    Return<void> getSimSlotsStatus(int32_t serial) override;
+    Return<void> setSimSlotsMapping(int32_t serial, const hidl_vec<uint32_t>& slotMap) override;
+
+    // Methods from ::android::hardware::radio::config::V1_1::IRadioConfig follow.
+    Return<void> getPhoneCapability(int32_t serial) override;
+    Return<void> setPreferredDataModem(int32_t serial, uint8_t modemId) override;
+    Return<void> setModemsConfig(
+            int32_t serial,
+            const ::android::hardware::radio::config::V1_1::ModemsConfig& modemsConfig) override;
+    Return<void> getModemsConfig(int32_t serial) override;
+
+  private:
+    sp<::lineage::hardware::radio::config::V1_0::IRadioConfig> mRealRadioConfig;
+    sp<::lineage::hardware::radio::config::V1_1::IRadioConfig> mRealRadioConfigV1_1;
+
+    sp<::android::hardware::radio::config::V1_0::IRadioConfigResponse> mRadioConfigResponse;
+    sp<::android::hardware::radio::config::V1_1::IRadioConfigResponse> mRadioConfigResponseV1_1;
+
+    std::map<uint8_t, sp<::android::hardware::radio::V1_0::IRadio>> mModemIdToRadioCache;
+
+    std::mutex mMutex;
+
+    // Helper methods follow.
+    sp<::android::hardware::radio::V1_0::IRadio> getRadioForModemId(uint8_t modemId);
+    ::android::hardware::radio::V1_0::RadioResponseInfo getUnimplementedResponseInfo(
+            int32_t serial);
+};
+
+}  // namespace android::hardware::radio::config::implementation
diff --git a/radio/config/1.1-wrapper/android.hardware.radio.config@1.1-service.wrapper.rc b/radio/config/1.1-wrapper/android.hardware.radio.config@1.1-service.wrapper.rc
new file mode 100644
index 0000000..26411ab
--- /dev/null
+++ b/radio/config/1.1-wrapper/android.hardware.radio.config@1.1-service.wrapper.rc
@@ -0,0 +1,6 @@
+service vendor.radio-config-1-1-wrapper /vendor/bin/hw/android.hardware.radio.config@1.1-service.wrapper
+    interface android.hardware.radio.config@1.0::IRadioConfig default
+    interface android.hardware.radio.config@1.1::IRadioConfig default
+    class hal
+    user system
+    group system
diff --git a/radio/config/1.1-wrapper/android.hardware.radio.config@1.1-service.wrapper.xml b/radio/config/1.1-wrapper/android.hardware.radio.config@1.1-service.wrapper.xml
new file mode 100644
index 0000000..1cee730
--- /dev/null
+++ b/radio/config/1.1-wrapper/android.hardware.radio.config@1.1-service.wrapper.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.radio.config</name>
+        <transport>hwbinder</transport>
+        <fqname>@1.1::IRadioConfig/default</fqname>
+    </hal>
+</manifest>
diff --git a/radio/config/1.1-wrapper/hidl-utils.cpp b/radio/config/1.1-wrapper/hidl-utils.cpp
new file mode 100644
index 0000000..fc0d54d
--- /dev/null
+++ b/radio/config/1.1-wrapper/hidl-utils.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include "hidl-utils.h"
+
+#include <android-base/logging.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+
+namespace android::hardware::hidl_utils {
+
+class HalDeathRecipient : public hidl_death_recipient {
+    void serviceDied(uint64_t /* cookie */, const wp<hidl::base::V1_0::IBase>& /* who */) override {
+        LOG(FATAL) << "One of the linked HALs died. Restarting...";
+    }
+};
+
+static const auto gHalDeathRecipient = sp<HalDeathRecipient>::make();
+
+void linkDeathToDeath(sp<::android::hidl::base::V1_0::IBase> hal) {
+    const auto linkStatus = hal->linkToDeath(gHalDeathRecipient, 0);
+    CHECK(linkStatus.withDefault(false)) << "Failed to link to HAL death";
+}
+
+hidl_vec<hidl_string> listManifestByInterface(const char* descriptor) {
+    auto manager = hidl::manager::V1_2::IServiceManager::getService();
+    hidl_vec<hidl_string> services;
+    manager->listManifestByInterface(descriptor, hidl_utils::fill(&services));
+    CHECK_GT(services.size(), 0u) << "No " << descriptor
+                                  << " services in manifest (missing privileges?)" << std::endl;
+    return services;
+}
+
+}  // namespace android::hardware::hidl_utils
diff --git a/radio/config/1.1-wrapper/hidl-utils.h b/radio/config/1.1-wrapper/hidl-utils.h
new file mode 100644
index 0000000..be3386f
--- /dev/null
+++ b/radio/config/1.1-wrapper/hidl-utils.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <android/hidl/base/1.0/IBase.h>
+
+#include <functional>
+
+namespace android::hardware::hidl_utils {
+
+/**
+ * Helper functor to fetch results from multi-return HIDL calls.
+ * It's meant to be used in place of _hidl_cb callbacks.
+ *
+ * Please note extracting these return variables outside of the callback scope requires making
+ * a copy of each return variable. This may be costly for frequently called HIDL methods with
+ * non-negligible return object size. Please be cautious about performance when using this.
+ *
+ * Example usage:
+ *     Result result;
+ *     sp<ISomeInterface> iface;
+ *     hidlObject->someMethod(arg1, arg2, hidl_utils::fill(&result, &iface)).assertOk();
+ *     // use result and iface
+ */
+template <typename... T>
+struct fill : public std::function<void(const T&...)> {
+    /**
+     * Create _hidl_cb functor that copies the call arguments to specified pointers.
+     *
+     * \param args... Targets to copy the call arguments to
+     */
+    fill(T*... args) : mTargets(args...) {}
+
+    void operator()(const T&... args) { copy<0, T...>(args...); }
+
+  private:
+    std::tuple<T*...> mTargets;
+
+    template <int Pos, typename First>
+    inline void copy(const First& first) {
+        *std::get<Pos>(mTargets) = first;
+    }
+
+    template <int Pos, typename First, typename... Rest>
+    inline void copy(const First& first, const Rest&... rest) {
+        *std::get<Pos>(mTargets) = first;
+        copy<Pos + 1, Rest...>(rest...);
+    }
+};
+
+/**
+ * Link to a given HALs death and restart the current process in such a case.
+ * \param hal HAL to which death to link
+ */
+void linkDeathToDeath(sp<hidl::base::V1_0::IBase> hal);
+
+/**
+ * List HAL instances of a given interface.
+ *
+ * \descriptor HIDL HAL descriptor
+ */
+hidl_vec<hidl_string> listManifestByInterface(const char* descriptor);
+
+}  // namespace android::hardware::hidl_utils
diff --git a/radio/config/1.1-wrapper/service.cpp b/radio/config/1.1-wrapper/service.cpp
new file mode 100644
index 0000000..04a4206
--- /dev/null
+++ b/radio/config/1.1-wrapper/service.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The LineageOS Project
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define LOG_TAG "android.hardware.radio.config@1.1-service.wrapper"
+
+#include <android-base/logging.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include "RadioConfig.h"
+#include "hidl-utils.h"
+
+using namespace android::hardware::hidl_utils;
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+using android::hardware::radio::config::implementation::RadioConfig;
+
+using android::OK;
+using android::sp;
+using android::status_t;
+
+int main() {
+    sp<lineage::hardware::radio::config::V1_0::IRadioConfig> realRadioConfig =
+            lineage::hardware::radio::config::V1_0::IRadioConfig::getService();
+    CHECK(realRadioConfig) << "Cannot get backend radio config V1.0 service.";
+    linkDeathToDeath(realRadioConfig);
+
+    sp<lineage::hardware::radio::config::V1_1::IRadioConfig> realRadioConfigV1_1 =
+            lineage::hardware::radio::config::V1_1::IRadioConfig::getService();
+    if (realRadioConfigV1_1 == nullptr) {
+        LOG(ERROR) << "Cannot get backend radio config V1.1 service (not fatal).";
+    } else {
+        linkDeathToDeath(realRadioConfigV1_1);
+    }
+
+    sp<android::hardware::radio::config::V1_1::IRadioConfig> radioConfig =
+            new RadioConfig(realRadioConfig, realRadioConfigV1_1);
+
+    configureRpcThreadpool(1, true);
+
+    status_t status = radioConfig->registerAsService();
+    CHECK_EQ(status, OK) << "Cannot register RadioConfig HAL service.";
+
+    LOG(INFO) << "RadioConfig HAL service ready.";
+
+    joinRpcThreadpool();
+
+    LOG(FATAL) << "RadioConfig HAL service failed to join thread pool.";
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/update-makefiles.sh b/update-makefiles.sh
index 848c2d6..6bb69e4 100755
--- a/update-makefiles.sh
+++ b/update-makefiles.sh
@@ -6,3 +6,8 @@
   "vendor.lineage:hardware/lineage/interfaces" \
   "android.hardware:hardware/interfaces" \
   "android.hidl:system/libhidl/transport"
+
+do_makefiles_update \
+  "lineage.hardware:hardware/lineage/interfaces/_backend" \
+  "android.hardware:hardware/interfaces" \
+  "android.hidl:system/libhidl/transport"