Implement SAR power backoff feature [1/2]

Implement the feature for Antenna SAR testing.

Issue: FP3-A11#231
Change-Id: I94e75a165c28ae71e15c79ad95b1141d626ea882
(cherry picked from commit 738098f50fda2939070b9c4c8dec4b5b6c1548b9)
diff --git a/packages/SystemUI/src/com/android/systemui/power/GsensorListener.java b/packages/SystemUI/src/com/android/systemui/power/GsensorListener.java
new file mode 100644
index 0000000..2da1e04
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/GsensorListener.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2020-2022 Fairphone B.V.
+ *
+ * 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 com.android.systemui.power;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.SystemClock;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+
+public class GsensorListener {
+    private final String TAG = "GsensorListener";
+    private SensorManager mSensorManager;
+    private TelephonyManager mTelephonyMgr;
+    private Sensor mAccelerometerSensor;
+    private Context mContext;
+    private LinkedList<float[]> mAccSensorEventList;
+    private float mAccXSum = 0;
+    private float mAccYSum = 0;
+    private float mAccZSum = 0;
+    private final float mThreshold = 0.1f;
+    private final int mKey = 1;
+    private final int mValueFixed = 0;
+    private final int mValueMoving = 1;
+    private int mDetectEventNumer = 3;
+    private int mCurrentState = 0; // 0=Fixed, 1=Moving
+    private long mStartMotionLessTime = -1L;
+    private boolean mIsActive;
+
+    public GsensorListener(Context context) {
+        mContext = context;
+        mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        mTelephonyMgr = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        update();
+    }
+
+    public void update() {
+        if (mAccelerometerSensor == null) {
+            mAccelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+        }
+    }
+
+    public void Register() {
+        mSensorManager.registerListener(sensorEventListener, mAccelerometerSensor, 100000);
+        mAccSensorEventList = new LinkedList();
+        mIsActive = true;
+        setTransmitPower(mKey, mValueFixed);
+    }
+
+    public void unRegister() {
+        mSensorManager.unregisterListener(sensorEventListener, mAccelerometerSensor);
+        clearAccSensorData();
+        // mCurrentState = 0;
+        mIsActive = false;
+        mStartMotionLessTime = -1L;
+        resetDefaulState();
+    }
+
+    SensorEventListener sensorEventListener =
+            new SensorEventListener() {
+                @Override
+                public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+                @Override
+                public void onSensorChanged(SensorEvent event) {
+                    addAccSensorData(event);
+                    if (mAccSensorEventList.size() < mDetectEventNumer) return;
+                    try {
+                        handleMotionDetection();
+                        removeFirstAccSensorData();
+                        return;
+                    } catch (java.util.NoSuchElementException paramSensorEvent) {
+                        Log.e(
+                                TAG,
+                                "[SW_SAR] onSensorChanged: Acc-sensor was already disabled. Ignore:"
+                                        + " "
+                                        + paramSensorEvent);
+                    }
+                }
+            };
+
+    private void addAccSensorData(SensorEvent Event) {
+        mAccXSum += Event.values[0];
+        mAccYSum += Event.values[1];
+        mAccZSum += Event.values[2];
+        mAccSensorEventList.add(Arrays.copyOf(Event.values, Event.values.length));
+    }
+
+    private void handleMotionDetection() {
+
+        if (isMotionDetectedByAcc() && mCurrentState != mValueMoving) {
+            mCurrentState = 1;
+            setTransmitPower(mKey, mCurrentState);
+        } else if (!isMotionDetectedByAcc() && mCurrentState != mValueFixed) {
+            mCurrentState = 0;
+            setTransmitPower(mKey, mCurrentState);
+        }
+    }
+
+    private boolean isMotionDetectedByAcc() {
+        float[] firstEvent = (float[]) mAccSensorEventList.getFirst();
+        float[] lastEvent = (float[]) mAccSensorEventList.getLast();
+        float mAccXSecond = mAccXSum - (firstEvent[0] + lastEvent[0]);
+        float mAccYSecond = mAccYSum - (firstEvent[1] + lastEvent[1]);
+        float mAccZSecond = mAccZSum - (firstEvent[2] + lastEvent[2]);
+        int detectMotion = 0;
+        if (Math.abs(mAccXSecond - firstEvent[0]) > mThreshold
+                || Math.abs(mAccYSecond - firstEvent[1]) > mThreshold
+                || Math.abs(mAccZSecond - firstEvent[2]) > mThreshold) {
+            detectMotion += 1;
+        }
+        if (Math.abs(lastEvent[0] - mAccXSecond) > mThreshold
+                || Math.abs(lastEvent[1] - mAccYSecond) > mThreshold
+                || Math.abs(lastEvent[2] - mAccZSecond) > mThreshold) {
+            detectMotion += 1;
+        }
+        if (detectMotion == mDetectEventNumer - 1) {
+            mStartMotionLessTime = -1L;
+            return true;
+        } else {
+            long mCurrentTime = SystemClock.elapsedRealtime();
+            if (mStartMotionLessTime == -1L) mStartMotionLessTime = mCurrentTime;
+            if ((mCurrentTime - mStartMotionLessTime < 500)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private void removeFirstAccSensorData() {
+        if (mAccSensorEventList.size() == 0) return;
+        float[] arrayOfFloat = (float[]) mAccSensorEventList.removeFirst();
+        mAccXSum -= arrayOfFloat[0];
+        mAccYSum -= arrayOfFloat[1];
+        mAccZSum -= arrayOfFloat[2];
+    }
+
+    private void clearAccSensorData() {
+        mAccSensorEventList.clear();
+        mAccXSum = 0;
+        mAccYSum = 0;
+        mAccZSum = 0;
+    }
+
+    public void resetDefaulState() {
+        if (!getActive() && mCurrentState == mValueMoving) {
+            setTransmitPower(mKey, mValueFixed);
+            mCurrentState = mValueFixed;
+        }
+    }
+
+    private void setTransmitPower(int key, int value) {
+        Log.d(TAG, "[SW_SAR] GsensorListener setTransmitPower key = " + key + ", value = " + value);
+        if (mTelephonyMgr != null) {
+            mTelephonyMgr.setTransmitPower(key, value);
+        }
+    }
+
+    public boolean getActive() {
+        return mIsActive;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PsensorListener.java b/packages/SystemUI/src/com/android/systemui/power/PsensorListener.java
new file mode 100644
index 0000000..2956356
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/PsensorListener.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2020-2022 Fairphone B.V.
+ *
+ * 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 com.android.systemui.power;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+public class PsensorListener {
+    private final String TAG = "PsensorListener";
+    private SensorManager mSensorManager;
+    private TelephonyManager mTelephonyMgr;
+    private Sensor mProximitySensor;
+    private Context mContext;
+    float mPsensorMaxRange = 0.0F;
+    private final int mKey = 2;
+    private final int mValueFar = 0;
+    private final int mValueNear = 2;
+    private int mCurrentState = 0; // 0=far, 2=near
+    private boolean mIsActive = false;
+
+    public PsensorListener(Context context) {
+        mContext = context;
+        mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+        mTelephonyMgr = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        update();
+    }
+
+    public void update() {
+        if (mProximitySensor == null) {
+            mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+        }
+
+        if (mProximitySensor != null) {
+            mPsensorMaxRange = mProximitySensor.getMaximumRange();
+        } else {
+            mPsensorMaxRange = 0.0F;
+            Log.d(
+                    TAG,
+                    "[SW_SAR] Psensor abnormal, using default value for mPsensorMaxRange = "
+                            + mPsensorMaxRange);
+        }
+    }
+
+    public void Register() {
+        mSensorManager.registerListener(
+                sensorEventListener, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+        mIsActive = true;
+    }
+
+    public void unRegister() {
+        mSensorManager.unregisterListener(sensorEventListener, mProximitySensor);
+        mIsActive = false;
+        resetDefaulState();
+    }
+
+    SensorEventListener sensorEventListener =
+            new SensorEventListener() {
+                @Override
+                public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+                @Override
+                public void onSensorChanged(SensorEvent event) {
+                    float f = event.values[0];
+                    int mDetectState;
+                    if (f < mPsensorMaxRange) {
+                        mDetectState = 2; // near
+                    } else {
+                        mDetectState = 0; // far
+                    }
+                    handleDistUpdate(mDetectState);
+                }
+            };
+
+    private void handleDistUpdate(int value) {
+        if (mCurrentState != value) {
+            Log.d(
+                    TAG,
+                    "[SW_SAR] PsensorListener CurrentState = "
+                            + mCurrentState
+                            + " OldState = "
+                            + value);
+            mCurrentState = value;
+            setTransmitPower(mKey, mCurrentState);
+        }
+    }
+
+    public void resetDefaulState() {
+        if (!getActive() && mCurrentState == mValueNear) {
+            setTransmitPower(mKey, mValueFar);
+            mCurrentState = mValueFar;
+        }
+    }
+
+    private void setTransmitPower(int key, int value) {
+        Log.d(TAG, "[SW_SAR] PsensorListener setTransmitPower key = " + key + ", value = " + value);
+        if (mTelephonyMgr != null) {
+            mTelephonyMgr.setTransmitPower(key, value);
+        }
+    }
+
+    public boolean getActive() {
+        return mIsActive;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 17655b4..b6f4a68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -24,6 +24,7 @@
 import android.net.NetworkCapabilities;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.SystemProperties;
 import android.provider.Settings.Global;
 import android.telephony.Annotation;
 import android.telephony.CdmaEriInformation;
@@ -58,6 +59,8 @@
 import com.android.systemui.statusbar.policy.FiveGServiceClient;
 import com.android.systemui.statusbar.policy.FiveGServiceClient.FiveGServiceState;
 import com.android.systemui.statusbar.policy.FiveGServiceClient.IFiveGStateListener;
+import com.android.systemui.power.PsensorListener;
+import com.android.systemui.power.GsensorListener;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
@@ -71,6 +74,8 @@
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
+import java.io.*;
+
 import org.codeaurora.internal.NrConfigType;
 import org.codeaurora.internal.NrIconType;
 
@@ -84,6 +89,8 @@
     @VisibleForTesting
     final PhoneStateListener mPhoneStateListener;
     // Save entire info for logging, we only use the id.
+    final PsensorListener mPsensorListener;
+    final GsensorListener mGsensorListener;
     final SubscriptionInfo mSubscriptionInfo;
 
     // @VisibleForDemoMode
@@ -116,6 +123,18 @@
 
     private ImsManager mImsManager;
     private FeatureConnector<ImsManager> mFeatureConnector;
+    private final String mDefaultSarDetectMethod = "SW"; // NONE, HW, SW
+    private String mSarDetectMethod;
+    private int mSwSarDataActive = 0; // 0=off, 1=on
+    private int mSwSarCallState = 0; // 0=off, 1=on
+    private final int mDataKey = 4;
+    private final int mCallKey = 8;
+    private int mSarSensorDataActive = 0; // 0=off, 1=on
+    private int mSarSensorCallState = 0; // 0=off, 1=on
+    private final int mDataValueOff = 0;
+    private final int mDataValueOn = 1;
+    private final int mCallValueOff = 0;
+    private final int mCallValueOn = 1;
 
     // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
     // need listener lists anymore.
@@ -133,6 +152,9 @@
         mSubscriptionInfo = info;
         mFiveGStateListener = new FiveGStateListener();
         mFiveGState = new FiveGServiceState();
+        mPsensorListener = new PsensorListener(context);
+        mGsensorListener = new GsensorListener(context);
+        setSarDetectMethod();
         mPhoneStateListener = new MobilePhoneStateListener((new Handler(receiverLooper))::post);
         mNetworkNameSeparator = getTextIfExists(R.string.status_bar_network_name_separator)
                 .toString();
@@ -556,18 +578,33 @@
                     statusIcon.contentDescription);
         }
         if (DEBUG) {
-            Log.d(mTag, "notifyListeners mConfig.alwaysShowNetworkTypeIcon="
-                    + mConfig.alwaysShowNetworkTypeIcon + "  getNetworkType:" + mTelephonyDisplayInfo.getNetworkType() +
-                    "/" + TelephonyManager.getNetworkTypeName(mTelephonyDisplayInfo.getNetworkType())
-                    + " voiceNetType=" + getVoiceNetworkType() + "/"
-                    + TelephonyManager.getNetworkTypeName(getVoiceNetworkType())
-                    + " showDataIcon=" + showDataIcon
-                    + " mConfig.alwaysShowDataRatIcon=" + mConfig.alwaysShowDataRatIcon
-                    + " icons.mDataType=" + icons.mDataType
-                    + " mConfig.showVolteIcon=" + mConfig.showVolteIcon
-                    + " isVolteSwitchOn=" + isVolteSwitchOn()
-                    + " volteIcon=" + volteIcon
-                    + " mConfig.showVowifiIcon=" + mConfig.showVowifiIcon);
+            Log.d(
+                    mTag,
+                    "notifyListeners mConfig.alwaysShowNetworkTypeIcon="
+                            + mConfig.alwaysShowNetworkTypeIcon
+                            + "  getNetworkType:"
+                            + mTelephonyDisplayInfo.getNetworkType()
+                            + "/"
+                            + TelephonyManager.getNetworkTypeName(
+                                    mTelephonyDisplayInfo.getNetworkType())
+                            + " voiceNetType="
+                            + getVoiceNetworkType()
+                            + "/"
+                            + TelephonyManager.getNetworkTypeName(getVoiceNetworkType())
+                            + " showDataIcon="
+                            + showDataIcon
+                            + " mConfig.alwaysShowDataRatIcon="
+                            + mConfig.alwaysShowDataRatIcon
+                            + " icons.mDataType="
+                            + icons.mDataType
+                            + " mConfig.showVolteIcon="
+                            + mConfig.showVolteIcon
+                            + " isVolteSwitchOn="
+                            + isVolteSwitchOn()
+                            + " volteIcon="
+                            + volteIcon
+                            + " mConfig.showVowifiIcon="
+                            + mConfig.showVowifiIcon);
         }
         callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
                 activityIn, activityOut, volteIcon, dataContentDescription, dataContentDescriptionHtml,
@@ -1037,6 +1074,110 @@
             if (DEBUG) {
                 Log.d(mTag, "onDataActivity: direction=" + direction);
             }
+
+            if (mSarDetectMethod.equals("SW")) {
+                switch (direction) {
+                    case TelephonyManager.DATA_ACTIVITY_IN:
+                    case TelephonyManager.DATA_ACTIVITY_OUT:
+                    case TelephonyManager.DATA_ACTIVITY_INOUT:
+                        if (mSwSarCallState != mCallValueOn) {
+                            enableSensorListener();
+                        }
+                        if (mSwSarDataActive != mDataValueOn) {
+                            setTransmitPower(mDataKey, mDataKey * mDataValueOn);
+                        }
+                        mSwSarDataActive = mDataValueOn;
+                        break;
+                    case TelephonyManager.DATA_ACTIVITY_NONE:
+                    case TelephonyManager.DATA_ACTIVITY_DORMANT:
+                        if (mSwSarCallState != mCallValueOn) {
+                            disableSensorListener();
+                        }
+                        if (mSwSarDataActive != mDataValueOff) {
+                            setTransmitPower(mDataKey, mDataKey * mDataValueOff);
+                        }
+                        mSwSarDataActive = mDataValueOff;
+                        break;
+                    default:
+                        break;
+                }
+            } else if (mSarDetectMethod.equals("HW")) {
+                int state_change = 0;
+                switch (direction) {
+                    case TelephonyManager.DATA_ACTIVITY_IN:
+                    case TelephonyManager.DATA_ACTIVITY_OUT:
+                    case TelephonyManager.DATA_ACTIVITY_INOUT:
+                        if (mSarSensorDataActive != mDataValueOn) {
+                            state_change = 1;
+                            Log.d(mTag, "[SAR_SENSOR] data_state_on");
+                        }
+                        mSarSensorDataActive = mDataValueOn;
+                        break;
+                    case TelephonyManager.DATA_ACTIVITY_NONE:
+                    case TelephonyManager.DATA_ACTIVITY_DORMANT:
+                        if (mSarSensorDataActive != mDataValueOff) {
+                            state_change = 1;
+                            Log.d(mTag, "[SAR_SENSOR] data_state_off");
+                        }
+                        mSarSensorDataActive = mDataValueOff;
+                        break;
+                    default:
+                        if (mSarSensorDataActive != mDataValueOff) {
+                            state_change = 1;
+                            Log.d(mTag, "[SAR_SENSOR] Unknown data state, set data_state_off.");
+                        }
+                        mSarSensorDataActive = mDataValueOff;
+                        break;
+                }
+                if (state_change == 1) {
+                    String data_state_on =
+                            "cat sys/devices/platform/soc/soc:sar_sensor/data_state_on";
+                    String data_state_off =
+                            "cat sys/devices/platform/soc/soc:sar_sensor/data_state_off";
+
+                    StringBuffer output = new StringBuffer();
+                    BufferedReader reader = null;
+
+                    try {
+                        Process process1 = null;
+                        if (mSarSensorDataActive == mDataValueOn) {
+                            process1 = Runtime.getRuntime().exec(data_state_on);
+                        } else {
+                            process1 = Runtime.getRuntime().exec(data_state_off);
+                        }
+
+                        try {
+                            process1.waitFor();
+                        } catch (InterruptedException ie) {
+                            ie.printStackTrace();
+                        }
+
+                        reader =
+                                new BufferedReader(
+                                        new InputStreamReader(process1.getInputStream()));
+
+                        String line = "";
+                        while ((line = reader.readLine()) != null) {
+                            output.append(line + "\n");
+                        }
+
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    } finally {
+                        try {
+                            if (reader != null) {
+                                reader.close();
+                                reader = null;
+                            }
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+
+                    Log.d(mTag, "[SAR_SENSOR] Driver interrupt status: " + output.toString());
+                }
+            }
+
             setActivity(direction);
         }
 
@@ -1070,6 +1211,100 @@
             if (DEBUG) {
                 Log.d(mTag, "onCallStateChanged: state=" + state);
             }
+            if (mSarDetectMethod.equals("SW")) {
+                switch (state) {
+                    case TelephonyManager.CALL_STATE_OFFHOOK:
+                    case TelephonyManager.CALL_STATE_RINGING:
+                        if (mSwSarCallState != mCallValueOn) {
+                            enableSensorListener();
+                            setTransmitPower(mCallKey, mCallKey * mCallValueOn);
+                        }
+                        mSwSarCallState = mCallValueOn;
+                        break;
+                    case TelephonyManager.CALL_STATE_IDLE:
+                        if (mSwSarCallState != mCallValueOff) {
+                            disableSensorListener();
+                            setTransmitPower(mCallKey, mCallKey * mCallValueOff);
+                        }
+                        mSwSarCallState = mCallValueOff;
+                        break;
+                    default:
+                        break;
+                }
+            } else if (mSarDetectMethod.equals("HW")) {
+                int state_change = 0;
+                switch (state) {
+                    case TelephonyManager.CALL_STATE_OFFHOOK:
+                    case TelephonyManager.CALL_STATE_RINGING:
+                        if (mSarSensorCallState != mCallValueOn) {
+                            state_change = 1;
+                            Log.d(mTag, "[SAR_SENSOR] call_state_on");
+                        }
+                        mSarSensorCallState = mCallValueOn;
+                        break;
+                    case TelephonyManager.CALL_STATE_IDLE:
+                        if (mSarSensorCallState != mCallValueOff) {
+                            state_change = 1;
+                            Log.d(mTag, "[SAR_SENSOR] call_state_off");
+                        }
+                        mSarSensorCallState = mCallValueOff;
+                        break;
+                    default:
+                        if (mSarSensorCallState != mCallValueOff) {
+                            state_change = 1;
+                            Log.d(mTag, "[SAR_SENSOR] Unknown data state, set call_state_off.");
+                        }
+                        mSarSensorCallState = mCallValueOff;
+                        break;
+                }
+                if (state_change == 1) {
+                    String call_state_on =
+                            "cat sys/devices/platform/soc/soc:sar_sensor/call_state_on";
+                    String call_state_off =
+                            "cat sys/devices/platform/soc/soc:sar_sensor/call_state_off";
+
+                    StringBuffer output = new StringBuffer();
+                    BufferedReader reader = null;
+
+                    try {
+                        Process process1 = null;
+                        if (mSarSensorCallState == mCallValueOn) {
+                            process1 = Runtime.getRuntime().exec(call_state_on);
+                        } else {
+                            process1 = Runtime.getRuntime().exec(call_state_off);
+                        }
+                        try {
+                            process1.waitFor();
+                        } catch (InterruptedException ie) {
+                            ie.printStackTrace();
+                        }
+
+                        reader =
+                                new BufferedReader(
+                                        new InputStreamReader(process1.getInputStream()));
+
+                        String line = "";
+                        while ((line = reader.readLine()) != null) {
+                            output.append(line + "\n");
+                        }
+
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    } finally {
+                        try {
+                            if (reader != null) {
+                                reader.close();
+                                reader = null;
+                            }
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+
+                    Log.d(mTag, "[SAR_SENSOR] Driver interrupt status: " + output.toString());
+                }
+            }
+
             mCallState = state;
             updateTelephony();
         }
@@ -1235,4 +1470,53 @@
                     && ((MobileState) o).roamingDataEnabled == roamingDataEnabled;
         }
     }
+
+    private void setSarDetectMethod() {
+        mSarDetectMethod = SystemProperties.get("persist.radio.backoff.method", "-1");
+        if (mSarDetectMethod.equals("NONE")
+                || mSarDetectMethod.equals("HW")
+                || mSarDetectMethod.equals("SW")) {
+            Log.d(
+                    mTag,
+                    "[SW_SAR/SAR_SENSOR] setSarDetectMethod mSarDetectMethod = "
+                            + mSarDetectMethod);
+        } else {
+            mSarDetectMethod = mDefaultSarDetectMethod;
+            Log.d(
+                    mTag,
+                    "[SW_SAR/SAR_SENSOR] setSarDetectMethod Undefined method, set to default"
+                        + " method, mSarDetectMethod = "
+                            + mSarDetectMethod);
+        }
+    }
+
+    private void enableSensorListener() {
+        if (!mPsensorListener.getActive()) {
+            mPsensorListener.Register();
+        }
+        if (!mGsensorListener.getActive()) {
+            mGsensorListener.Register();
+        }
+    }
+
+    private void disableSensorListener() {
+        if (mPsensorListener.getActive()) {
+            mPsensorListener.unRegister();
+        }
+        if (mGsensorListener.getActive()) {
+            mGsensorListener.unRegister();
+        }
+    }
+
+    private void setTransmitPower(int key, int value) {
+        if (mPhone != null) {
+            Log.d(
+                    mTag,
+                    "[SW_SAR] MobuileSignalController setTransmitPower key = "
+                            + key
+                            + ", value = "
+                            + value);
+            mPhone.setTransmitPower(key, value);
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a6e8236..3798def 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -13564,4 +13564,19 @@
             return true;
         }
     }
+
+    /** @hide */
+    public void setTransmitPower(int key, int value) {
+        Log.d(TAG, "[SW_SAR] Telephonymanager setTransmitPower");
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setTransmitPower(key, value);
+            }
+        } catch (RemoteException ex) {
+            Log.e(TAG, "[SW_SAR] error to get phone");
+        } catch (NullPointerException e) {
+            Log.e(TAG, "[SW_SAR] error to get phone");
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 363d6f5..fd890ce 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2315,4 +2315,6 @@
      * Whether device can connect to 5G network when two SIMs are active.
      */
     boolean canConnectTo5GInDsdsMode();
+
+    void setTransmitPower(int key, int value);
 }