Handle RADIO_NOT_AVAILABLE in deactivateDataCall

Test: atest DataNetworkTest, DataNetworkControllerTest, DcTrackerTest
Bug: 221174412
Change-Id: I073622fa4cb0ea12bacf5f95354aa38f7a44d1f8
Merged-In: I073622fa4cb0ea12bacf5f95354aa38f7a44d1f8
diff --git a/src/java/com/android/internal/telephony/DataResponse.java b/src/java/com/android/internal/telephony/DataResponse.java
index 061f9f6..e657152 100644
--- a/src/java/com/android/internal/telephony/DataResponse.java
+++ b/src/java/com/android/internal/telephony/DataResponse.java
@@ -71,7 +71,13 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void deactivateDataCallResponse(RadioResponseInfo responseInfo) {
-        RadioResponse.responseVoid(RIL.DATA_SERVICE, mRil, responseInfo);
+        RILRequest rr = mRil.processResponse(RIL.DATA_SERVICE, responseInfo);
+
+        if (rr != null) {
+            int response = responseInfo.error;
+            RadioResponse.sendMessageResponse(rr.mResult, response);
+            mRil.processResponseDone(rr, responseInfo, response);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index 5265be5..d13f3ca 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -740,7 +740,13 @@
      * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void deactivateDataCallResponse(RadioResponseInfo responseInfo) {
-        responseVoid(responseInfo);
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            int response = responseInfo.error;
+            sendMessageResponse(rr.mResult, response);
+            mRil.processResponseDone(rr, responseInfo, response);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/data/CellularDataService.java b/src/java/com/android/internal/telephony/data/CellularDataService.java
index f041d5f..e7cf86f 100644
--- a/src/java/com/android/internal/telephony/data/CellularDataService.java
+++ b/src/java/com/android/internal/telephony/data/CellularDataService.java
@@ -19,6 +19,7 @@
 import static android.telephony.data.DataServiceCallback.RESULT_SUCCESS;
 
 import android.annotation.Nullable;
+import android.hardware.radio.RadioError;
 import android.net.LinkProperties;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -51,7 +52,7 @@
     private static final boolean DBG = false;
 
     private static final int SETUP_DATA_CALL_COMPLETE               = 1;
-    private static final int DEACTIVATE_DATA_ALL_COMPLETE           = 2;
+    private static final int DEACTIVATE_DATA_CALL_COMPLETE          = 2;
     private static final int SET_INITIAL_ATTACH_APN_COMPLETE        = 3;
     private static final int SET_DATA_PROFILE_COMPLETE              = 4;
     private static final int REQUEST_DATA_CALL_LIST_COMPLETE        = 5;
@@ -87,10 +88,13 @@
                                     : RESULT_SUCCESS,
                                     response);
                             break;
-                        case DEACTIVATE_DATA_ALL_COMPLETE:
+                        case DEACTIVATE_DATA_CALL_COMPLETE:
+                            int error = (int) ar.result;
                             callback.onDeactivateDataCallComplete(ar.exception != null
                                     ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
-                                    : RESULT_SUCCESS);
+                                    : error == RadioError.RADIO_NOT_AVAILABLE
+                                            ? DataServiceCallback.RESULT_ERROR_RADIO_NOT_AVAILABLE
+                                            : RESULT_SUCCESS);
                             break;
                         case SET_INITIAL_ATTACH_APN_COMPLETE:
                             callback.onSetInitialAttachApnComplete(ar.exception != null
@@ -189,7 +193,7 @@
             // Only obtain the message when the caller wants a callback. If the caller doesn't care
             // the request completed or results, then no need to pass the message down.
             if (callback != null) {
-                message = Message.obtain(mHandler, DEACTIVATE_DATA_ALL_COMPLETE);
+                message = Message.obtain(mHandler, DEACTIVATE_DATA_CALL_COMPLETE);
                 mCallbackMap.put(message, callback);
             }
 
diff --git a/src/java/com/android/internal/telephony/data/DataNetwork.java b/src/java/com/android/internal/telephony/data/DataNetwork.java
index 71da08c..f2f2ddc 100644
--- a/src/java/com/android/internal/telephony/data/DataNetwork.java
+++ b/src/java/com/android/internal/telephony/data/DataNetwork.java
@@ -123,9 +123,9 @@
  * {@link ConnectedState}. When the data network is about to be disconnected, it first enters
  * {@link DisconnectingState} when performing graceful tear down or when sending the data
  * deactivation request. At the end, it enters {@link DisconnectedState} when {@link DataService}
- * notifies data disconnected. Note that a unsolicited disconnected event from {@link DataService}
- * can immediately move data network transited from {@link ConnectedState} to
- * {@link DisconnectedState}. {@link DisconnectedState} is the final state of a data network.
+ * notifies data disconnected. Note that an unsolicited disconnected event from {@link DataService}
+ * or a RADIO_NOT_AVAILABLE response can immediately move data network from {@link ConnectedState}
+ * to {@link DisconnectedState}. {@link DisconnectedState} is the final state of a data network.
  *
  * State machine diagram:
  *
@@ -158,7 +158,7 @@
     private static final int EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE = 5;
 
     /** Event for setup data network response. */
-    private static final int EVENT_SETUP_DATA_CALL_RESPONSE = 6;
+    private static final int EVENT_SETUP_DATA_NETWORK_RESPONSE = 6;
 
     /** Event for tearing down data network. */
     private static final int EVENT_TEAR_DOWN_NETWORK = 7;
@@ -196,6 +196,9 @@
     /** Event for carrier privileged UIDs changed. */
     private static final int EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED = 18;
 
+    /** Event for deactivate data network response. */
+    private static final int EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE = 19;
+
     /** The default MTU for IPv4 network. */
     private static final int DEFAULT_MTU_V4 = 1280;
 
@@ -795,8 +798,7 @@
 
         for (TelephonyNetworkRequest networkRequest : networkRequestList) {
             networkRequest.setAttachedNetwork(DataNetwork.this);
-            networkRequest.setState(
-                    TelephonyNetworkRequest.REQUEST_STATE_SATISFIED);
+            networkRequest.setState(TelephonyNetworkRequest.REQUEST_STATE_SATISFIED);
         }
 
         addState(mDefaultState);
@@ -1017,7 +1019,7 @@
                     }
                     setupData();
                     break;
-                case EVENT_SETUP_DATA_CALL_RESPONSE:
+                case EVENT_SETUP_DATA_NETWORK_RESPONSE:
                     int resultCode = msg.arg1;
                     DataCallResponse dataCallResponse =
                             msg.getData().getParcelable(DataServiceManager.DATA_CALL_RESPONSE);
@@ -1128,6 +1130,10 @@
                     ar = (AsyncResult) msg.obj;
                     onPcoDataReceived((PcoData) ar.result);
                     break;
+                case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE:
+                    int resultCode = msg.arg1;
+                    onDeactivateResponse(resultCode);
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -1236,7 +1242,15 @@
         @Override
         public boolean processMessage(Message msg) {
             logv("event=" + eventToString(msg.what));
-            return NOT_HANDLED;
+            switch (msg.what) {
+                case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE:
+                    int resultCode = msg.arg1;
+                    onDeactivateResponse(resultCode);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
         }
     }
 
@@ -1737,7 +1751,7 @@
                 .setupDataCall(accessNetwork, mDataProfile, isModemRoaming, allowRoaming,
                         DataService.REQUEST_REASON_NORMAL, null, mPduSessionId, null,
                         trafficDescriptor, matchAllRuleAllowed,
-                        obtainMessage(EVENT_SETUP_DATA_CALL_RESPONSE));
+                        obtainMessage(EVENT_SETUP_DATA_NETWORK_RESPONSE));
 
         int apnTypeBitmask = mDataProfile.getApnSetting() != null
                 ? mDataProfile.getApnSetting().getApnTypeBitmask() : ApnSetting.TYPE_NONE;
