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"