Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c5daef5b authored by Sarah Chin's avatar Sarah Chin
Browse files

Create SatelliteServiceController and SateliteServiceConnection

Test: manual boot up and verify
Bug: 261131816
Change-Id: Ie53510cd40b759ba9c4d68018b96b53dd57c64b7
parent e6dda944
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3171,7 +3171,7 @@ public interface CommandsInterface {
    default void isSatelliteCommunicationAllowedForCurrentLocation(Message result) {}

    /**
     * Get the time after which the satellite will next be visible.
     * Get the time after which the satellite will be visible.
     *
     * @param result Message that will be sent back to the requester.
     */
+1 −1
Original line number Diff line number Diff line
@@ -5499,7 +5499,7 @@ public abstract class Phone extends Handler implements PhoneInternalInterface {
    }

    /**
     * Get the time after which the satellite will next be visible.
     * Get the time after which the satellite will be visible.
     * @param result The Message to send the result of the operation to.
     */
    public void requestTimeForNextSatelliteVisibility(Message result) {
+2 −4
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SATELLITE_RA

import android.hardware.radio.satellite.IRadioSatelliteIndication;
import android.os.AsyncResult;
import android.telephony.satellite.stub.SatelliteImplBase;

/**
 * Interface declaring unsolicited radio indications for Satellite APIs.
@@ -129,7 +128,7 @@ public class SatelliteIndication extends IRadioSatelliteIndication.Stub {
     * @param indicationType Type of radio indication
     * @param mode The current mode of the satellite modem.
     */
    public void onSatelliteModeChanged(int indicationType, @SatelliteImplBase.Mode int mode) {
    public void onSatelliteModeChanged(int indicationType, int mode) {
        mRil.processIndication(HAL_SERVICE_SATELLITE, indicationType);

        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SATELLITE_MODE_CHANGED);
@@ -146,8 +145,7 @@ public class SatelliteIndication extends IRadioSatelliteIndication.Stub {
     * @param indicationType Type of radio indication
     * @param technology The current technology of the satellite modem.
     */
    public void onSatelliteRadioTechnologyChanged(int indicationType,
            @SatelliteImplBase.NTRadioTechnology int technology) {
    public void onSatelliteRadioTechnologyChanged(int indicationType, int technology) {
        mRil.processIndication(HAL_SERVICE_SATELLITE, indicationType);

        if (mRil.isLogOrTrace()) mRil.unsljLog(RIL_UNSOL_SATELLITE_RADIO_TECHNOLOGY_CHANGED);
+1 −4
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import android.hardware.radio.RadioError;
import android.hardware.radio.RadioResponseInfo;
import android.hardware.radio.satellite.IRadioSatelliteResponse;
import android.telephony.satellite.SatelliteCapabilities;
import android.telephony.satellite.stub.SatelliteImplBase;

/**
 * Interface declaring response functions to solicited radio requests for Satellite APIs.
@@ -145,9 +144,7 @@ public class SatelliteResponse extends IRadioSatelliteResponse.Stub {
     * @param mode Current Mode of the satellite modem.
     * @param technology The current technology of the satellite modem.
     */
    public void getSatelliteModeResponse(
            RadioResponseInfo responseInfo, @SatelliteImplBase.Mode int mode,
            @SatelliteImplBase.NTRadioTechnology int technology) {
    public void getSatelliteModeResponse(RadioResponseInfo responseInfo, int mode, int technology) {
        RILRequest rr = mRil.processResponse(HAL_SERVICE_SATELLITE, responseInfo);

        if (rr != null) {
+185 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.satellite;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Looper;
import android.telephony.Rlog;
import android.telephony.satellite.stub.ISatellite;
import android.telephony.satellite.stub.SatelliteService;
import android.text.TextUtils;

import com.android.internal.telephony.ExponentialBackoff;

/**
 * Satellite service controller to manage connections with the satellite service.
 */
public class SatelliteServiceController {
    private static final String TAG = "SatelliteServiceController";
    private static final long REBIND_INITIAL_DELAY = 2 * 1000; // 2 seconds
    private static final long REBIND_MAXIMUM_DELAY = 64 * 1000; // 1 minute
    private static final int REBIND_MULTIPLIER = 2;

    @NonNull private static SatelliteServiceController sInstance;
    @NonNull private final Context mContext;
    @NonNull private final ExponentialBackoff mExponentialBackoff;
    @NonNull private final Object mLock = new Object();
    @Nullable private ISatellite mSatelliteService;
    @Nullable private SatelliteServiceConnection mSatelliteServiceConnection;
    private boolean mIsBound;

    /**
     * @return The singleton instance of SatelliteServiceController.
     */
    public static SatelliteServiceController getInstance() {
        if (sInstance == null) {
            loge("SatelliteServiceController was not yet initialized.");
        }
        return sInstance;
    }

    /**
     * Create the SatelliteServiceController singleton instance.
     * @param context The Context to use to create the SatelliteServiceController.
     */
    public static void make(@NonNull Context context) {
        if (sInstance == null) {
            sInstance = new SatelliteServiceController(context, Looper.getMainLooper());
        }
    }

    /**
     * Create a SatelliteServiceController to manage connections to the SatelliteService.
     *
     * @param context The Context for the SatelliteServiceController.
     * @param looper The Looper to run binding retry on.
     */
    private SatelliteServiceController(@NonNull Context context, @NonNull Looper looper) {
        mContext = context;
        mExponentialBackoff = new ExponentialBackoff(REBIND_INITIAL_DELAY, REBIND_MAXIMUM_DELAY,
                REBIND_MULTIPLIER, looper, () -> {
            synchronized (mLock) {
                if (mIsBound) {
                    return;
                }
                bindService();
            }
        });
        mExponentialBackoff.start();
        logd("Created SatelliteServiceController. Attempting to bind to SatelliteService.");
        bindService();
    }

    /**
     * Get the SatelliteService interface, if it exists.
     *
     * @return The bound ISatellite, or {@code null} if it is not yet connected.
     */
    @Nullable public ISatellite getService() {
        return mSatelliteService;
    }

    @NonNull private String getSatellitePackageName() {
        return TextUtils.emptyIfNull(mContext.getResources().getString(
                com.android.internal.R.string.config_satellite_service_package));
    }

    private void bindService() {
        String packageName = getSatellitePackageName();
        if (TextUtils.isEmpty(packageName)) {
            loge("Unable to bind to the satellite service because the package is undefined.");
            // Since the package name comes from static device configs, stop retry because
            // rebind will continue to fail without a valid package name.
            mExponentialBackoff.stop();
            return;
        }
        Intent intent = new Intent(SatelliteService.SERVICE_INTERFACE);
        intent.setPackage(packageName);

        mSatelliteServiceConnection = new SatelliteServiceConnection();
        try {
            boolean success = mContext.bindService(
                    intent, mSatelliteServiceConnection, Context.BIND_AUTO_CREATE);
            if (success) {
                logd("Successfully bound to the satellite service.");
            } else {
                mExponentialBackoff.notifyFailed();
                loge("Error binding to the satellite service. Retrying in "
                        + mExponentialBackoff.getCurrentDelay() + " ms.");
            }
        } catch (Exception e) {
            mExponentialBackoff.notifyFailed();
            loge("Exception binding to the satellite service. Retrying in "
                    + mExponentialBackoff.getCurrentDelay() + " ms. Exception: " + e);
        }
    }

    private void unbindService() {
        resetService();
        mContext.unbindService(mSatelliteServiceConnection);
        mSatelliteServiceConnection = null;
    }

    private void resetService() {
        // TODO: clean up any listeners and return failed for pending callbacks
        mSatelliteService = null;
    }

    private class SatelliteServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            logd("onServiceConnected: ComponentName=" + name);
            synchronized (mLock) {
                mIsBound = true;
            }
            mSatelliteService = ISatellite.Stub.asInterface(service);
            mExponentialBackoff.stop();
            // TODO: register any listeners
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            loge("onServiceDisconnected: Waiting for reconnect.");
            // Since we are still technically bound, clear the service and wait for reconnect.
            resetService();
        }

        @Override
        public void onBindingDied(ComponentName name) {
            loge("onBindingDied: Unbinding and rebinding service.");
            synchronized (mLock) {
                mIsBound = false;
            }
            unbindService();
            mExponentialBackoff.start();
        }
    }

    private static void logd(@NonNull String log) {
        Rlog.d(TAG, log);
    }

    private static void loge(@NonNull String log) {
        Rlog.e(TAG, log);
    }
}