@@ -2013,6 +2027,23 @@
     }
 
     /**
+     * Called when receiving deactivate data network response from the data service.
+     *
+     * @param resultCode The result code.
+     */
+    private void onDeactivateResponse(@DataServiceCallback.ResultCode int resultCode) {
+        logl("onDeactivateResponse: resultCode="
+                + DataServiceCallback.resultCodeToString(resultCode));
+        if (resultCode == DataServiceCallback.RESULT_ERROR_RADIO_NOT_AVAILABLE) {
+            log("Remove network since deactivate request returned RADIO_NOT_AVAILABLE.");
+            mDataNetworkCallback.invokeFromExecutor(
+                    () -> mDataNetworkCallback.onDisconnected(
+                            DataNetwork.this, DataFailCause.RADIO_NOT_AVAILABLE));
+            transitionTo(mDisconnectedState);
+        }
+    }
+
+    /**
      * Tear down the data network immediately.
      *
      * @param reason The reason of tearing down the network.
@@ -2037,7 +2068,8 @@
 
         // TODO: Need to support DataService.REQUEST_REASON_SHUTDOWN
         mDataServiceManagers.get(mTransport).deactivateDataCall(mCid.get(mTransport),
-                DataService.REQUEST_REASON_NORMAL, null);
+                DataService.REQUEST_REASON_NORMAL,
+                obtainMessage(EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE));
         mDataCallSessionStats.setDeactivateDataCallReason(DataService.REQUEST_REASON_NORMAL);
         mInvokedDataDeactivation = true;
     }
@@ -2606,7 +2638,8 @@
 
             // Clean up on the source transport.
             mDataServiceManagers.get(mTransport).deactivateDataCall(mCid.get(mTransport),
-                    DataService.REQUEST_REASON_HANDOVER, null);
+                    DataService.REQUEST_REASON_HANDOVER,
+                    obtainMessage(EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE));
             // Switch the transport to the target.
             mTransport = DataUtils.getTargetTransport(mTransport);
             // Update the logging tag
@@ -2778,7 +2811,7 @@
                 return "EVENT_DETACH_NETWORK_REQUEST";
             case EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE:
                 return "EVENT_ALLOCATE_PDU_SESSION_ID_RESPONSE";
-            case EVENT_SETUP_DATA_CALL_RESPONSE:
+            case EVENT_SETUP_DATA_NETWORK_RESPONSE:
                 return "EVENT_SETUP_DATA_NETWORK_RESPONSE";
             case EVENT_TEAR_DOWN_NETWORK:
                 return "EVENT_TEAR_DOWN_NETWORK";
@@ -2804,6 +2837,8 @@
                 return "EVENT_PCO_DATA_RECEIVED";
             case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED:
                 return "EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED";
+            case EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE:
+                return "EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE";
             default:
                 return "Unknown(" + event + ")";
         }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
index 91f8d3e..b635192 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommands.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.test;
 
 import android.compat.annotation.UnsupportedAppUsage;
+import android.hardware.radio.RadioError;
 import android.hardware.radio.V1_0.DataRegStateResult;
 import android.hardware.radio.V1_0.SetupDataCallResult;
 import android.hardware.radio.V1_0.VoiceRegStateResult;
@@ -1239,7 +1240,7 @@
     @Override
     public void deactivateDataCall(int cid, int reason, Message result) {
         SimulatedCommandsVerifier.getInstance().deactivateDataCall(cid, reason, result);
-        resultSuccess(result, null);
+        resultSuccess(result, RadioError.NONE);
     }
 
     @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
index e1f4a3a..5486c12 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkControllerTest.java
@@ -468,7 +468,7 @@
                                 transport).values()), null));
                 return null;
             }).when(mMockedDataServiceManagers.get(transport)).deactivateDataCall(
-                    anyInt(), anyInt(), eq(null));
+                    anyInt(), anyInt(), any(Message.class));
 
             doAnswer(invocation -> {
                 Handler h = (Handler) invocation.getArguments()[0];
diff --git a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
index b6e1aa5..2a64729 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/data/DataNetworkTest.java
@@ -461,7 +461,7 @@
         verify(mSimulatedCommandsVerifier, never()).releasePduSessionId(nullable(Message.class),
                 anyInt());
         verify(mMockedWwanDataServiceManager).deactivateDataCall(eq(123),
-                eq(DataService.REQUEST_REASON_NORMAL), eq(null));
+                eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
         verify(mDataNetworkCallback).onDisconnected(eq(mDataNetworkUT), eq(
                 DataFailCause.EMM_DETACHED));
 
@@ -563,7 +563,7 @@
 
         verify(mSimulatedCommandsVerifier).releasePduSessionId(nullable(Message.class), eq(1));
         verify(mMockedWlanDataServiceManager).deactivateDataCall(eq(123),
-                eq(DataService.REQUEST_REASON_NORMAL), eq(null));
+                eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
         verify(mDataNetworkCallback).onDisconnected(eq(mDataNetworkUT), eq(
                 DataFailCause.EMM_DETACHED));
 
@@ -691,4 +691,17 @@
                 .containsExactly(ADMIN_UID1, ADMIN_UID2);
         assertThat(mDataNetworkUT.getNetworkCapabilities().getOwnerUid()).isEqualTo(ADMIN_UID2);
     }
+
+    @Test
+    public void testDeactivateDataCallRadioNotAvailable() throws Exception {
+        testCreateDataNetwork();
+        assertThat(mDataNetworkUT.isConnected()).isTrue();
+        mDataNetworkUT.sendMessage(19/*EVENT_DEACTIVATE_DATA_NETWORK_RESPONSE*/,
+                6/*RESULT_ERROR_RADIO_NOT_AVAILABLE*/);
+        processAllMessages();
+
+        verify(mDataNetworkCallback).onDisconnected(eq(mDataNetworkUT), eq(
+                DataFailCause.RADIO_NOT_AVAILABLE));
+        assertThat(mDataNetworkUT.isConnected()).isFalse();
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index 5ee093e..ad9fe43 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -110,7 +110,6 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 import java.lang.reflect.Field;
@@ -739,14 +738,11 @@
         doReturn(true).when(mSST).getDesiredPowerState();
         doReturn(true).when(mSST).getPowerStateFromCarrier();
         doAnswer(
-                new Answer<Void>() {
-                    @Override
-                    public Void answer(InvocationOnMock invocation) throws Throwable {
-                        mOnSubscriptionsChangedListener =
-                                (SubscriptionManager.OnSubscriptionsChangedListener)
-                                        invocation.getArguments()[0];
-                        return null;
-                    }
+                (Answer<Void>) invocation -> {
+                    mOnSubscriptionsChangedListener =
+                            (SubscriptionManager.OnSubscriptionsChangedListener)
+                                    invocation.getArguments()[0];
+                    return null;
                 }
         ).when(mSubscriptionManager).addOnSubscriptionsChangedListener(any());
         doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());