/*
 * 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.
 */

package com.android.internal.telephony;

import static android.telephony.TelephonyManager.HAL_SERVICE_MODEM;

import android.hardware.radio.RadioError;
import android.hardware.radio.RadioResponseInfo;
import android.hardware.radio.modem.IRadioModemResponse;
import android.hardware.radio.modem.ImeiInfo;
import android.os.SystemClock;
import android.telephony.ActivityStatsTechSpecificInfo;
import android.telephony.AnomalyReporter;
import android.telephony.ModemActivityInfo;

import java.util.ArrayList;
import java.util.UUID;

/**
 * Interface declaring response functions to solicited radio requests for modem APIs.
 */
public class ModemResponse extends IRadioModemResponse.Stub {
    private final RIL mRil;

    public ModemResponse(RIL ril) {
        mRil = ril;
    }

    /**
     * Acknowledge the receipt of radio request sent to the vendor. This must be sent only for
     * radio request which take long time to respond.
     * For more details, refer https://source.android.com/devices/tech/connect/ril.html
     * @param serial Serial no. of the request whose acknowledgement is sent.
     */
    public void acknowledgeRequest(int serial) {
        mRil.processRequestAck(serial);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial number and error.
     */
    public void enableModemResponse(RadioResponseInfo responseInfo) {
        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     * @param version String containing version string for log reporting
     */
    public void getBasebandVersionResponse(RadioResponseInfo responseInfo, String version) {
        RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, version);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     * @param imei IMEI if GSM subscription is available
     * @param imeisv IMEISV if GSM subscription is available
     * @param esn ESN if CDMA subscription is available
     * @param meid MEID if CDMA subscription is available
     */
    public void getDeviceIdentityResponse(RadioResponseInfo responseInfo, String imei,
            String imeisv, String esn, String meid) {
        RadioResponse.responseStrings(
                HAL_SERVICE_MODEM, mRil, responseInfo, imei, imeisv, esn, meid);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     * @param imeiInfo object containing ImeiType, device IMEI and IMEISV
     */
    public void getImeiResponse(RadioResponseInfo responseInfo, ImeiInfo imeiInfo) {
        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);
        if (rr != null) {
            if (responseInfo.error == RadioError.NONE) {
                RadioResponse.sendMessageResponse(rr.mResult, imeiInfo);
            }
            mRil.processResponseDone(rr, responseInfo, imeiInfo);
        }
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     * @param config Array of HardwareConfig of the radio
     */
    public void getHardwareConfigResponse(RadioResponseInfo responseInfo,
            android.hardware.radio.modem.HardwareConfig[] config) {
        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);

        if (rr != null) {
            ArrayList<HardwareConfig> ret = RILUtils.convertHalHardwareConfigList(config);
            if (responseInfo.error == RadioError.NONE) {
                RadioResponse.sendMessageResponse(rr.mResult, ret);
            }
            mRil.processResponseDone(rr, responseInfo, ret);
        }
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     * @param activityInfo modem activity information
     */
    public void getModemActivityInfoResponse(RadioResponseInfo responseInfo,
            android.hardware.radio.modem.ActivityStatsInfo activityInfo) {
        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);

        if (rr != null) {
            ModemActivityInfo ret = null;
            ActivityStatsTechSpecificInfo[] astsi = null;
            if (responseInfo.error == RadioError.NONE) {
                final int sleepModeTimeMs = activityInfo.sleepModeTimeMs;
                final int idleModeTimeMs = activityInfo.idleModeTimeMs;
                int size = activityInfo.techSpecificInfo.length;
                astsi = new ActivityStatsTechSpecificInfo[size];
                for (int s = 0; s < size; s++) {
                    int rat = activityInfo.techSpecificInfo[s].rat;
                    int frequencyRange = activityInfo.techSpecificInfo[s].frequencyRange;
                    int [] txModeTimeMs = new int[ModemActivityInfo.getNumTxPowerLevels()];
                    int rxModeTimeMs = activityInfo.techSpecificInfo[s].rxModeTimeMs;
                    for (int i = 0; i < ModemActivityInfo.getNumTxPowerLevels(); i++) {
                        txModeTimeMs[i] = activityInfo.techSpecificInfo[s].txmModetimeMs[i];
                    }
                    astsi[s] = new ActivityStatsTechSpecificInfo(
                                    rat,
                                    frequencyRange,
                                    txModeTimeMs,
                                    rxModeTimeMs);
                }
                ret = new ModemActivityInfo(SystemClock.elapsedRealtime(), sleepModeTimeMs,
                        idleModeTimeMs, astsi);
            } else {
                astsi = new ActivityStatsTechSpecificInfo[1];
                astsi[0] = new ActivityStatsTechSpecificInfo(0, 0,
                        new int[ModemActivityInfo.getNumTxPowerLevels()], 0);
                ret = new ModemActivityInfo(SystemClock.elapsedRealtime(), 0, 0, astsi);
                responseInfo.error = RadioError.NONE;
            }
            RadioResponse.sendMessageResponse(rr.mResult, ret);
            mRil.processResponseDone(rr, responseInfo, ret);
        }
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     * @param isEnabled whether the modem stack is enabled.
     */
    public void getModemStackStatusResponse(RadioResponseInfo responseInfo, boolean isEnabled) {
        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);

        if (rr != null) {
            if (responseInfo.error == RadioError.NONE) {
                RadioResponse.sendMessageResponse(rr.mResult, isEnabled);
            }
            mRil.processResponseDone(rr, responseInfo, isEnabled);
        }
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     * @param radioCapability RadioCapability from the modem
     */
    public void getRadioCapabilityResponse(RadioResponseInfo responseInfo,
            android.hardware.radio.modem.RadioCapability radioCapability) {
        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);

        if (rr != null) {
            RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil);
            if (responseInfo.error == RadioError.REQUEST_NOT_SUPPORTED
                    || responseInfo.error == RadioError.GENERIC_FAILURE) {
                // TODO: Construct the supported RAF bitmask based on preferred network bitmasks
                ret = mRil.makeStaticRadioCapability();
                responseInfo.error = RadioError.NONE;
            }
            if (responseInfo.error == RadioError.NONE) {
                RadioResponse.sendMessageResponse(rr.mResult, ret);
            }
            mRil.processResponseDone(rr, responseInfo, ret);
        }
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     * @param result String containing the contents of the NV item
     */
    public void nvReadItemResponse(RadioResponseInfo responseInfo, String result) {
        RadioResponse.responseString(HAL_SERVICE_MODEM, mRil, responseInfo, result);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     */
    public void nvResetConfigResponse(RadioResponseInfo responseInfo) {
        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     */
    public void nvWriteCdmaPrlResponse(RadioResponseInfo responseInfo) {
        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     */
    public void nvWriteItemResponse(RadioResponseInfo responseInfo) {
        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     */
    public void requestShutdownResponse(RadioResponseInfo responseInfo) {
        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     */
    public void sendDeviceStateResponse(RadioResponseInfo responseInfo) {
        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
     * @param radioCapability RadioCapability to set
     */
    public void setRadioCapabilityResponse(RadioResponseInfo responseInfo,
            android.hardware.radio.modem.RadioCapability radioCapability) {
        RILRequest rr = mRil.processResponse(HAL_SERVICE_MODEM, responseInfo);

        if (rr != null) {
            RadioCapability ret = RILUtils.convertHalRadioCapability(radioCapability, mRil);
            if (responseInfo.error == RadioError.NONE) {
                RadioResponse.sendMessageResponse(rr.mResult, ret);
            }
            mRil.processResponseDone(rr, responseInfo, ret);
        }
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error.
     */
    public void setRadioPowerResponse(RadioResponseInfo responseInfo) {
        RadioResponse.responseVoid(HAL_SERVICE_MODEM, mRil, responseInfo);
        mRil.mLastRadioPowerResult = responseInfo.error;
        if (responseInfo.error == RadioError.RF_HARDWARE_ISSUE) {
            AnomalyReporter.reportAnomaly(
                    UUID.fromString(RILUtils.RADIO_POWER_FAILURE_RF_HARDWARE_ISSUE_UUID),
                    "RF HW damaged");
        } else if (responseInfo.error == RadioError.NO_RF_CALIBRATION_INFO) {
            AnomalyReporter.reportAnomaly(
                    UUID.fromString(RILUtils.RADIO_POWER_FAILURE_NO_RF_CALIBRATION_UUID),
                    "No RF calibration data");
        } else if (responseInfo.error != RadioError.RADIO_NOT_AVAILABLE
                && responseInfo.error != RadioError.NONE) {
            AnomalyReporter.reportAnomaly(
                    UUID.fromString(RILUtils.RADIO_POWER_FAILURE_BUGREPORT_UUID),
                    "Radio power failure");
        }
    }

    @Override
    public String getInterfaceHash() {
        return IRadioModemResponse.HASH;
    }

    @Override
    public int getInterfaceVersion() {
        return IRadioModemResponse.VERSION;
    }
}
