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

Commit 52240c86 authored by Rambo Wang's avatar Rambo Wang
Browse files

Implement Radio HAL Fallback Compatibility.

Refer to go/radio-compat. This CL provides a mechanism for partially
implemented HAL versions in Android' Radio HAl, starting with support
for methods in Radio HAL 1.5 to fall back to 1.4 as needed.

Then radio response of a HAL request reports unsupported, RIL will
retry with a new request on the next HAL version and cache the
sucessful request/HAL-version for further handling.

Bug: 151106728
Test: atest com.android.internal.telephony.RILTest

Change-Id: Ied80fccba8b66194aa9c1aa03107c26a49abbd18
parent f490798f
Loading
Loading
Loading
Loading
+88 −6
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.android.internal.telephony.RILConstants.*;
import static com.android.internal.util.Preconditions.checkNotNull;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.hardware.radio.V1_0.Carrier;
@@ -132,6 +133,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
@@ -266,6 +268,13 @@ public class RIL extends BaseCommands implements CommandsInterface {
    final RadioProxyDeathRecipient mRadioProxyDeathRecipient;
    final RilHandler mRilHandler;

    // Thread-safe HashMap to map from RIL_REQUEST_XXX constant to HalVersion.
    // This is for Radio HAL Fallback Compatibility feature. When a RIL request
    // is received, the HAL method from the mapping HalVersion here (if present),
    // instead of the latest HalVersion, will be invoked.
    private ConcurrentHashMap<Integer, HalVersion> mCompatOverrides =
            new ConcurrentHashMap<>();

    //***** Events
    static final int EVENT_WAKE_LOCK_TIMEOUT    = 2;
    static final int EVENT_ACK_WAKE_LOCK_TIMEOUT    = 4;
@@ -432,6 +441,25 @@ public class RIL extends BaseCommands implements CommandsInterface {
        getOemHookProxy(null);
    }

    /** Set a radio HAL fallback compatibility override. */
    @VisibleForTesting
    public void setCompatVersion(int rilRequest, @NonNull HalVersion halVersion) {
        HalVersion oldVersion = getCompatVersion(rilRequest);
        // Do not allow to set same or greater verions
        if (oldVersion != null && halVersion.greaterOrEqual(oldVersion)) {
            riljLoge("setCompatVersion with equal or greater one, ignored, halVerion=" + halVersion
                    + ", oldVerion=" + oldVersion);
            return;
        }
        mCompatOverrides.put(rilRequest, halVersion);
    }

    /** Get a radio HAL fallback compatibility override, or null if not exist. */
    @VisibleForTesting
    public @Nullable HalVersion getCompatVersion(int rilRequest) {
        return mCompatOverrides.getOrDefault(rilRequest, null);
    }

    /** Returns a {@link IRadio} instance or null if the service is not available. */
    @VisibleForTesting
    public synchronized IRadio getRadioProxy(Message result) {
@@ -695,6 +723,13 @@ public class RIL extends BaseCommands implements CommandsInterface {
        return rr;
    }

    private RILRequest obtainRequest(int request, Message result, WorkSource workSource,
            Object... args) {
        RILRequest rr = RILRequest.obtain(request, result, workSource, args);
        addRequest(rr);
        return rr;
    }

    private void handleRadioProxyExceptionForRR(RILRequest rr, String caller, Exception e) {
        riljLoge(caller + ": " + e);
        resetProxyAndRequestList();
@@ -1458,7 +1493,13 @@ public class RIL extends BaseCommands implements CommandsInterface {

            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));

            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
            HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_VOICE_REGISTRATION_STATE);
            if (RILJ_LOGD) {
                riljLog("getVoiceRegistrationState: overrideHalVersion=" + overrideHalVersion);
            }
            if ((overrideHalVersion == null
                        || overrideHalVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5))
                    && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
                final android.hardware.radio.V1_5.IRadio radioProxy15 =
                        (android.hardware.radio.V1_5.IRadio) radioProxy;
                try {
@@ -1485,8 +1526,13 @@ public class RIL extends BaseCommands implements CommandsInterface {

            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));


            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
            HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_DATA_REGISTRATION_STATE);
            if (RILJ_LOGD) {
                riljLog("getDataRegistrationState: overrideHalVersion=" + overrideHalVersion);
            }
            if ((overrideHalVersion == null
                        || overrideHalVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5))
                    && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
                final android.hardware.radio.V1_5.IRadio radioProxy15 =
                        (android.hardware.radio.V1_5.IRadio) radioProxy;
                try {
@@ -2443,11 +2489,25 @@ public class RIL extends BaseCommands implements CommandsInterface {
        return rasInHalFormat;
    }

    /**
     * Radio HAL fallback compatibility feature (b/151106728) assumes that the input parameter
     * networkScanRequest is immutable (read-only) here. Once the caller invokes the method, the
     * parameter networkScanRequest should not be modified. This helps us keep a consistent and
     * simple data model that avoid copying it in the scan result.
     */
    @Override
    public void startNetworkScan(NetworkScanRequest nsr, Message result) {
    public void startNetworkScan(NetworkScanRequest networkScanRequest, Message result) {
        final NetworkScanRequest nsr = networkScanRequest;
        IRadio radioProxy = getRadioProxy(result);
        if (radioProxy != null) {
            if (mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {

            HalVersion overrideHalVersion = getCompatVersion(RIL_REQUEST_START_NETWORK_SCAN);
            if (RILJ_LOGD) {
                riljLog("startNetworkScan: overrideHalVersion=" + overrideHalVersion);
            }
            if ((overrideHalVersion == null
                        || overrideHalVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5))
                    && mRadioVersion.greaterOrEqual(RADIO_HAL_VERSION_1_5)) {
                android.hardware.radio.V1_5.NetworkScanRequest request =
                        new android.hardware.radio.V1_5.NetworkScanRequest();
                request.type = nsr.getScanType();
@@ -2469,7 +2529,7 @@ public class RIL extends BaseCommands implements CommandsInterface {

                request.mccMncs.addAll(nsr.getPlmns());
                RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
                        mRILDefaultWorkSource);
                        mRILDefaultWorkSource, nsr);

                if (RILJ_LOGD) {
                    riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
@@ -5514,6 +5574,28 @@ public class RIL extends BaseCommands implements CommandsInterface {
            }
            rr.onError(responseInfo.error, ret);
        }
        processResponseCleanUp(rr, responseInfo, ret);
    }

    /**
     * This is a helper function to be called at the end of all RadioResponse callbacks for
     * radio HAL fallback cases. It takes care of logging, decrementing wakelock if needed, and
     * releases the request from memory pool. Unlike processResponseDone, it will not send
     * error response to caller.
     * @param rr RILRequest for which response callback was called
     * @param responseInfo RadioResponseInfo received in the callback
     * @param ret object to be returned to request sender
     */
    @VisibleForTesting
    public void processResponseFallback(RILRequest rr, RadioResponseInfo responseInfo, Object ret) {
        if (responseInfo.error == REQUEST_NOT_SUPPORTED && RILJ_LOGD) {
            riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
                    + " request not supported, falling back");
        }
        processResponseCleanUp(rr, responseInfo, ret);
    }

    private void processResponseCleanUp(RILRequest rr, RadioResponseInfo responseInfo, Object ret) {
        mMetrics.writeOnRilSolicitedResponse(mPhoneId, rr.mSerial, responseInfo.error,
                rr.mRequest, ret);
        if (rr != null) {
+22 −0
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@ public class RILRequest {
    String mClientId;
    // time in ms when RIL request was made
    long mStartTimeMs;
    /** Argument list for radio HAL fallback method call */
    Object[] mArguments;

    public int getSerial() {
        return mSerial;
@@ -133,6 +135,25 @@ public class RILRequest {
        return rr;
    }

    /**
     * Retrieves a new RILRequest instance from the pool and sets the clientId
     *
     * @param request RIL_REQUEST_*
     * @param result sent when operation completes
     * @param workSource WorkSource to track the client
     * @param args The list of parameters used to call the fallback HAL method
     * @return a RILRequest instance from the pool.
     */
    // @VisibleForTesting
    public static RILRequest obtain(int request, Message result, WorkSource workSource,
            Object... args) {
        RILRequest rr = obtain(request, result, workSource);

        rr.mArguments = args;

        return rr;
    }

    /**
     * Generate a String client ID from the WorkSource.
     */
@@ -175,6 +196,7 @@ public class RILRequest {
                                + serialString());
                    }
                }
                mArguments = null;
            }
        }
    }
+71 −25
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import android.telephony.CarrierRestrictionRules;
import android.telephony.CellInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
import android.telephony.PhoneNumberUtils;
import android.telephony.RadioAccessFamily;
import android.telephony.SignalStrength;
@@ -357,14 +358,27 @@ public class RadioResponse extends IRadioResponse.Stub {
    public void getVoiceRegistrationStateResponse_1_5(RadioResponseInfo responseInfo,
            android.hardware.radio.V1_5.RegStateResult voiceRegResponse) {
        RILRequest rr = mRil.processResponse(responseInfo);
        if (rr == null) {
            return;
        }

        if (rr != null) {
            if (responseInfo.error == RadioError.NONE) {
        if (responseInfo.error == RadioError.REQUEST_NOT_SUPPORTED) {
            // Move the data needed for fallback call from rr which will be released soon
            final int request = rr.getRequest();
            final Message result = rr.getResult();

            mRil.mRilHandler.post(() -> {
                mRil.setCompatVersion(request, RIL.RADIO_HAL_VERSION_1_4);
                mRil.getVoiceRegistrationState(result);
            });

            mRil.processResponseFallback(rr, responseInfo, voiceRegResponse);
            return;
        } else if (responseInfo.error == RadioError.NONE) {
            sendMessageResponse(rr.mResult, voiceRegResponse);
        }
        mRil.processResponseDone(rr, responseInfo, voiceRegResponse);
    }
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
@@ -425,14 +439,27 @@ public class RadioResponse extends IRadioResponse.Stub {
    public void getDataRegistrationStateResponse_1_5(RadioResponseInfo responseInfo,
            android.hardware.radio.V1_5.RegStateResult dataRegResponse) {
        RILRequest rr = mRil.processResponse(responseInfo);
        if (rr == null) {
            return;
        }

        if (rr != null) {
            if (responseInfo.error == RadioError.NONE) {
        if (responseInfo.error == RadioError.REQUEST_NOT_SUPPORTED) {
            // Move the data needed for fallback call from rr which will be released soon
            final int request = rr.getRequest();
            final Message result = rr.getResult();

            mRil.mRilHandler.post(() -> {
                mRil.setCompatVersion(request, RIL.RADIO_HAL_VERSION_1_4);
                mRil.getDataRegistrationState(result);
            });

            mRil.processResponseFallback(rr, responseInfo, dataRegResponse);
            return;
        } else if (responseInfo.error == RadioError.NONE) {
            sendMessageResponse(rr.mResult, dataRegResponse);
        }
        mRil.processResponseDone(rr, responseInfo, dataRegResponse);
    }
    }

    /**
     * @param responseInfo Response info struct containing response type, serial no. and error
@@ -684,26 +711,26 @@ public class RadioResponse extends IRadioResponse.Stub {
     * @param responseInfo Response info struct containing response type, serial no. and error
     */
    public void startNetworkScanResponse(RadioResponseInfo responseInfo) {
        responseScanStatus(responseInfo);
        responseScanStatus(responseInfo, null /*fallbackHalVersion*/);
    }

    /**
     * The same method as startNetworkScanResponse, except disallowing error codes
     * OPERATION_NOT_ALLOWED and REQUEST_NOT_SUPPORTED.
     * OPERATION_NOT_ALLOWED.
     *
     * @param responseInfo Response info struct containing response type, serial no. and error
     */
    public void startNetworkScanResponse_1_4(RadioResponseInfo responseInfo) {
        responseScanStatus(responseInfo);
        responseScanStatus(responseInfo, null /*fallbackHalVersion*/);
    }

    /**
     * The same method as startNetworkScanResponse_1_5.
     * The same method as startNetworkScanResponse_1_4.
     *
     * @param responseInfo Response info struct containing response type, serial no. and error
     */
    public void startNetworkScanResponse_1_5(RadioResponseInfo responseInfo) {
        responseScanStatus(responseInfo);
        responseScanStatus(responseInfo, RIL.RADIO_HAL_VERSION_1_4);
    }

    /**
@@ -711,7 +738,7 @@ public class RadioResponse extends IRadioResponse.Stub {
     * @param responseInfo Response info struct containing response type, serial no. and error
     */
    public void stopNetworkScanResponse(RadioResponseInfo responseInfo) {
        responseScanStatus(responseInfo);
        responseScanStatus(responseInfo, null /*fallbackHalVersion*/);
    }

    /**
@@ -2207,10 +2234,30 @@ public class RadioResponse extends IRadioResponse.Stub {
        }
    }

    private void responseScanStatus(RadioResponseInfo responseInfo) {
    private void responseScanStatus(RadioResponseInfo responseInfo, HalVersion fallbackHalVersion) {
        RILRequest rr = mRil.processResponse(responseInfo);
        if (rr == null) {
            return;
        }

        final boolean needFallback = responseInfo.error == RadioError.REQUEST_NOT_SUPPORTED
                && fallbackHalVersion != null && rr.mArguments != null && rr.mArguments.length > 0
                && rr.mArguments[0] instanceof NetworkScanRequest;
        if (needFallback) {
            // Move the data needed for fallback call from rr which will be released soon
            final int request = rr.getRequest();
            final Message result = rr.getResult();
            final NetworkScanRequest scanRequest = (NetworkScanRequest) rr.mArguments[0];

            mRil.mRilHandler.post(() -> {
                mRil.setCompatVersion(request, RIL.RADIO_HAL_VERSION_1_4);
                mRil.startNetworkScan(scanRequest, result);
            });

            mRil.processResponseFallback(rr, responseInfo, null);
            return;
        }

        if (rr != null) {
        NetworkScanResult nsr = null;
        if (responseInfo.error == RadioError.NONE) {
            nsr = new NetworkScanResult(
@@ -2219,7 +2266,6 @@ public class RadioResponse extends IRadioResponse.Stub {
        }
        mRil.processResponseDone(rr, responseInfo, nsr);
    }
    }

    private void responseDataCallList(RadioResponseInfo responseInfo,
                                      List<? extends Object> dataCallResultList) {
+174 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_AUTHEN
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_CLOSE_CHANNEL;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SIM_OPEN_CHANNEL;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_LCE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_START_NETWORK_SCAN;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_STOP_LCE;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE;
@@ -83,6 +84,7 @@ import static com.android.internal.telephony.RILConstants.RIL_REQUEST_WRITE_SMS_
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;

import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -142,6 +144,8 @@ import android.telephony.CellSignalStrengthLte;
import android.telephony.CellSignalStrengthNr;
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.NetworkScanRequest;
import android.telephony.RadioAccessSpecifier;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SmsManager;
@@ -169,6 +173,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -544,6 +549,108 @@ public class RILTest extends TelephonyTest {
                RIL_REQUEST_VOICE_REGISTRATION_STATE);
    }

    private RadioAccessSpecifier getRadioAccessSpecifier(CellInfo cellInfo) {
        RadioAccessSpecifier ras;
        if (cellInfo instanceof CellInfoLte) {
            int ranLte = AccessNetworkConstants.AccessNetworkType.EUTRAN;
            int[] lteChannels = {((CellInfoLte) cellInfo).getCellIdentity().getEarfcn()};
            ras = new RadioAccessSpecifier(ranLte, null /* bands */, lteChannels);
        } else if (cellInfo instanceof CellInfoWcdma) {
            int ranLte = AccessNetworkConstants.AccessNetworkType.UTRAN;
            int[] wcdmaChannels = {((CellInfoWcdma) cellInfo).getCellIdentity().getUarfcn()};
            ras = new RadioAccessSpecifier(ranLte, null /* bands */, wcdmaChannels);
        } else if (cellInfo instanceof CellInfoGsm) {
            int ranGsm = AccessNetworkConstants.AccessNetworkType.GERAN;
            int[] gsmChannels = {((CellInfoGsm) cellInfo).getCellIdentity().getArfcn()};
            ras = new RadioAccessSpecifier(ranGsm, null /* bands */, gsmChannels);
        } else {
            ras = null;
        }
        return ras;
    }

    private NetworkScanRequest getNetworkScanRequestForTesting() {
        // Construct a NetworkScanRequest for testing
        List<CellInfo> allCellInfo = mTelephonyManager.getAllCellInfo();
        List<RadioAccessSpecifier> radioAccessSpecifier = new ArrayList<>();
        for (int i = 0; i < allCellInfo.size(); i++) {
            RadioAccessSpecifier ras = getRadioAccessSpecifier(allCellInfo.get(i));
            if (ras != null) {
                radioAccessSpecifier.add(ras);
            }
        }
        if (radioAccessSpecifier.size() == 0) {
            RadioAccessSpecifier gsm = new RadioAccessSpecifier(
                    AccessNetworkConstants.AccessNetworkType.GERAN,
                    null /* bands */,
                    null /* channels */);
            radioAccessSpecifier.add(gsm);
        }
        RadioAccessSpecifier[] radioAccessSpecifierArray =
                new RadioAccessSpecifier[radioAccessSpecifier.size()];
        return new NetworkScanRequest(
                NetworkScanRequest.SCAN_TYPE_ONE_SHOT /* scan type */,
                radioAccessSpecifier.toArray(radioAccessSpecifierArray),
                5 /* search periodicity */,
                60 /* max search time */,
                true /*enable incremental results*/,
                5 /* incremental results periodicity */,
                null /* List of PLMN ids (MCC-MNC) */);
    }

    @FlakyTest
    @Test
    public void testStartNetworkScanWithUnsupportedResponse() throws Exception {
        // Use Radio HAL v1.5
        try {
            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
        } catch (Exception e) {
        }
        NetworkScanRequest nsr = getNetworkScanRequestForTesting();
        mRILUnderTest.startNetworkScan(nsr, obtainMessage());

        // Verify the v1.5 HAL methed is called firstly
        verify(mRadioProxy).startNetworkScan_1_5(mSerialNumberCaptor.capture(), any());

        // Before we find a way to trigger real RadioResponse method, emulate the behaivor.
        Consumer<RILRequest> unsupportedResponseEmulator = rr -> {
            mRILUnderTest.setCompatVersion(rr.getRequest(), RIL.RADIO_HAL_VERSION_1_4);
            mRILUnderTest.startNetworkScan(nsr, Message.obtain(rr.getResult()));
        };

        verifyRILUnsupportedResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
                RIL_REQUEST_START_NETWORK_SCAN, unsupportedResponseEmulator);

        // Verify the fallback method is invoked
        verify(mRadioProxy).startNetworkScan_1_4(eq(mSerialNumberCaptor.getValue() + 1), any());
    }

    @FlakyTest
    @Test
    public void testGetVoiceRegistrationStateWithUnsupportedResponse() throws Exception {
        // Use Radio HAL v1.5
        try {
            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
        } catch (Exception e) {
        }
        mRILUnderTest.getVoiceRegistrationState(obtainMessage());

        // Verify the v1.5 HAL method is called
        verify(mRadioProxy).getVoiceRegistrationState_1_5(mSerialNumberCaptor.capture());

        // Before we find a way to trigger real RadioResponse method, emulate the behaivor.
        Consumer<RILRequest> unsupportedResponseEmulator = rr -> {
            mRILUnderTest.setCompatVersion(rr.getRequest(), RIL.RADIO_HAL_VERSION_1_4);
            mRILUnderTest.getVoiceRegistrationState(Message.obtain(rr.getResult()));
        };

        verifyRILUnsupportedResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
                RIL_REQUEST_VOICE_REGISTRATION_STATE, unsupportedResponseEmulator);

        // Verify the fallback method is invoked
        verify(mRadioProxy).getVoiceRegistrationState(mSerialNumberCaptor.getValue() + 1);
    }

    @FlakyTest
    @Test
    public void testGetDataRegistrationState() throws Exception {
@@ -553,6 +660,32 @@ public class RILTest extends TelephonyTest {
                mRILUnderTest, mSerialNumberCaptor.getValue(), RIL_REQUEST_DATA_REGISTRATION_STATE);
    }

    @FlakyTest
    @Test
    public void testGetDataRegistrationStateWithUnsupportedResponse() throws Exception {
        // Use Radio HAL v1.5
        try {
            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
        } catch (Exception e) {
        }

        // Verify the v1.5 HAL method is called
        mRILUnderTest.getDataRegistrationState(obtainMessage());
        verify(mRadioProxy).getDataRegistrationState_1_5(mSerialNumberCaptor.capture());

        // Before we find a way to trigger real RadioResponse method, emulate the behaivor.
        Consumer<RILRequest> unsupportedResponseEmulator = rr -> {
            mRILUnderTest.setCompatVersion(rr.getRequest(), RIL.RADIO_HAL_VERSION_1_4);
            mRILUnderTest.getDataRegistrationState(Message.obtain(rr.getResult()));
        };

        verifyRILUnsupportedResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
                RIL_REQUEST_DATA_REGISTRATION_STATE, unsupportedResponseEmulator);

        // Verify the fallback method is invoked
        verify(mRadioProxy).getDataRegistrationState(mSerialNumberCaptor.getValue() + 1);
    }

    @FlakyTest
    @Test
    public void testGetOperator() throws Exception {
@@ -1158,6 +1291,27 @@ public class RILTest extends TelephonyTest {
        assertFalse(ril.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
    }

    private static void verifyRILUnsupportedResponse(RIL ril, int serial, int requestType,
            Consumer<RILRequest> unsupportedResponseEmulator) {
        RadioResponseInfo responseInfo =
                createFakeRadioResponseInfo(serial, RadioError.REQUEST_NOT_SUPPORTED,
                        RadioResponseType.SOLICITED);

        RILRequest rr = ril.processResponse(responseInfo);
        assertNotNull(rr);

        assertEquals(serial, rr.getSerial());
        assertEquals(requestType, rr.getRequest());
        assertTrue(ril.getWakeLock(RIL.FOR_WAKELOCK).isHeld());

        unsupportedResponseEmulator.accept(rr);

        ril.processResponseDone(rr, responseInfo, null);

        assertEquals(1, ril.getRilRequestList().size());
        assertTrue(ril.getWakeLock(RIL.FOR_WAKELOCK).isHeld());
    }

    private static RadioResponseInfo createFakeRadioResponseInfo(int serial, int error, int type) {
        RadioResponseInfo respInfo = new RadioResponseInfo();
        respInfo.serial = serial;
@@ -2331,4 +2485,24 @@ public class RILTest extends TelephonyTest {
        verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
                RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT);
    }

    @Test
    public void testSetGetCompatVersion() throws Exception {
        final int testRequest = RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT;

        // getCompactVersion should return null before first setting
        assertNull(mRILUnderTest.getCompatVersion(testRequest));

        // first time setting any valid HalVersion will success
        mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_4);
        assertEquals(RIL.RADIO_HAL_VERSION_1_4, mRILUnderTest.getCompatVersion(testRequest));

        // try to set a lower HalVersion will success
        mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_3);
        assertEquals(RIL.RADIO_HAL_VERSION_1_3, mRILUnderTest.getCompatVersion(testRequest));

        // try to set a greater HalVersion will not success
        mRILUnderTest.setCompatVersion(testRequest, RIL.RADIO_HAL_VERSION_1_5);
        assertEquals(RIL.RADIO_HAL_VERSION_1_3, mRILUnderTest.getCompatVersion(testRequest));
    }
}