Merge "Tests for correct decoding when TP-OA contains non-integer information"
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index e4e7f35..5960051 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -1392,11 +1392,14 @@
 
             case EVENT_CONFERENCE_RESULT:
                 if (isPhoneTypeGsm()) {
-                    // The conference merge failed, so notify listeners.  Ultimately this bubbles up
-                    // to Telecom, which will inform the InCall UI of the failure.
-                    Connection connection = mForegroundCall.getLatestConnection();
-                    if (connection != null) {
-                        connection.onConferenceMergeFailed();
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        // The conference merge failed, so notify listeners.  Ultimately this
+                        // bubbles up to Telecom, which will inform the InCall UI of the failure.
+                        Connection connection = mForegroundCall.getLatestConnection();
+                        if (connection != null) {
+                            connection.onConferenceMergeFailed();
+                        }
                     }
                 }
                 // fall through
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index b4884fd..9873d97 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -2065,6 +2065,7 @@
     private void syncClirSetting() {
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         int clirSetting = sp.getInt(CLIR_KEY + getPhoneId(), -1);
+        Rlog.i(LOG_TAG, "syncClirSetting: " + CLIR_KEY + getPhoneId() + "=" + clirSetting);
         if (clirSetting >= 0) {
             mCi.setCLIR(clirSetting, null);
         }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 75b6052..1ccf1c4 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -1333,6 +1333,8 @@
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
         editor.putInt(CLIR_KEY + getPhoneId(), commandInterfaceCLIRMode);
+        Rlog.i(LOG_TAG, "saveClirSetting: " + CLIR_KEY + getPhoneId() + "=" +
+                commandInterfaceCLIRMode);
 
         // Commit and log the result.
         if (!editor.commit()) {
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 4598b0c..2ec5101 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -76,6 +76,7 @@
 import android.widget.TextView;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccController;
@@ -970,7 +971,8 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      * -param destAddr the destination phone number (for short code confirmation)
      */
-    protected void sendRawPdu(SmsTracker tracker) {
+    @VisibleForTesting
+    public void sendRawPdu(SmsTracker tracker) {
         HashMap map = tracker.getData();
         byte pdu[] = (byte[]) map.get("pdu");
 
@@ -1075,7 +1077,7 @@
 
             // Wait for user confirmation unless the user has set permission to always allow/deny
             int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
-                    tracker.mAppInfo.packageName);
+                    tracker.getAppPackageName());
             if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
                 // First time trying to send to premium SMS.
                 premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
@@ -1150,7 +1152,7 @@
             return;     // queue limit reached; error was returned to caller
         }
 
-        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName, tracker.mUserId);
+        CharSequence appLabel = getAppLabel(tracker.getAppPackageName(), tracker.mUserId);
         Resources r = Resources.getSystem();
         Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
 
@@ -1188,7 +1190,7 @@
             detailsId = R.string.sms_short_code_details;
         }
 
-        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName, tracker.mUserId);
+        CharSequence appLabel = getAppLabel(tracker.getAppPackageName(), tracker.mUserId);
         Resources r = Resources.getSystem();
         Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message,
                 appLabel, tracker.mDestAddress));
@@ -1401,6 +1403,14 @@
         }
 
         /**
+         * Get the App package name
+         * @return App package name info
+         */
+        public String getAppPackageName() {
+            return mAppInfo != null ? mAppInfo.packageName : null;
+        }
+
+        /**
          * Update the status of this message if we persisted it
          */
         public void updateSentMessageStatus(Context context, int status) {
@@ -1710,7 +1720,7 @@
                 }
                 sendMessage(msg);
             }
-            setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
+            setPremiumSmsPermission(mTracker.getAppPackageName(), newSmsPermission);
         }
 
         @Override
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 2049d05..fb8231e 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -721,6 +721,7 @@
             try {
                 mPhone.notifySignalStrength();
                 notified = true;
+                mLastSignalStrength = mSignalStrength;
             } catch (NullPointerException ex) {
                 loge("updateSignalStrength() Phone already destroyed: " + ex
                         + "SignalStrength not notified");
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index 73e9a42..402a5ef 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -29,8 +29,8 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.PhoneNumberUtils;
-import android.util.AtomicFile;
 import android.telephony.Rlog;
+import android.util.AtomicFile;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -48,10 +48,10 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Pattern;
 
 /**
@@ -85,7 +85,7 @@
     static final int CATEGORY_STANDARD_SHORT_CODE = 2;
 
     /** Return value from {@link #checkDestination} for possible premium short codes. */
-    static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
+    public static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
 
     /** Return value from {@link #checkDestination} for premium short codes. */
     static final int CATEGORY_PREMIUM_SHORT_CODE = 4;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 634a3c4..5bd3eea 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -113,6 +113,10 @@
         void onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState);
     }
 
