Merge "Clean up: remove jarjar rule for ArrayUtils"
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 7004ef2..f0a718a 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -101,6 +101,7 @@
     protected RegistrantList mPhysicalChannelConfigurationRegistrants = new RegistrantList();
     protected RegistrantList mLceInfoRegistrants = new RegistrantList();
     protected RegistrantList mEmergencyNumberListRegistrants = new RegistrantList();
+    protected RegistrantList mUiccApplicationsEnablementRegistrants = new RegistrantList();
 
     @UnsupportedAppUsage
     protected Registrant mGsmSmsRegistrant;
@@ -952,7 +953,7 @@
 
     @Override
     public void registerForModemReset(Handler h, int what, Object obj) {
-        mModemResetRegistrants.add(new Registrant(h, what, obj));
+        mModemResetRegistrants.addUnique(h, what, obj);
     }
 
     @Override
@@ -962,7 +963,7 @@
 
     @Override
     public void registerForPcoData(Handler h, int what, Object obj) {
-        mPcoDataRegistrants.add(new Registrant(h, what, obj));
+        mPcoDataRegistrants.addUnique(h, what, obj);
     }
 
     @Override
@@ -972,7 +973,7 @@
 
     @Override
     public void registerForCarrierInfoForImsiEncryption(Handler h, int what, Object obj) {
-        mCarrierInfoForImsiEncryptionRegistrants.add(new Registrant(h, what, obj));
+        mCarrierInfoForImsiEncryptionRegistrants.addUnique(h, what, obj);
     }
 
     @Override
@@ -993,4 +994,26 @@
             mNattKeepaliveStatusRegistrants.remove(h);
         }
     }
+
+    /**
+     * Registers the handler for RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED events.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    @Override
+    public void registerUiccApplicationEnablementChanged(Handler h, int what, Object obj) {
+        mUiccApplicationsEnablementRegistrants.addUnique(h, what, obj);
+    }
+
+    /**
+     * Unregisters the handler for RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED events.
+     *
+     * @param h Handler for notification message.
+     */
+    @Override
+    public void unregisterUiccApplicationEnablementChanged(Handler h) {
+        mUiccApplicationsEnablementRegistrants.remove(h);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 273de72..c67c675 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -656,6 +656,22 @@
      void unregisterForRilConnected(Handler h);
 
     /**
+     * Registers the handler for RIL_UNSOL_SIM_DETACH_FROM_NETWORK_CONFIG_CHANGED events.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    default void registerUiccApplicationEnablementChanged(Handler h, int what, Object obj) {};
+
+    /**
+     * Unregisters the handler for RIL_UNSOL_SIM_DETACH_FROM_NETWORK_CONFIG_CHANGED events.
+     *
+     * @param h Handler for notification message.
+     */
+    default void unregisterUiccApplicationEnablementChanged(Handler h) {};
+
+    /**
      * Supply the ICC PIN to the ICC card
      *
      *  returned message
@@ -2393,6 +2409,28 @@
      */
     default void getModemStatus(Message result) {};
 
+    /**
+     * Enable or disable uicc applications on the SIM.
+     *
+     * @param enable enable or disable UiccApplications on the SIM.
+     * @param onCompleteMessage a Message to return to the requester
+     */
+    default void enableUiccApplications(boolean enable, Message onCompleteMessage) {}
+
+    /**
+     * Whether uicc applications are enabled or not.
+     *
+     * @param onCompleteMessage a Message to return to the requester
+     */
+    default void areUiccApplicationsEnabled(Message onCompleteMessage) {}
+
+    /**
+     * Whether {@link #enableUiccApplications} is supported, based on IRadio version.
+     */
+    default boolean canToggleUiccApplicationsEnablement() {
+        return false;
+    }
+
     default List<ClientRequestStats> getClientRequestStats() {
         return null;
     }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 898f7b9..8405e5f 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -88,6 +88,7 @@
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
+import com.android.internal.telephony.uicc.IccCardStatus;
 import com.android.internal.telephony.uicc.IccException;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.IccVmNotSupportedException;
@@ -152,6 +153,7 @@
     private String mMeid;
     // string to define how the carrier specifies its own ota sp number
     private String mCarrierOtaSpNumSchema;
+    private boolean mUiccApplicationsEnabled = true;
 
     // A runnable which is used to automatically exit from Ecm after a period of time.
     private Runnable mExitEcmRunnable = new Runnable() {
@@ -324,6 +326,8 @@
         mCi.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
         mCi.registerForOn(this, EVENT_RADIO_ON, null);
         mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
+        mCi.registerUiccApplicationEnablementChanged(this, EVENT_UICC_APPS_ENABLEMENT_CHANGED,
+                null);
         mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);
 
         //GSM
@@ -2404,6 +2408,8 @@
 
         mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
         mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
+        mCi.areUiccApplicationsEnabled(obtainMessage(EVENT_GET_UICC_APPS_ENABLEMENT_DONE));
+
         startLceAfterRadioIsAvailable();
     }
 
