Make action provision protected with extra phone ID

Previously, the phone ID was appended to the broadcast and sent to
ConnectivityManager. Instead of sending both as an action, send the
phone ID as an extra instead. Only process the broadcast if the extra
matches the phone ID for DCT. Move the action to a protected broadcast
to prevent security issues.

Test: manually verify a SecurityException when action provision is sent
Test: atest DcTrackerTest
Bug: 172459128
Change-Id: If822e874e58e2f5378a5cfdc4619e7b1881b6388
Merged-In: If822e874e58e2f5378a5cfdc4619e7b1881b6388
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index a8dcbb7..b56ee9d 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -289,6 +289,16 @@
     /* Indicating data service is bound or not */
     private boolean mDataServiceBound = false;
 
+    /* Intent to hide/show the sign-in error notification for provisioning */
+    private static final String INTENT_PROVISION = "com.android.internal.telephony.PROVISION";
+
+    /**
+     * Extra containing the phone ID for INTENT_PROVISION
+     * Must be kept consistent with NetworkNotificationManager#setProvNotificationVisible.
+     * TODO: refactor the deprecated API to prevent hardcoding values.
+     */
+    private static final String EXTRA_PROVISION_PHONE_ID = "provision.phone.id";
+
     /* Intent for the provisioning apn alarm */
     private static final String INTENT_PROVISIONING_APN_ALARM =
             "com.android.internal.telephony.provisioning_apn_alarm";
@@ -647,7 +657,6 @@
     /** Watches for changes to the APN db. */
     private ApnChangeObserver mApnObserver;
 
-    private final String mProvisionActionName;
     private BroadcastReceiver mProvisionBroadcastReceiver;
     private ProgressDialog mProvisioningSpinner;
 
@@ -729,8 +738,6 @@
         initEmergencyApnSetting();
         addEmergencyApnSetting();
 
-        mProvisionActionName = "com.android.internal.telephony.PROVISION" + phone.getPhoneId();
-
         mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
         registerSettingsObserver();
     }
@@ -743,7 +750,6 @@
         mAlarmManager = null;
         mPhone = null;
         mDataConnectionTracker = null;
-        mProvisionActionName = null;
         mSettingsObserver = new SettingsObserver(null, this);
         mDataEnabledSettings = null;
         mTransportType = 0;
@@ -950,6 +956,10 @@
 
         @Override
         public void onReceive(Context context, Intent intent) {
+            if (mPhone.getPhoneId() != intent.getIntExtra(EXTRA_PROVISION_PHONE_ID,
+                    SubscriptionManager.INVALID_PHONE_INDEX)) {
+                return;
+            }
             // Turning back on the radio can take time on the order of a minute, so show user a
             // spinner so they know something is going on.
             log("onReceive : ProvisionNotificationBroadcastReceiver");
@@ -2967,7 +2977,7 @@
                 if ((!isProvApn) || mIsProvisioning) {
                     // Hide any provisioning notification.
                     cm.setProvisioningNotificationVisible(false, ConnectivityManager.TYPE_MOBILE,
-                            mProvisionActionName);
+                            INTENT_PROVISION + ":" + mPhone.getPhoneId());
                     // Complete the connection normally notifying the world we're connected.
                     // We do this if this isn't a special provisioning apn or if we've been
                     // told its time to provision.
@@ -2990,10 +3000,10 @@
                             cm.getMobileProvisioningUrl(),
                             mTelephonyManager.getNetworkOperatorName());
                     mPhone.getContext().registerReceiver(mProvisionBroadcastReceiver,
-                            new IntentFilter(mProvisionActionName));
+                            new IntentFilter(INTENT_PROVISION));
                     // Put up user notification that sign-in is required.
                     cm.setProvisioningNotificationVisible(true, ConnectivityManager.TYPE_MOBILE,
-                            mProvisionActionName);
+                            INTENT_PROVISION + ":" + mPhone.getPhoneId());
                     // Turn off radio to save battery and avoid wasting carrier resources.
                     // The network isn't usable and network validation will just fail anyhow.
                     setRadio(false);
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 7c2df59..9c7b350 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -2215,4 +2215,16 @@
                 .filter(x -> x.getApnType().equals(type))
                 .findFirst().get().getPriority());
     }
+
+    @Test
+    public void testProvisionBroadcastReceiver() {
+        Intent intent = new Intent("com.android.internal.telephony.PROVISION");
+        intent.putExtra("provision.phone.id", mPhone.getPhoneId());
+        try {
+            mContext.sendBroadcast(intent);
+        } catch (SecurityException e) {
+            fail();
+        }
+        waitForLastHandlerAction(mDcTrackerTestHandler.getThreadHandler());
+    }
 }