+    public interface SharedPreferenceProxy {
+        SharedPreferences getDefaultSharedPreferences(Context context);
+    }
+
     private static final boolean DBG = true;
 
     // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
@@ -128,6 +132,7 @@
             "UTLTE", "UTWiFi"};
 
     private TelephonyMetrics mMetrics;
+    private boolean mCarrierConfigLoaded = false;
 
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -212,6 +217,7 @@
                     log("onReceive : Updating mAllowEmergencyVideoCalls = " +
                             mAllowEmergencyVideoCalls);
                 }
+                mCarrierConfigLoaded  = true;
             } else if (TelecomManager.ACTION_CHANGE_DEFAULT_DIALER.equals(intent.getAction())) {
                 mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra(
                         TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME)));
@@ -598,6 +604,13 @@
      */
     private boolean mShouldUpdateImsConfigOnDisconnect = false;
 
+    /**
+     * Default implementation for retrieving shared preferences; uses the actual PreferencesManager.
+     */
+    private SharedPreferenceProxy mSharedPreferenceProxy = (Context context) -> {
+        return PreferenceManager.getDefaultSharedPreferences(context);
+    };
+
     // Callback fires when ImsManager MMTel Feature changes state
     private ImsServiceProxy.INotifyStatusChanged mNotifyStatusChangedCallback = () -> {
         try {
@@ -678,11 +691,23 @@
         sendEmptyMessage(EVENT_GET_IMS_SERVICE);
     }
 
+    /**
+     * Test-only method used to mock out access to the shared preferences through the
+     * {@link PreferenceManager}.
+     * @param sharedPreferenceProxy
+     */
+    @VisibleForTesting
+    public void setSharedPreferenceProxy(SharedPreferenceProxy sharedPreferenceProxy) {
+        mSharedPreferenceProxy = sharedPreferenceProxy;
+    }
+
     private int getPackageUid(Context context, String pkg) {
         if (pkg == null) {
             return NetworkStats.UID_ALL;
         }
 
+        // Initialize to UID_ALL so at least it can be counted to overall data usage if
+        // the dialer's package uid is not available.
         int uid = NetworkStats.UID_ALL;
         try {
             uid = context.getPackageManager().getPackageUid(pkg, 0);
@@ -735,10 +760,16 @@
             multiEndpoint.setExternalCallStateListener(
                     mPhone.getExternalCallTracker().getExternalCallStateListener());
         }
+
+        if (mCarrierConfigLoaded) {
+            ImsManager.updateImsServiceConfig(mPhone.getContext(),
+                    mPhone.getPhoneId(), true);
+        }
     }
 
     private void stopListeningForCalls() {
         try {
+            resetImsCapabilities();
             // Only close on valid session.
             if (mImsManager != null && mServiceId > 0) {
                 mImsManager.close(mServiceId);
@@ -794,8 +825,16 @@
 
     public Connection dial(String dialString, int videoState, Bundle intentExtras) throws
             CallStateException {
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
-        int oirMode = sp.getInt(Phone.CLIR_KEY, CommandsInterface.CLIR_DEFAULT);
+        int oirMode;
+        if (mSharedPreferenceProxy != null && mPhone.getDefaultPhone() != null) {
+            SharedPreferences sp = mSharedPreferenceProxy.getDefaultSharedPreferences(
+                    mPhone.getContext());
+            oirMode = sp.getInt(Phone.CLIR_KEY + mPhone.getDefaultPhone().getPhoneId(),
+                    CommandsInterface.CLIR_DEFAULT);
+        } else {
+            loge("dial; could not get default CLIR mode.");
+            oirMode = CommandsInterface.CLIR_DEFAULT;
+        }
         return dial(dialString, oirMode, videoState, intentExtras);
     }
 
@@ -2302,10 +2341,21 @@
                     mPhone.stopOnHoldTone(conn);
                     mOnHoldToneStarted = false;
                 }
-
                 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_UNHELD, null);
             }
 
+            boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
+                    com.android.internal.R.bool.config_useVideoPauseWorkaround);
+            if (useVideoPauseWorkaround && mSupportPauseVideo &&
+                    VideoProfile.isVideo(conn.getVideoState())) {
+                // If we are using the video pause workaround, the vendor IMS code has issues
+                // with video pause signalling.  In this case, when a call is remotely
+                // held, the modem does not reliably change the video state of the call to be
+                // paused.
+                // As a workaround, we will turn on that bit now.
+                conn.changeToUnPausedState();
+            }
+
             SuppServiceNotification supp = new SuppServiceNotification();
             // Type of notification: 0 = MO; 1 = MT
             // Refer SuppServiceNotification class documentation.
