Routes thermalMitigationRequest to proper HAL APIs.

go/telephony-thermal-mitigation

Track whether an emergency call is pending for phone.

Necessary to know when thermal can turn off radio.

Bug: 158872959
Test: make
Change-Id: Iebe57e93d19486a1f3dc5e69abe326cc5d09e7fd
Merged-In: Iebe57e93d19486a1f3dc5e69abe326cc5d09e7fd
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 4473aaa..2834845 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -64,6 +64,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.ThermalMitigationResult;
 import android.telephony.CallForwardingInfo;
 import android.telephony.CarrierBandwidth;
 import android.telephony.CarrierConfigManager;
@@ -75,6 +76,7 @@
 import android.telephony.CellInfoGsm;
 import android.telephony.CellInfoWcdma;
 import android.telephony.ClientRequestStats;
+import android.telephony.DataThrottlingRequest;
 import android.telephony.ICellInfoCallback;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.LocationAccessPolicy;
@@ -93,6 +95,7 @@
 import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
 import android.telephony.TelephonyScanManager;
+import android.telephony.ThermalMitigationRequest;
 import android.telephony.UiccCardInfo;
 import android.telephony.UiccSlotInfo;
 import android.telephony.UssdResponse;
@@ -179,6 +182,8 @@
 import com.android.phone.vvm.RemoteVvmTaskManager;
 import com.android.phone.vvm.VisualVoicemailSettingsUtil;
 import com.android.phone.vvm.VisualVoicemailSmsFilterConfig;
+import com.android.services.telephony.TelecomAccountRegistry;
+import com.android.services.telephony.TelephonyConnectionService;
 import com.android.telephony.Rlog;
 
 import java.io.FileDescriptor;
@@ -298,6 +303,8 @@
     private static final int EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE = 96;
     private static final int CMD_GET_SYSTEM_SELECTION_CHANNELS = 97;
     private static final int EVENT_GET_SYSTEM_SELECTION_CHANNELS_DONE = 98;
+    private static final int CMD_SET_DATA_THROTTLING = 99;
+    private static final int EVENT_SET_DATA_THROTTLING_DONE = 100;
 
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
@@ -340,6 +347,8 @@
     private static final int TYPE_ALLOCATION_CODE_LENGTH = 8;
     private static final int MANUFACTURER_CODE_LENGTH = 8;
 
+    private static final int SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS = -1;
+
     /**
      * Experiment flag to enable erase modem config on reset network, default value is false
      */
@@ -1680,6 +1689,52 @@
                     getDefaultPhone().getContext().sendBroadcastAsUser(
                             intent, UserHandle.ALL, permission.USER_ACTIVITY);
                     break;