@@ -2843,6 +2849,18 @@
                     onComplete.sendToTarget();
                 }
                 break;
+            case EVENT_GET_UICC_APPS_ENABLEMENT_DONE:
+            case EVENT_UICC_APPS_ENABLEMENT_CHANGED: {
+                ar = (AsyncResult) msg.obj;
+                if (ar == null) return;
+                if (ar.exception != null) {
+                    logd("Received exception on event" + msg.what + " : " + ar.exception);
+                    return;
+                }
+
+                mUiccApplicationsEnabled = (boolean) ar.result;
+                break;
+            }
             default:
                 super.handleMessage(msg);
         }
@@ -4036,4 +4054,30 @@
                 Settings.Secure.PREFERRED_TTY_MODE, TelecomManager.TTY_MODE_OFF);
         updateUiTtyMode(ttyMode);
     }
+
+    // Enable or disable uicc applications.
+    @Override
+    public void enableUiccApplications(boolean enable, Message onCompleteMessage) {
+        // First check if card is present. Otherwise mUiccApplicationsDisabled doesn't make
+        // any sense.
+        UiccSlot slot = mUiccController.getUiccSlotForPhone(mPhoneId);
+        if (slot == null || slot.getCardState() != IccCardStatus.CardState.CARDSTATE_PRESENT) {
+            if (onCompleteMessage != null) {
+                AsyncResult.forMessage(onCompleteMessage, null,
+                        new IllegalStateException("No SIM card is present"));
+                onCompleteMessage.sendToTarget();
+            }
+            return;
+        }
+
+        mCi.enableUiccApplications(enable, onCompleteMessage);
+    }
+
+    /**
+     * Whether disabling a physical subscription is supported or not.
+     */
+    @Override
+    public boolean canDisablePhysicalSubscription() {
+        return mCi.canToggleUiccApplicationsEnablement();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index ae3b778..8184305 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -195,7 +195,9 @@
     protected static final int EVENT_DEVICE_PROVISIONING_DATA_SETTING_CHANGE = 50;
     protected static final int EVENT_GET_AVAILABLE_NETWORKS_DONE    = 51;
 
-    private static final int EVENT_ALL_DATA_DISCONNECTED         = 52;
+    private static final int EVENT_ALL_DATA_DISCONNECTED            = 52;
+    protected static final int EVENT_UICC_APPS_ENABLEMENT_CHANGED   = 53;
+    protected static final int EVENT_GET_UICC_APPS_ENABLEMENT_DONE  = 54;
 
     protected static final int EVENT_LAST = EVENT_ALL_DATA_DISCONNECTED;
 
@@ -4076,6 +4078,20 @@
     }
 
     /**
+     * Enable or disable uicc applications.
+     * @param enable whether to enable or disable uicc applications.
+     * @param onCompleteMessage callback for async operation. Ignored if blockingCall is true.
+     */
+    public void enableUiccApplications(boolean enable, Message onCompleteMessage) {}
+
+    /**
+     * Whether disabling a physical subscription is supported or not.
+     */
+    public boolean canDisablePhysicalSubscription() {
+        return false;
+    }
+
+    /**
      * Get the HAL version.
      *
      * @return the current HalVersion
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index f725020..695e1e7 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -4843,6 +4843,98 @@
     }
 
     /**
+     * Enable or disable uicc applications on the SIM.
+     *
+     * @param enable whether to enable or disable uicc applications.
+     * @param onCompleteMessage a Message to return to the requester
+     */
+    @Override
+    public void enableUiccApplications(boolean enable, Message onCompleteMessage) {
+        IRadio radioProxy = getRadioProxy(onCompleteMessage);
+        if (radioProxy == null) {
+            Rlog.e(RIL.RILJ_LOG_TAG, "Radio Proxy object is null!");
+            if (onCompleteMessage != null) {
+                AsyncResult.forMessage(onCompleteMessage, null,
+                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+                onCompleteMessage.sendToTarget();
+            }
+        }
+
+        if (mRadioVersion.less(RADIO_HAL_VERSION_1_5)) {
+            if (onCompleteMessage != null) {
+                AsyncResult.forMessage(onCompleteMessage, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                onCompleteMessage.sendToTarget();
+            }
+            return;
+        }
+
+        android.hardware.radio.V1_5.IRadio radioProxy15 =
+                (android.hardware.radio.V1_5.IRadio) radioProxy;
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_ENABLE_UICC_APPLICATIONS,
+                onCompleteMessage, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+        try {
+            radioProxy15.enableUiccApplications(rr.mSerial, enable);
+        } catch (RemoteException | RuntimeException e) {
+            handleRadioProxyExceptionForRR(rr, "enableUiccApplications", e);
+        }
+    }
+
+    /**
+     * Whether uicc applications are enabled or not.
+     *
+     * @param onCompleteMessage a Message to return to the requester
+     */
+    @Override
+    public void areUiccApplicationsEnabled(Message onCompleteMessage) {
+        IRadio radioProxy = getRadioProxy(onCompleteMessage);
+        if (radioProxy == null) {
+            Rlog.e(RIL.RILJ_LOG_TAG, "Radio Proxy object is null!");
+            if (onCompleteMessage != null) {
+                AsyncResult.forMessage(onCompleteMessage, null,
+                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+                onCompleteMessage.sendToTarget();
+            }
+        }
+
+        if (mRadioVersion.less(RADIO_HAL_VERSION_1_5)) {
+            if (onCompleteMessage != null) {
+                AsyncResult.forMessage(onCompleteMessage, null,
+                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                onCompleteMessage.sendToTarget();
+            }
+            return;
+        }
+
+        android.hardware.radio.V1_5.IRadio radioProxy15 =
+                (android.hardware.radio.V1_5.IRadio) radioProxy;
+
+        RILRequest rr = obtainRequest(RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT,
+                onCompleteMessage, mRILDefaultWorkSource);
+
+        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+        try {
+            radioProxy15.areUiccApplicationsEnabled(rr.mSerial);
+        } catch (RemoteException | RuntimeException e) {
+            handleRadioProxyExceptionForRR(rr, "areUiccApplicationsEnabled", e);
+        }
+    }
+
+    /**
+     * Whether {@link #enableUiccApplications} is supported, which is supported in 1.5 version.
+     */
+    @Override
+    public boolean canToggleUiccApplicationsEnablement() {
+        return getRadioProxy(null) != null && mRadioVersion
+                .greaterOrEqual(RADIO_HAL_VERSION_1_5);
+    }
+
+    /**
      *  Translates EF_SMS status bits to a status value compatible with
      *  SMS AT commands.  See TS 27.005 3.1.
      */
@@ -5764,6 +5856,10 @@
                 return "RIL_REQUEST_ENABLE_MODEM";
             case RIL_REQUEST_GET_MODEM_STATUS:
                 return "RIL_REQUEST_GET_MODEM_STATUS";
+            case RIL_REQUEST_ENABLE_UICC_APPLICATIONS:
+                return "RIL_REQUEST_ENABLE_UICC_APPLICATIONS";
+            case RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT:
+                return "RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT";
             default: return "<unknown request>";
         }
     }
@@ -5877,6 +5973,8 @@
                 return "RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG";
             case RIL_UNSOL_EMERGENCY_NUMBER_LIST:
                 return "RIL_UNSOL_EMERGENCY_NUMBER_LIST";
+            case RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED:
+                return "RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED";
             default:
                 return "<unknown response>";
         }
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index 5d00690..5b98070 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -63,6 +63,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_PROACTIVE_COMMAND;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_SESSION_END;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SUPP_SVC_NOTIFICATION;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOL_VOICE_RADIO_TECH_CHANGED;
 import static com.android.internal.telephony.RILConstants.RIL_UNSOl_CDMA_PRL_CHANGED;
@@ -761,6 +762,17 @@
         mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
     }
 
