/*
 * Copyright (C) 2008 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.location;

import android.Manifest;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.SystemClock;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.emergency.EmergencyNumber;
import android.util.Log;

import com.android.internal.telephony.flags.Flags;

import java.util.concurrent.TimeUnit;

/**
 * A GPS Network-initiated Handler class used by LocationManager.
 *
 * {@hide}
 */
public class GpsNetInitiatedHandler {

    private static final String TAG = "GpsNetInitiatedHandler";

    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private final Context mContext;
    private final TelephonyManager mTelephonyManager;

    // parent gps location provider
    private final LocationManager mLocationManager;

    // Set to true if the phone is having emergency call.
    private volatile boolean mIsInEmergencyCall;


    // End time of emergency call, and extension, if set
    private volatile long mCallEndElapsedRealtimeMillis = 0;
    private volatile long mEmergencyExtensionMillis = 0;

    /** Callbacks for Emergency call events. */
    public interface EmergencyCallCallback {
        /** Callback invoked when an emergency call starts */
        void onEmergencyCallStart(int subId);
        /** Callback invoked when an emergency call ends */
        void onEmergencyCallEnd();
    }

    private class EmergencyCallListener extends TelephonyCallback implements
            TelephonyCallback.OutgoingEmergencyCallListener,
            TelephonyCallback.CallStateListener {

        @Override
        @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
        public void onOutgoingEmergencyCall(EmergencyNumber placedEmergencyNumber,
                int subscriptionId) {
            mIsInEmergencyCall = true;
            if (DEBUG) Log.d(TAG, "onOutgoingEmergencyCall(): inEmergency = " + getInEmergency());
            mEmergencyCallCallback.onEmergencyCallStart(subscriptionId);
        }

        @Override
        @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
        public void onCallStateChanged(int state) {
            if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is " + state);
            // listening for emergency call ends
            if (state == TelephonyManager.CALL_STATE_IDLE) {
                if (mIsInEmergencyCall) {
                    mCallEndElapsedRealtimeMillis = SystemClock.elapsedRealtime();
                    mIsInEmergencyCall = false;
                    mEmergencyCallCallback.onEmergencyCallEnd();
                }
            }
        }
    }

    // The internal implementation of TelephonyManager uses WeakReference so we have to keep a
    // reference here.
    private final EmergencyCallListener mEmergencyCallListener = new EmergencyCallListener();

    private final EmergencyCallCallback mEmergencyCallCallback;

    public GpsNetInitiatedHandler(Context context,
                                  EmergencyCallCallback emergencyCallCallback,
                                  boolean isSuplEsEnabled) {
        mContext = context;
        mEmergencyCallCallback = emergencyCallCallback;

        mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
        mTelephonyManager =
            (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
        mTelephonyManager.registerTelephonyCallback(mContext.getMainExecutor(),
                mEmergencyCallListener);

    }

    /**
     * Determines whether device is in user-initiated emergency session based on the following
     * 1. If the user is making an emergency call, this is provided by actively
     *    monitoring the outgoing phone number;
     * 2. If the user has recently ended an emergency call, and the device is in a configured time
     *    window after the end of that call.
     * 3. If the device is in a emergency callback state, this is provided by querying
     *    TelephonyManager.
     * 4. If the user has recently sent an Emergency SMS and telephony reports that it is in
     *    emergency SMS mode, this is provided by querying TelephonyManager.
     * @return true if is considered in user initiated emergency mode for NI purposes
     */
    public boolean getInEmergency() {
        return getInEmergency(mEmergencyExtensionMillis);
    }

    /**
     * Determines whether device is in user-initiated emergency session with the given extension
     * time.
     *
     * @return true if is considered in user initiated emergency mode for NI purposes within the
     * given extension time.
     *
     * @see {@link #getInEmergency()}
     */
    public boolean getInEmergency(long emergencyExtensionMillis) {
        boolean isInEmergencyExtension =
                (mCallEndElapsedRealtimeMillis > 0)
                        && ((SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis)
                        < emergencyExtensionMillis);
        boolean isInEmergencyCallback = false;
        boolean isInEmergencySmsMode = false;
        if (!Flags.enforceTelephonyFeatureMappingForPublicApis()) {
            isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
            isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
        } else {
            PackageManager pm = mContext.getPackageManager();
            if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
                isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
            }
            if (pm != null && pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_MESSAGING)) {
                isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
            }
        }
        return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension
                || isInEmergencySmsMode;
    }

    public void setEmergencyExtensionSeconds(int emergencyExtensionSeconds) {
        mEmergencyExtensionMillis = TimeUnit.SECONDS.toMillis(emergencyExtensionSeconds);
    }
}