+
+                case CMD_SET_DATA_THROTTLING: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_SET_DATA_THROTTLING_DONE, request);
+                    DataThrottlingRequest dataThrottlingRequest =
+                            (DataThrottlingRequest) request.argument;
+                    Phone phone = getPhoneFromRequest(request);
+                    if (phone != null) {
+                        phone.setDataThrottling(onCompleted,
+                                request.workSource, dataThrottlingRequest.getDataThrottlingAction(),
+                                dataThrottlingRequest.getCompletionDurationMillis());
+                    } else {
+                        loge("setDataThrottling: No phone object");
+                        request.result =
+                                TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+                        notifyRequester(request);
+                    }
+
+                    break;
+                }
+                case EVENT_SET_DATA_THROTTLING_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+
+                    if (ar.exception == null) {
+                        request.result = TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS;
+                    } else if (ar.exception instanceof CommandException) {
+                        loge("setDataThrottling: CommandException: " + ar.exception);
+                        CommandException.Error error =
+                                ((CommandException) (ar.exception)).getCommandError();
+
+                        if (error == CommandException.Error.RADIO_NOT_AVAILABLE) {
+                            request.result = TelephonyManager
+                                .THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+                        } else if (error == CommandException.Error.INVALID_ARGUMENTS) {
+                            request.result = SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS;
+                        } else {
+                            request.result =
+                                    TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR;
+                        }
+                    } else {
+                        request.result = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR;
+                    }
+                    Log.w(LOG_TAG, "DataThrottlingResult = " + request.result);
+                    notifyRequester(request);
+                    break;
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -9064,4 +9119,157 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    /**
+     * Attempts to set the radio power state for thermal reason. This does not guarantee that the
+     * requested radio power state will actually be set. See {@link
+     * PhoneInternalInterface#setRadioPowerForReason} for more details.
+     *
+     * @param subId the subscription ID of the phone requesting to set the radio power state.
+     * @param enable {@code true} if trying to turn radio on.
+     * @return {@code true} if phone setRadioPowerForReason was called. Otherwise, returns {@code
+     * false}.
+     */
+    private boolean setRadioPowerForThermal(int subId, boolean enable) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            phone.setRadioPowerForReason(enable, Phone.RADIO_POWER_REASON_THERMAL);
+            return true;
+        }
+        return false;
+    }
+
+    private int handleDataThrottlingRequest(int subId,
+            DataThrottlingRequest dataThrottlingRequest) {
+        // Ensure that radio is on. If not able to power on due to phone being unavailable, return
+        // THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE.
+        if (!setRadioPowerForThermal(subId, true)) {
+            return TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+        }
+
+        setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_THERMAL, true);
+
+        int thermalMitigationResult =
+                (int) sendRequest(CMD_SET_DATA_THROTTLING, dataThrottlingRequest, subId);
+        if (thermalMitigationResult == SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS) {
+            throw new IllegalArgumentException("modem returned INVALID_ARGUMENTS");
+        }
+        return thermalMitigationResult;
+    }
+
+    /**
+     * Thermal mitigation request to control functionalities at modem.
+     *
+     * @param subId the id of the subscription.
+     * @param thermalMitigationRequest holds all necessary information to be passed down to modem.
+     *
+     * @return thermalMitigationResult enum as defined in android.telephony.Annotation.
+     */
+    @Override
+    @ThermalMitigationResult
+    public int sendThermalMitigationRequest(
+            int subId,
+            ThermalMitigationRequest thermalMitigationRequest) throws IllegalArgumentException {
+        enforceModifyPermission();
+
+        WorkSource workSource = getWorkSource(Binder.getCallingUid());
+        final long identity = Binder.clearCallingIdentity();
+
+        int thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_UNKNOWN_ERROR;
+        try {
+            int thermalMitigationAction = thermalMitigationRequest.getThermalMitigationAction();
+            switch (thermalMitigationAction) {
+                case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_DATA_THROTTLING:
+                    thermalMitigationResult =
+                        handleDataThrottlingRequest(subId,
+                                thermalMitigationRequest.getDataThrottlingRequest());
+                    break;
+                case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_VOICE_ONLY:
+                    if (thermalMitigationRequest.getDataThrottlingRequest() != null) {
+                        throw new IllegalArgumentException("dataThrottlingRequest must be null for "
+                                + "ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_VOICE_ONLY");
+                    }
+
+                    // Ensure that radio is on. If not able to power on due to phone being
+                    // unavailable, return THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE.
+                    if (!setRadioPowerForThermal(subId, true)) {
+                        thermalMitigationResult =
+                                TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+                        break;
+                    }
+
+                    setDataEnabledForReason(subId, TelephonyManager.DATA_ENABLED_REASON_THERMAL,
+                            false);
+                    thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS;
+                    break;
+                case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_RADIO_OFF:
+                    if (thermalMitigationRequest.getDataThrottlingRequest() != null) {
+                        throw new IllegalArgumentException("dataThrottlingRequest  must be null for"
+                                + " ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_RADIO_OFF");
+                    }
+
+                    TelecomAccountRegistry registry = TelecomAccountRegistry.getInstance(null);
+                    if (registry != null) {
+                        TelephonyConnectionService service =
+                                registry.getTelephonyConnectionService();
+                        Phone phone = getPhone(subId);
+                        if (phone == null) {
+                            thermalMitigationResult =
+                                TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+                            break;
+                        }
+
+                        if (PhoneConstantConversions.convertCallState(phone.getState())
+                                    != TelephonyManager.CALL_STATE_IDLE
+                                    || phone.isInEmergencySmsMode() || phone.isInEcm()
+                                    || (service != null && service.isEmergencyCallPending())) {
+                            String errorMessage = "Phone state is not valid. call state = "
+                                    + PhoneConstantConversions.convertCallState(phone.getState())
+                                    + " isInEmergencySmsMode = " + phone.isInEmergencySmsMode()
+                                    + " isInEmergencyCallbackMode = " + phone.isInEcm();
+                            errorMessage += service == null
+                                    ? " TelephonyConnectionService is null"
+                                    : " isEmergencyCallPending = "
+                                            + service.isEmergencyCallPending();
+                            Log.e(LOG_TAG, errorMessage);
+                            thermalMitigationResult =
+                                TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE;
+                            break;
+                        }
+                    } else {
+                        thermalMitigationResult =
+                                TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+                        break;
+                    }
+
+                    // Turn radio off. If not able to power off due to phone being unavailable,
+                    // return THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE.
+                    if (!setRadioPowerForThermal(subId, false)) {
+                        thermalMitigationResult =
+                                TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+                        break;
+                    }
+                    thermalMitigationResult =
+                        TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS;
+                    break;
+                default:
+                    throw new IllegalArgumentException("the requested thermalMitigationAction does "
+                            + "not exist. Requested action: " + thermalMitigationAction);
+            }
+        } catch (IllegalArgumentException e) {
+            throw e;
+        } catch (Exception e) {
+            Log.e(LOG_TAG, "thermalMitigationRequest. Exception e =" + e);
+            thermalMitigationResult = TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_ERROR;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+
+        if (DBG) {
+            log("thermalMitigationRequest returning with thermalMitigationResult: "
+                    + thermalMitigationResult);
+        }
+
+        return thermalMitigationResult;
+    }
 }
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 0e7c8ed..8b5b64f 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -1134,7 +1134,7 @@
         this.mTelephonyConnectionService = telephonyConnectionService;
     }
 