+    /** Get unsolicited message for uicc applications enablement changes. */
+    public void uiccApplicationsEnablementChanged(int indicationType, boolean enabled) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) {
+            mRil.unsljLogRet(RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED, enabled);
+        }
+
+        mRil.mUiccApplicationsEnablementRegistrants.notifyResult(enabled);
+    }
+
     /** Incremental network scan results */
     public void networkScanResult(int indicationType,
                                   android.hardware.radio.V1_1.NetworkScanResult result) {
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index da68b69..ea14b40 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -2363,4 +2363,27 @@
     public void setSystemSelectionChannelsResponse(RadioResponseInfo responseInfo) {
         responseVoid(responseInfo);
     }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error.
+     */
+    public void enableUiccApplicationsResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error.
+     * @param enabled whether Uicc applications are enabled.
+     */
+    public void areUiccApplicationsEnabledResponse(RadioResponseInfo responseInfo,
+            boolean enabled) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, enabled);
+            }
+            mRil.processResponseDone(rr, responseInfo, enabled);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 08112f0..1ebea33 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -3055,12 +3055,6 @@
             throw new IllegalArgumentException("Invalid groupUuid");
         }
 
-        // TODO: Revisit whether we need this restriction in R. There's no technical need for it,
-        // but we don't want to change the API behavior at this time.
-        if (getSubscriptionsInGroup(groupUuid, callingPackage).isEmpty()) {
-            throw new IllegalArgumentException("Cannot add subscriptions to a non-existent group!");
-        }
-
         // Makes sure calling package matches caller UID.
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         // If it doesn't have modify phone state permission, or carrier privilege permission,
@@ -3315,6 +3309,9 @@
                         "setSubscriptionEnabled not usable subId " + subId);
             }
 