@@ -2327,8 +2377,19 @@
                     mOnHoldToneStarted = true;
                     mOnHoldToneId = System.identityHashCode(conn);
                 }
-
                 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null);
+
+                boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
+                        com.android.internal.R.bool.config_useVideoPauseWorkaround);
+                if (useVideoPauseWorkaround && mSupportPauseVideo &&
+                        VideoProfile.isVideo(conn.getVideoState())) {
+                    // If we are using the video pause workaround, the vendor IMS code has issues
+                    // with video pause signalling.  In this case, when a call is remotely
+                    // held, the modem does not reliably change the video state of the call to be
+                    // paused.
+                    // As a workaround, we will turn on that bit now.
+                    conn.changeToPausedState();
+                }
             }
 
             SuppServiceNotification supp = new SuppServiceNotification();
@@ -2907,6 +2968,17 @@
         // a separate entry if uid is different from the previous snapshot.
         NetworkStats vtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
         vtDataUsageUidSnapshot.combineAllValues(mVtDataUsageUidSnapshot);
+
+        // The dialer uid might not be initialized correctly during boot up due to telecom service
+        // not ready or its default dialer cache not ready. So we double check again here to see if
+        // default dialer uid is really not available.
+        if (mDefaultDialerUid.get() == NetworkStats.UID_ALL) {
+            final TelecomManager telecomManager =
+                    (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
+            mDefaultDialerUid.set(
+                    getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage()));
+        }
+
         // Since the modem only reports the total vt data usage rather than rx/tx separately,
         // the only thing we can do here is splitting the usage into half rx and half tx.
         vtDataUsageUidSnapshot.combineValues(new NetworkStats.Entry(
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 4f99a57..b39a6fb 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -828,10 +828,7 @@
                     }
 
                     if (!mShouldIgnoreVideoStateChanges) {
-                        if (mImsVideoCallProviderWrapper != null) {
-                            mImsVideoCallProviderWrapper.onVideoStateChanged(newVideoState);
-                        }
-                        setVideoState(newVideoState);
+                        updateVideoState(newVideoState);
                         changed = true;
                     } else {
                         Rlog.d(LOG_TAG, "updateMediaCapabilities - ignoring video state change " +
@@ -894,6 +891,13 @@
         return changed;
     }
 
+    private void updateVideoState(int newVideoState) {
+        if (mImsVideoCallProviderWrapper != null) {
+            mImsVideoCallProviderWrapper.onVideoStateChanged(newVideoState);
+        }
+        setVideoState(newVideoState);
+    }
+
     public void sendRttModifyRequest(android.telecom.Connection.RttTextStream textStream) {
         getImsCall().sendRttModifyRequest();
         setCurrentRttTextStream(textStream);
@@ -1175,4 +1179,18 @@
 
         return mImsVideoCallProviderWrapper.wasVideoPausedFromSource(source);
     }
+
+    public void changeToPausedState() {
+        int newVideoState = getVideoState() | VideoProfile.STATE_PAUSED;
+        Rlog.i(LOG_TAG, "ImsPhoneConnection: changeToPausedState - setting paused bit; "
+                + "newVideoState=" + VideoProfile.videoStateToString(newVideoState));
+        updateVideoState(newVideoState);
+    }
+
+    public void changeToUnPausedState() {
+        int newVideoState = getVideoState() & ~VideoProfile.STATE_PAUSED;
+        Rlog.i(LOG_TAG, "ImsPhoneConnection: changeToUnPausedState - unsetting paused bit; "
+                + "newVideoState=" + VideoProfile.videoStateToString(newVideoState));
+        updateVideoState(newVideoState);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index fa0add5..93d54d0 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -94,6 +94,8 @@
     private static final String TAG_PKG_REF_DO = "CA";
     private static final String TAG_AR_DO = "E3";
     private static final String TAG_PERM_AR_DO = "DB";
+    private static final String TAG_AID_REF_DO = "4F";
+    private static final String CARRIER_PRIVILEGE_AID = "FFFFFFFFFFFF";
 
     private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
     private static final int EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE = 2;
@@ -562,37 +564,52 @@
             if (rule.startsWith(TAG_REF_DO)) {
                 TLV refDo = new TLV(TAG_REF_DO); //E1
                 rule = refDo.parse(rule, false);
-
-                // Skip unrelated rules.
-                if (!refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+                // Allow 4F tag with a default value "FF FF FF FF FF FF" to be compatible with
+                // devices having GP access control enforcer:
+                //  - If no 4F tag is present, it's a CP rule.
+                //  - If 4F tag has value "FF FF FF FF FF FF", it's a CP rule.
+                //  - If 4F tag has other values, it's not a CP rule and Android should ignore it.
+                TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
+                if (refDo.value.startsWith(TAG_AID_REF_DO)) {
+                    TLV cpDo = new TLV(TAG_AID_REF_DO); //4F
+                    String remain = cpDo.parse(refDo.value, false);
+                    if (!cpDo.lengthBytes.equals("06") || !cpDo.value.equals(CARRIER_PRIVILEGE_AID)
+                            || remain.isEmpty() || !remain.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+                        return null;
+                    }
+                    tmp = deviceDo.parse(remain, false);
+                    certificateHash = deviceDo.value;
+                } else if (refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+                    tmp = deviceDo.parse(refDo.value, false);
+                    certificateHash = deviceDo.value;
+                } else {
                     return null;
                 }
-
-                TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
-                tmp = deviceDo.parse(refDo.value, false);
-                certificateHash = deviceDo.value;
-
                 if (!tmp.isEmpty()) {
-                  if (!tmp.startsWith(TAG_PKG_REF_DO)) {
-                      return null;
-                  }
-                  TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
-                  pkgDo.parse(tmp, true);
-                  packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
+                    if (!tmp.startsWith(TAG_PKG_REF_DO)) {
+                        return null;
+                    }
+                    TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
+                    pkgDo.parse(tmp, true);
+                    packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
                 } else {
-                  packageName = null;
+                    packageName = null;
                 }
             } else if (rule.startsWith(TAG_AR_DO)) {
                 TLV arDo = new TLV(TAG_AR_DO); //E3
                 rule = arDo.parse(rule, false);
-
-                // Skip unrelated rules.
-                if (!arDo.value.startsWith(TAG_PERM_AR_DO)) {
+                // Skip all the irrelevant tags (All the optional tags here are two bytes
+                // according to the spec GlobalPlatform Secure Element Access Control).
+                String remain = arDo.value;
+                while (!remain.isEmpty() && !remain.startsWith(TAG_PERM_AR_DO)) {
+                    TLV tmpDo = new TLV(remain.substring(0, 2));
+                    remain = tmpDo.parse(remain, false);
+                }
+                if (remain.isEmpty()) {
                     return null;
                 }
-
                 TLV permDo = new TLV(TAG_PERM_AR_DO); //DB
-                permDo.parse(arDo.value, true);
+                permDo.parse(remain, true);
             } else  {
                 // Spec requires it must be either TAG_REF_DO or TAG_AR_DO.
                 throw new RuntimeException("Invalid Rule type");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index d62a23a..23e30a2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -439,7 +439,14 @@
 
         @Override
         public int checkCallingOrSelfPermission(String permission) {
-            return PackageManager.PERMISSION_GRANTED;
+            if (mPermissionTable.contains(permission)
+                    || mPermissionTable.contains(PERMISSION_ENABLE_ALL)) {
+                logd("checkCallingOrSelfPermission: " + permission + " return GRANTED");
+                return PackageManager.PERMISSION_GRANTED;
+            } else {
+                logd("checkCallingOrSelfPermission: " + permission + " return DENIED");
+                return PackageManager.PERMISSION_DENIED;
+            }
         }
 
         @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 8ab749c..335634d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -16,9 +16,14 @@
 
 package com.android.internal.telephony.gsm;
 
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
 import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -35,6 +40,7 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.provider.Telephony;
 import android.support.test.filters.FlakyTest;
 import android.telephony.SmsManager;
@@ -42,8 +48,10 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Singleton;
 
+import com.android.internal.telephony.ContextFixture;
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.ImsSMSDispatcher;
+import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.TelephonyTestUtils;
 import com.android.internal.telephony.TestApplication;
@@ -51,8 +59,11 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import java.util.HashMap;
+
 public class GsmSmsDispatcherTest extends TelephonyTest {
     @Mock
     private android.telephony.SmsMessage mSmsMessage;
@@ -65,6 +76,8 @@
     @Mock
     private CountryDetector mCountryDetector;
     @Mock
+    private SMSDispatcher.SmsTracker mSmsTracker;
+    @Mock
     private ISub.Stub mISubStub;
     private Object mLock = new Object();
     private boolean mReceivedTestIntent = false;
@@ -191,4 +204,35 @@
             assertEquals(SmsManager.RESULT_ERROR_NULL_PDU, mTestReceiver.getResultCode());
         }
     }
+
+    @Test
+    public void testSendRawPduWithEventStopSending() throws Exception {
+        setupMockPackagePermissionChecks();
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+
+        // return a fake value to pass getData()
+        HashMap data = new HashMap<String, String>();
+        data.put("pdu", new byte[1]);
+        when(mSmsTracker.getData()).thenReturn(data);
+
+        // Set values to return to simulate EVENT_STOP_SENDING
+        when(mSmsUsageMonitor.checkDestination(any(), any()))
+                .thenReturn(CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE);
+        when(mSmsUsageMonitor.getPremiumSmsPermission(any()))
+                .thenReturn(PREMIUM_SMS_PERMISSION_NEVER_ALLOW);
+        when(mSmsTracker.getAppPackageName()).thenReturn("");
+
+        // Settings.Global.DEVICE_PROVISIONED to 1
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 1);
+
+        mGsmSmsDispatcher.sendRawPdu(mSmsTracker);
+
+        verify(mSmsUsageMonitor, times(1)).checkDestination(any(), any());
+        verify(mSmsUsageMonitor, times(1)).getPremiumSmsPermission(any());
+        ArgumentCaptor<Integer> argumentCaptor = ArgumentCaptor
+                .forClass(Integer.class);
+        verify(mSmsTracker, times(1)).onFailed(any(), argumentCaptor.capture(), anyInt());
+        assertEquals(RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, (int) argumentCaptor.getValue());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 0b3d6f1..be8d6f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -37,12 +37,14 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.support.test.filters.FlakyTest;
 import android.telephony.DisconnectCause;
+import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ims.feature.ImsFeature;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -57,6 +59,8 @@
 import com.android.ims.ImsServiceClass;
 import com.android.ims.internal.ImsCallSession;
 import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
@@ -66,6 +70,7 @@
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -81,6 +86,8 @@
     private int mServiceId;
     @Mock
     private ImsCallSession mImsCallSession;
+    @Mock
+    private SharedPreferences mSharedPreferences;
     private Handler mCTHander;
 
     private class ImsCTHandlerThread extends HandlerThread {
@@ -348,6 +355,31 @@
         assertEquals(Call.State.HOLDING, mCTUT.mBackgroundCall.getState());
     }
 
+    /**
+     * Ensures that the dial method will perform a shared preferences lookup using the correct
+     * shared preference key to determine the CLIR mode.
+     */
+    @Test
+    @SmallTest
+    public void testDialClirMode() {
+        mCTUT.setSharedPreferenceProxy((Context context) -> {
+            return mSharedPreferences;
+        });
+        ArgumentCaptor<String> mStringCaptor = ArgumentCaptor.forClass(String.class);
+        doReturn(CommandsInterface.CLIR_INVOCATION).when(mSharedPreferences).getInt(
+                mStringCaptor.capture(), anyInt());
+
+        try {
+            mCTUT.dial("+17005554141", VideoProfile.STATE_AUDIO_ONLY, null);
+        } catch (CallStateException cse) {
+            cse.printStackTrace();
+            Assert.fail("unexpected exception thrown" + cse.getMessage());
+        }
+
+        // Ensure that the correct key was queried from the shared prefs.
+        assertEquals("clir_key0", mStringCaptor.getValue());
+    }
+
     @FlakyTest
     @Test
     @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
new file mode 100644
index 0000000..e6ca407
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2016 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.uicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
+
+import android.content.pm.Signature;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
+    private UiccCarrierPrivilegeRules mUiccCarrierPrivilegeRules;
+    public UiccCarrierPrivilegeRulesTest() {
+        super();
+    }
+    private UiccCarrierPrivilegeRulesHandlerThread mTestHandlerThread;
+    private Handler mHandler;
+
+    private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
+    private static final int EVENT_TEST_DONE = 2;
+
+    @Mock
+    private UiccCard mUiccCard;
+
+    private class UiccCarrierPrivilegeRulesHandlerThread extends HandlerThread {
+
+        private UiccCarrierPrivilegeRulesHandlerThread(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            /* create a custom handler for the Handler Thread */
+            mHandler = new Handler(mTestHandlerThread.getLooper()) {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
+                            /* Upon handling this event, new CarrierPrivilegeRule
+                            will be created with the looper of HandlerThread */
+                            mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(
+                                    mUiccCard, mHandler.obtainMessage(EVENT_TEST_DONE));
+                            break;
+                        case EVENT_TEST_DONE:
+                            setReady(true);
+                            break;
+                        default:
+                            logd("Unknown Event " + msg.what);
+                    }
+                }
+            };
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mTestHandlerThread = new UiccCarrierPrivilegeRulesHandlerThread(TAG);
+        mTestHandlerThread.start();
+
+        waitUntilReady();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTestHandlerThread.quit();
+        super.tearDown();
+        mUiccCarrierPrivilegeRules = null;
+    }
+
+    private void testHelper(String hexString) {
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[2];
+                AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyString(), any(Message.class));
+
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+    }
+
+    @Test
+    @SmallTest
+    public void testHandleMessage_Normal() {
+        /**
+         * FF40 45
+         *   E2 43
+         *      E1 35
+         *         C1 14 ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4
+         *         CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+         *      E3 0A
+         *         DB 08 0000000000000001
+         */
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(1, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testHandleMessage_With4FD0D1() {
+        /**
+         * FF40 34
+         *   E2 32
+         *      E1 1E
+         *         4F 06 FF FF FF FF FF FF
+         *         C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+         *      E3 10
+         *         D0 01 01
+         *         D1 01 01
+         *         DB 08 00 00 00 00 00 00 00 01
+         */
+        final String hexString = "FF4034E232E11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED84"
+                + "93C313A137F89FA2765E310D00101D10101DB080000000000000001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testHandleMessage_With4FD0() {
+        /**
+         * FF40 31
+         *   E2 2F
+         *      E1 1E
+         *         4F 06 FF FF FF FF FF FF
+         *         C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+         *      E3 0D
+         *         D0 01 01
+         *         DB 08 00 00 00 00 00 00 00 01
+         */
+        final String hexString = "FF4031E22FE11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED8493C313A"
+                + "137F89FA2765E30DD00101DB080000000000000001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testHandleMessage_TwoMessages() {
+        /**
+         * FF40 68
+         *   E2 39
+         *      E1 2B
+         *         4F 06 FFFFFFFFFFFF
+         *         C1 02 B61B
+         *         CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+         *      E3 0A
+         *         D0 01 01
+         *         D1 01 01
+         *         DB 02 0001
+         *   E2 2B
+         *      E1 23
+         *         C1 02 ABCD
+         *         CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+         *      E3 04
+         *         DB 02 0001
+         */
+        final String hexString =
+                "FF4068E239E12B4F06FFFFFFFFFFFFC102B61BCA1D636F6D2E676F6F676C652E616E64726F69642"
+                        + "E617070732E6D79617070E30AD00101D10101DB020001E22BE123C102ABCDCA1D636F"
+                        + "6D2E676F6F676C652E616E64726F69642E617070732E6D79617070E304DB020001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(2, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature1 = new Signature("b61b");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature1,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(1));
+        Signature signature2 = new Signature("abcd");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature2,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testHandleMessage_InvalidRulesWith4F00() {
+        /**
+         * FF40 24
+         *   E2 22
+         *      E1 18
+         *         4F 00
+         *         C1 14 75C073AFD219AEB221948E828F066E778ADFDF23
+         *      E3 06
+         *         D0 01 01
+         *         D1 01 01
+         */
+        final String hexString = "FF4024E222E1184F00C11475C073AFD219AEB221948E828F066E778ADFDF23"
+                + "E306D00101D10101";
+
+        testHelper(hexString);
+
+        assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testHandleMessage_InvalidRulesWithoutDB() {
+        /**
+         * FF40 2A
+         *   E2 28
+         *      E1 1E
+         *         4F 06 FF FF FF FF FF FF
+         *         C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+         *      E3 06
+         *         D0 01 01
+         *         D1 01 01
+         */
+        final String hexString = "FF402AE228E11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED8493C313A"
+                + "137F89FA2765E306D00101D10101";
+
+        testHelper(hexString);
+
+        assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+}