-    TelephonyConnectionService getTelephonyConnectionService() {
+    public TelephonyConnectionService getTelephonyConnectionService() {
         return mTelephonyConnectionService;
     }
 
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 322993a..0b1d56d 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -160,6 +160,9 @@
     private EmergencyTonePlayer mEmergencyTonePlayer;
     private HoldTracker mHoldTracker;
     private boolean mIsTtyEnabled;
+    /** Set to true when there is an emergency call pending which will potential trigger a dial.
+     * This must be set to false when the call is dialed. */
+    private volatile boolean mIsEmergencyCallPending;
 
     // Contains one TelephonyConnection that has placed a call and a memory of which Phones it has
     // already tried to connect with. There should be only one TelephonyConnection trying to place a
@@ -800,6 +803,10 @@
             if (mRadioOnHelper == null) {
                 mRadioOnHelper = new RadioOnHelper(this);
             }
+
+            if (isEmergencyNumber) {
+                mIsEmergencyCallPending = true;
+            }
             mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
                 @Override
                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
@@ -912,6 +919,14 @@
     }
 
     /**
+     * @return whether radio has recently been turned on for emergency call but hasn't actually
+     * dialed the call yet.
+     */
+    public boolean isEmergencyCallPending() {
+        return mIsEmergencyCallPending;
+    }
+
+    /**
      * Whether the cellular radio is power off because the device is on Bluetooth.
      */
     private boolean isRadioPowerDownOnBluetooth() {
@@ -936,6 +951,7 @@
                 for (Phone curPhone : mPhoneFactoryProxy.getPhones()) {
                     curPhone.setRadioPower(true, false, false, true);
                 }
+                mIsEmergencyCallPending = false;
             }
             return;
         }
@@ -951,6 +967,7 @@
                         Log.i(this, "handleOnComplete - delayDialForDdsSwitch result = " + result);
                         adjustAndPlaceOutgoingConnection(phone, originalConnection, request,
                                 numberToDial, handle, originalPhoneType, true);
+                        mIsEmergencyCallPending = false;
                     }
                 });
             }
@@ -961,6 +978,7 @@
                     mDisconnectCauseFactory.toTelecomDisconnectCause(
                             android.telephony.DisconnectCause.POWER_OFF,
                             "Failed to turn on radio."));
+            mIsEmergencyCallPending = false;
         }
     }