+            // Nothing to do if it's already active or inactive.
+            if (enable == isActiveSubscriptionId(subId)) return true;
+
             SubscriptionInfo info = SubscriptionController.getInstance()
                     .getAllSubInfoList(mContext.getOpPackageName())
                     .stream()
@@ -3327,6 +3324,8 @@
                 return false;
             }
 
+            // TODO: make sure after slot mapping, we enable the uicc applications for the
+            // subscription we are enabling.
             if (info.isEmbedded()) {
                 return enableEmbeddedSubscription(info, enable);
             } else {
@@ -3358,46 +3357,43 @@
         // updateEnabledSubscriptionGlobalSetting(subId, physicalSlotIndex);
     }
 
-    private static boolean isInactiveInsertedPSim(UiccSlotInfo slotInfo, String cardId) {
-        return !slotInfo.getIsEuicc() && !slotInfo.getIsActive()
-                && slotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT
-                && TextUtils.equals(slotInfo.getCardId(), cardId);
-    }
-
     private boolean enablePhysicalSubscription(SubscriptionInfo info, boolean enable) {
-        if (enable && info.getSimSlotIndex() == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
-            UiccSlotInfo[] slotsInfo = mTelephonyManager.getUiccSlotsInfo();
-            if (slotsInfo == null) return false;
-            boolean foundMatch = false;
-            for (int i = 0; i < slotsInfo.length; i++) {
-                UiccSlotInfo slotInfo = slotsInfo[i];
-                if (isInactiveInsertedPSim(slotInfo, info.getCardString())) {
-                    // We need to send intents to Euicc if we are turning on an inactive pSIM.
-                    // Euicc will decide whether to ask user to switch to DSDS, or change SIM
-                    // slot mapping.
-                    enableSubscriptionOverEuiccManager(info.getSubscriptionId(), enable, i);
-                    foundMatch = true;
-                    break;
-                }
-            }
-
-            if (!foundMatch) {
-                logdl("enablePhysicalSubscription subId " + info.getSubscriptionId()
-                        + " is not inserted.");
-            }
-            // returning false to indicate state is not changed yet. If intent is sent to LPA and
-            // user consents switching, caller needs to listen to subscription info change.
+        if (info == null || !SubscriptionManager.isValidSubscriptionId(info.getSubscriptionId())) {
             return false;
-        } else {
-            return mTelephonyManager.enableModemForSlot(info.getSimSlotIndex(), enable);
         }
 
-        // TODO: uncomment or clean up if we decide whether to support standalone CBRS for Q.
-        // updateEnabledSubscriptionGlobalSetting(
-        //        enable ? subId : SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-        //        physicalSlotIndex);
-        // updateModemStackEnabledGlobalSetting(enable, physicalSlotIndex);
-        // refreshCachedActiveSubscriptionInfoList();
+        int subId = info.getSubscriptionId();
+
+        UiccSlotInfo slotInfo = null;
+        int physicalSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+        UiccSlotInfo[] slotsInfo = mTelephonyManager.getUiccSlotsInfo();
+        if (slotsInfo == null) return false;
+        for (int i = 0; i < slotsInfo.length; i++) {
+            UiccSlotInfo curSlotInfo = slotsInfo[i];
+            if (curSlotInfo.getCardStateInfo() == CARD_STATE_INFO_PRESENT
+                    && TextUtils.equals(curSlotInfo.getCardId(), info.getCardString())) {
+                slotInfo = curSlotInfo;
+                physicalSlotIndex = i;
+                break;
+            }
+        }
+
+        // Can't find the existing SIM.
+        if (slotInfo == null) return false;
+
+        if (enable && !slotInfo.getIsActive()) {
+            // We need to send intents to Euicc if we are turning on an inactive slot.
+            // Euicc will decide whether to ask user to switch to DSDS, or change SIM
+            // slot mapping.
+            enableSubscriptionOverEuiccManager(subId, enable, physicalSlotIndex);
+            return true;
+        } else {
+            // Enable / disable uicc applications.
+            Phone phone = PhoneFactory.getPhone(slotInfo.getLogicalSlotIdx());
+            if (phone == null) return false;
+            phone.enableUiccApplications(enable, null);
+            return true;
+        }
     }
 
     private void enableSubscriptionOverEuiccManager(int subId, boolean enable,
@@ -3776,8 +3772,8 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            // TODO: b/133379187
-            return false;
+            Phone phone = PhoneFactory.getDefaultPhone();
+            return phone != null && phone.canDisablePhysicalSubscription();
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
index 313e56f..0033fff 100644
--- a/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
+++ b/src/java/com/android/internal/telephony/cat/CatCmdMessage.java
@@ -138,10 +138,10 @@
     }
 
     public CatCmdMessage(Parcel in) {
-        mCmdDet = in.readParcelable(null);
-        mTextMsg = in.readParcelable(null);
-        mMenu = in.readParcelable(null);
-        mInput = in.readParcelable(null);
+        mCmdDet = in.readParcelable(CommandDetails.class.getClassLoader());
+        mTextMsg = in.readParcelable(TextMessage.class.getClassLoader());
+        mMenu = in.readParcelable(Menu.class.getClassLoader());
+        mInput = in.readParcelable(Input.class.getClassLoader());
         mLoadIconFailed = (in.readByte() == 1);
         switch (getCmdType()) {
         case LAUNCH_BROWSER:
@@ -150,12 +150,12 @@
             mBrowserSettings.mode = LaunchBrowserMode.values()[in.readInt()];
             break;
         case PLAY_TONE:
-            mToneSettings = in.readParcelable(null);
+            mToneSettings = in.readParcelable(ToneSettings.class.getClassLoader());
             break;
         case SET_UP_CALL:
             mCallSettings = new CallSettings();
-            mCallSettings.confirmMsg = in.readParcelable(null);
-            mCallSettings.callMsg = in.readParcelable(null);
+            mCallSettings.confirmMsg = in.readParcelable(TextMessage.class.getClassLoader());
+            mCallSettings.callMsg = in.readParcelable(TextMessage.class.getClassLoader());
             break;
         case SET_UP_EVENT_LIST:
             mSetupEventListSettings = new SetupEventListSettings();
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 50ceaac..e24a408 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -541,7 +541,7 @@
 
     private void broadcastCatCmdIntent(CatCmdMessage cmdMsg) {
         Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
-        intent.putExtra("STK CMD", cmdMsg);
+        intent.putExtra( "STK CMD", cmdMsg);
         intent.putExtra("SLOT_ID", mSlotId);
         intent.setComponent(AppInterface.getDefaultSTKApplication());
         CatLog.d(this, "Sending CmdMsg: " + cmdMsg+ " on slotid:" + mSlotId);
diff --git a/src/java/com/android/internal/telephony/cat/CommandDetails.java b/src/java/com/android/internal/telephony/cat/CommandDetails.java
index d7c511a..dd1bbcf 100644
--- a/src/java/com/android/internal/telephony/cat/CommandDetails.java
+++ b/src/java/com/android/internal/telephony/cat/CommandDetails.java
@@ -28,7 +28,7 @@
  * Class for Command Details object of proactive commands from SIM.
  * {@hide}
  */
-class CommandDetails extends ValueObject implements Parcelable {
+public class CommandDetails extends ValueObject implements Parcelable {
     @UnsupportedAppUsage
     public boolean compRequired;
     @UnsupportedAppUsage
diff --git a/src/java/com/android/internal/telephony/cat/Input.java b/src/java/com/android/internal/telephony/cat/Input.java
index aaaff43..e9103b8 100644
--- a/src/java/com/android/internal/telephony/cat/Input.java
+++ b/src/java/com/android/internal/telephony/cat/Input.java
@@ -58,7 +58,7 @@
     private Input(Parcel in) {
         text = in.readString();
         defaultText = in.readString();
-        icon = in.readParcelable(null);
+        icon = in.readParcelable(Bitmap.class.getClassLoader());
         minLen = in.readInt();
         maxLen = in.readInt();
         ucs2 = in.readInt() == 1 ? true : false;
diff --git a/src/java/com/android/internal/telephony/cat/Item.java b/src/java/com/android/internal/telephony/cat/Item.java
index 456a46f..702ed4b 100644
--- a/src/java/com/android/internal/telephony/cat/Item.java
+++ b/src/java/com/android/internal/telephony/cat/Item.java
@@ -46,7 +46,7 @@
     public Item(Parcel in) {
         id = in.readInt();
         text = in.readString();
-        icon = in.readParcelable(null);
+        icon = in.readParcelable(Bitmap.class.getClassLoader());
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/cat/Menu.java b/src/java/com/android/internal/telephony/cat/Menu.java
index a93fd1f..f1d19b5 100644
--- a/src/java/com/android/internal/telephony/cat/Menu.java
+++ b/src/java/com/android/internal/telephony/cat/Menu.java
@@ -58,12 +58,12 @@
 
     private Menu(Parcel in) {
         title = in.readString();
-        titleIcon = in.readParcelable(null);
+        titleIcon = in.readParcelable(Bitmap.class.getClassLoader());
         // rebuild items list.
         items = new ArrayList<Item>();
         int size = in.readInt();
         for (int i=0; i<size; i++) {
-            Item item = in.readParcelable(null);
+            Item item = in.readParcelable(Item.class.getClassLoader());
             items.add(item);
         }
         defaultItem = in.readInt();
diff --git a/src/java/com/android/internal/telephony/cat/TextMessage.java b/src/java/com/android/internal/telephony/cat/TextMessage.java
index eddca4c..e7c2da6 100644
--- a/src/java/com/android/internal/telephony/cat/TextMessage.java
+++ b/src/java/com/android/internal/telephony/cat/TextMessage.java
@@ -40,7 +40,7 @@
     private TextMessage(Parcel in) {
         title = in.readString();
         text = in.readString();
-        icon = in.readParcelable(null);
+        icon = in.readParcelable(Bitmap.class.getClassLoader());
         iconSelfExplanatory = in.readInt() == 1 ? true : false;
         isHighPriority = in.readInt() == 1 ? true : false;
         responseNeeded = in.readInt() == 1 ? true : false;
diff --git a/src/java/com/android/internal/telephony/cat/ToneSettings.java b/src/java/com/android/internal/telephony/cat/ToneSettings.java
index 61c1573..4e94ead 100644
--- a/src/java/com/android/internal/telephony/cat/ToneSettings.java
+++ b/src/java/com/android/internal/telephony/cat/ToneSettings.java
@@ -35,8 +35,8 @@
     }
 
     private ToneSettings(Parcel in) {
-        duration = in.readParcelable(null);
-        tone = in.readParcelable(null);
+        duration = in.readParcelable(Duration.class.getClassLoader());
+        tone = in.readParcelable(Tone.class.getClassLoader());
         vibrate = in.readInt() == 1;
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index ae8dd62..e3b325c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.anyBoolean;
@@ -33,6 +34,7 @@
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -66,6 +68,7 @@
 
 import com.android.internal.telephony.test.SimulatedCommands;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus;
+import com.android.internal.telephony.uicc.IccCardStatus;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.IccVmNotSupportedException;
 import com.android.internal.telephony.uicc.UiccController;
@@ -89,6 +92,10 @@
 public class GsmCdmaPhoneTest extends TelephonyTest {
     @Mock
     private Handler mTestHandler;
+    @Mock
+    private UiccSlot mUiccSlot;
+    @Mock
+    private CommandsInterface mMockCi;
 
     //mPhoneUnderTest
     private GsmCdmaPhone mPhoneUT;
@@ -1197,4 +1204,24 @@
                 CarrierConfigManager.KEY_USE_USIM_BOOL, true);
         assertEquals(msisdn, mPhoneUT.getLine1Number());
     }
+
+    @Test
+    @SmallTest
+    public void testEnableUiccApplications() throws Exception {
+        mPhoneUT.mCi = mMockCi;
+        // UiccSlot is null. Doing nothing.
+        mPhoneUT.enableUiccApplications(true, null);
+        verify(mMockCi, never()).enableUiccApplications(anyBoolean(), any());
+
+        // Card state is not PRESENT. Doing nothing.
+        doReturn(mUiccSlot).when(mUiccController).getUiccSlotForPhone(anyInt());
+        doReturn(IccCardStatus.CardState.CARDSTATE_ABSENT).when(mUiccSlot).getCardState();
+        mPhoneUT.enableUiccApplications(true, null);
+        verify(mMockCi, never()).enableUiccApplications(anyBoolean(), any());
+
+        doReturn(IccCardStatus.CardState.CARDSTATE_PRESENT).when(mUiccSlot).getCardState();
+        Message message = Message.obtain();
+        mPhoneUT.enableUiccApplications(true, message);
+        verify(mMockCi).enableUiccApplications(eq(true), eq(message));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index fda30f5..cc1cb2c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -26,6 +26,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DELETE_SMS_ON_SIM;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEVICE_IDENTITY;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DTMF;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENABLE_UICC_APPLICATIONS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PIN;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ENTER_SIM_PIN2;
@@ -40,6 +41,7 @@
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_RADIO_CAPABILITY;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SIM_STATUS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_SMSC_ADDRESS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND;
@@ -82,11 +84,14 @@
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -97,13 +102,13 @@
 import android.hardware.radio.V1_0.CdmaSmsMessage;
 import android.hardware.radio.V1_0.DataProfileInfo;
 import android.hardware.radio.V1_0.GsmSmsMessage;
-import android.hardware.radio.V1_0.IRadio;
 import android.hardware.radio.V1_0.ImsSmsMessage;
 import android.hardware.radio.V1_0.NvWriteItem;
 import android.hardware.radio.V1_0.RadioError;
 import android.hardware.radio.V1_0.RadioResponseInfo;
 import android.hardware.radio.V1_0.RadioResponseType;
 import android.hardware.radio.V1_0.SmsWriteArgs;
+import android.hardware.radio.V1_5.IRadio;
 import android.hardware.radio.deprecated.V1_0.IOemHook;
 import android.net.ConnectivityManager;
 import android.net.LinkAddress;
@@ -177,7 +182,12 @@
     @Mock
     private IOemHook mOemHookProxy;
 
-    private HalVersion mRadioVersion = new HalVersion(1, 0);
+    private HalVersion mRadioVersionV10 = new HalVersion(1, 0);
+    private HalVersion mRadioVersionV11 = new HalVersion(1, 1);
+    private HalVersion mRadioVersionV12 = new HalVersion(1, 2);
+    private HalVersion mRadioVersionV13 = new HalVersion(1, 3);
+    private HalVersion mRadioVersionV14 = new HalVersion(1, 4);
+    private HalVersion mRadioVersionV15 = new HalVersion(1, 5);
 
     private RIL mRILInstance;
     private RIL mRILUnderTest;
@@ -273,7 +283,7 @@
         doReturn(mOemHookProxy).when(mRILUnderTest).getOemHookProxy(any());
 
         try {
-            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersion);
+            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV10);
         } catch (Exception e) {
         }
     }
@@ -2000,4 +2010,38 @@
 
         assertTrue(result.equals(expected));
     }
+
+    @Test
+    public void testEnableUiccApplications() throws Exception {
+        // Not supported on Radio 1.0.
+        mRILUnderTest.enableUiccApplications(false, obtainMessage());
+        verify(mRadioProxy, never()).enableUiccApplications(anyInt(), anyBoolean());
+
+        // Make radio version 1.5 to support the operation.
+        try {
+            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+        } catch (Exception e) {
+        }
+        mRILUnderTest.enableUiccApplications(false, obtainMessage());
+        verify(mRadioProxy).enableUiccApplications(mSerialNumberCaptor.capture(), anyBoolean());
+        verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
+                RIL_REQUEST_ENABLE_UICC_APPLICATIONS);
+    }
+
+    @Test
+    public void testAreUiccApplicationsEnabled() throws Exception {
+        // Not supported on Radio 1.0.
+        mRILUnderTest.areUiccApplicationsEnabled(obtainMessage());
+        verify(mRadioProxy, never()).areUiccApplicationsEnabled(mSerialNumberCaptor.capture());
+
+        // Make radio version 1.5 to support the operation.
+        try {
+            replaceInstance(RIL.class, "mRadioVersion", mRILUnderTest, mRadioVersionV15);
+        } catch (Exception e) {
+        }
+        mRILUnderTest.areUiccApplicationsEnabled(obtainMessage());
+        verify(mRadioProxy).areUiccApplicationsEnabled(mSerialNumberCaptor.capture());
+        verifyRILResponse(mRILUnderTest, mSerialNumberCaptor.getValue(),
+                RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
index 64b0c38..93077e3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
@@ -1444,4 +1444,10 @@
     @Override
     public void stopNattKeepalive(int sessionHandle, Message result)  {
     }
+
+    @Override
+    public void registerUiccApplicationEnablementChanged(Handler h, int what, Object obj) {}
+
+    @Override
+    public void unregisterUiccApplicationEnablementChanged(Handler h) {}
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 609b028..899ffc8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -60,6 +60,7 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
+import java.util.UUID;
 
 public class SubscriptionControllerTest extends TelephonyTest {
     private static final int SINGLE_SIM = 1;
@@ -729,6 +730,57 @@
 
     @Test
     @SmallTest
+    public void testAddSubscriptionIntoGroupWithCarrierPrivilegePermission() throws Exception {
+        testInsertSim();
+        // Adding a second profile and mark as embedded.
+        // TODO b/123300875 slot index 1 is not expected to be valid
+        mSubscriptionControllerUT.addSubInfoRecord("test2", 1);
+        ContentValues values = new ContentValues();
+        values.put(SubscriptionManager.IS_EMBEDDED, 1);
+        mFakeTelephonyProvider.update(SubscriptionManager.CONTENT_URI, values,
+                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + 2, null);
+        mSubscriptionControllerUT.refreshCachedActiveSubscriptionInfoList();
+
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+        mContextFixture.addCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE);
+
+        // Create group for sub 1.
+        int[] subIdList = new int[] {1};
+        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
+        ParcelUuid groupId = mSubscriptionControllerUT.createSubscriptionGroup(
+                subIdList, "packageName1");
+
+        // Try to add sub 2 into group of sub 1.
+        // Should fail as it doesn't have carrier privilege on sub 2.
+        try {
+            mSubscriptionControllerUT.addSubscriptionsIntoGroup(
+                    new int[] {2}, groupId, "packageName1");
+            fail("addSubscriptionsIntoGroup should fail with no permission on sub 2.");
+        } catch (SecurityException e) {
+            // Expected result.
+        }
+
+        doReturn(false).when(mTelephonyManager).hasCarrierPrivileges(1);
+        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(2);
+        // Try to add sub 2 into group of sub 1.
+        // Should fail as it doesn't have carrier privilege on sub 1.
+        try {
+            mSubscriptionControllerUT.addSubscriptionsIntoGroup(
+                    new int[] {2}, groupId, "packageName2");
+            fail("addSubscriptionsIntoGroup should fail with no permission on the group (sub 1).");
+        } catch (SecurityException e) {
+            // Expected result.
+        }
+
+        doReturn(true).when(mTelephonyManager).hasCarrierPrivileges(1);
+        mSubscriptionControllerUT.addSubscriptionsIntoGroup(new int[] {2}, groupId, "packageName2");
+        List<SubscriptionInfo> infoList = mSubscriptionControllerUT
+                .getSubscriptionsInGroup(groupId, "packageName2");
+        assertEquals(2, infoList.size());
+    }
+
+    @Test
+    @SmallTest
     public void testUpdateSubscriptionGroupWithCarrierPrivilegePermission() throws Exception {
         testInsertSim();
         // Adding a second profile and mark as embedded.
@@ -893,6 +945,15 @@
                 .getSubscriptionsInGroup(groupUuid, mContext.getOpPackageName());
         assertEquals(1, infoList.size());
         assertEquals(2, infoList.get(0).getSubscriptionId());
+
+        // Adding sub 1 into a non-existing UUID, which should be granted.
+        groupUuid = new ParcelUuid(UUID.randomUUID());
+        mSubscriptionControllerUT.addSubscriptionsIntoGroup(
+                subIdList, groupUuid, mContext.getOpPackageName());
+        infoList = mSubscriptionControllerUT
+                .getSubscriptionsInGroup(groupUuid, mContext.getOpPackageName());
+        assertEquals(1, infoList.size());
+        assertEquals(1, infoList.get(0).getSubscriptionId());
     }
 
     private void registerMockTelephonyRegistry() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index 3b23510..2dce712 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
@@ -806,8 +807,9 @@
         SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
 
         // verify that a broadcast receiver is registered for current user (user == null) based on
-        // implementation in ContextFixture
-        verify(mContext, times(1)).registerReceiverAsUser(any(BroadcastReceiver.class),
+        // implementation in ContextFixture. registerReceiver may be called more than once (for
+        // example by GsmInboundSmsHandler if TEST_MODE is true)
+        verify(mContext, atLeastOnce()).registerReceiverAsUser(any(BroadcastReceiver.class),
                 eq((UserHandle)null), any(IntentFilter.class), eq((String)null), eq((Handler)null));
 
         // wait for ScanRawTableThread