Move mobile provisioning APN support into telephony and rework.
Paul Jensen [Wed, 17 Sep 2014 17:49:13 +0000 (13:49 -0400)]
This code had to be reworked to cooperate with the new multi-network
logic.  The provisioning flow when using a provisioning APN is:
1. DcTracker detects connecting to provisioning APN, puts up
   notification, and turns off radio (as per b/13190133, b/10328264).
2. User selects notification. ProvisionNotificationBroadcastReceiver
   receives intent, turns on radio, enables fail-fast mode,
   sets up provisioning logic in DcTrackerBase.
3. When radio connects again, DcTracker.completeConnection() launches
   browser to facilitate activation.  Note that activation can be
   performed via any internet connection so it's not a problem that
   browser isn't multi-network aware.

bug:17324098
Change-Id: Ibd1c42b1a75795f90a6483d3d0a5a14f88b193d8

src/java/com/android/internal/telephony/dataconnection/DcTracker.java

index 64e813e..7c77b8b 100644 (file)
@@ -19,6 +19,7 @@ package com.android.internal.telephony.dataconnection;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -38,11 +39,13 @@ import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RegistrantList;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -63,6 +66,7 @@ import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.gsm.GSMPhone;
 import com.android.internal.telephony.PhoneConstants;
@@ -134,6 +138,9 @@ public final class DcTracker extends DcTrackerBase {
     /** Watches for changes to the APN db. */
     private ApnChangeObserver mApnObserver;
 
+    private final String mProvisionActionName;
+    private BroadcastReceiver mProvisionBroadcastReceiver;
+
     /** Used to send us NetworkRequests from ConnectivityService.  Remeber it so we can
      * unregister on dispose. */
     private Messenger mNetworkFactoryMessenger;
@@ -193,6 +200,8 @@ public final class DcTracker extends DcTrackerBase {
         // Add Emergency APN to APN setting list by default to support EPDN in sim absent cases
         initEmergencyApnSetting();
         addEmergencyApnSetting();
+
+        mProvisionActionName = "com.android.internal.telephony.PROVISION" + p.getPhoneId();
     }
 
     protected void registerForAllEvents() {
@@ -226,6 +235,11 @@ public final class DcTracker extends DcTrackerBase {
     public void dispose() {
         if (DBG) log("GsmDCT.dispose");
 
+        if (mProvisionBroadcastReceiver != null) {
+            mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
+            mProvisionBroadcastReceiver = null;
+        }
+
         ConnectivityManager cm = (ConnectivityManager)mPhone.getContext().getSystemService(
                 Context.CONNECTIVITY_SERVICE);
         cm.unregisterNetworkFactory(mNetworkFactoryMessenger);
@@ -366,6 +380,48 @@ public final class DcTracker extends DcTrackerBase {
         return apnContext;
     }
 
+    // Turn telephony radio on or off.
+    private void setRadio(boolean on) {
+        final ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+        try {
+            phone.setRadio(on);
+        } catch (Exception e) {
+            // Ignore.
+        }
+    }
+
+    // Class to handle Intent dispatched with user selects the "Sign-in to network"
+    // notification.
+    private class ProvisionNotificationBroadcastReceiver extends BroadcastReceiver {
+        // Mobile provisioning URL.  Valid while provisioning notification is up.
+        // Set prior to notification being posted as URL contains ICCID which
+        // disappears when radio is off (which is the case when notification is up).
+        private final String mProvisionUrl;
+
+        public ProvisionNotificationBroadcastReceiver(String provisionUrl) {
+            mProvisionUrl = provisionUrl;
+        }
+
+        private void setEnableFailFastMobileData(int enabled) {
+            sendMessage(obtainMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled));
+        }
+
+        private void enableMobileProvisioning() {
+            final Message msg = obtainMessage(DctConstants.CMD_ENABLE_MOBILE_PROVISIONING);
+            msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, mProvisionUrl));
+            sendMessage(msg);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // This code is almost identical to the old
+            // ConnectivityService.handleMobileProvisioningAction code.
+            setRadio(true);
+            setEnableFailFastMobileData(DctConstants.ENABLED);
+            enableMobileProvisioning();
+        }
+    }
+
     @Override
     public boolean isApnTypeActive(String type) {
         ApnContext apnContext = mApnContexts.get(type);
@@ -1807,7 +1863,15 @@ public final class DcTracker extends DcTrackerBase {
                 // A connection is setup
                 apnContext.setState(DctConstants.State.CONNECTED);
                 boolean isProvApn = apnContext.isProvisioningApn();
+                final ConnectivityManager cm = ConnectivityManager.from(mPhone.getContext());
+                if (mProvisionBroadcastReceiver != null) {
+                    mPhone.getContext().unregisterReceiver(mProvisionBroadcastReceiver);
+                    mProvisionBroadcastReceiver = null;
+                }
                 if ((!isProvApn) || mIsProvisioning) {
+                    // Hide any provisioning notification.
+                    cm.setProvisioningNotificationVisible(false, ConnectivityManager.TYPE_MOBILE,
+                            mProvisionActionName);
                     // 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.
@@ -1824,6 +1888,19 @@ public final class DcTracker extends DcTrackerBase {
                                 + " && (isProvisioningApn:" + isProvApn + " == true");
                     }
 
+                    // While radio is up, grab provisioning URL.  The URL contains ICCID which
+                    // disappears when radio is off.
+                    mProvisionBroadcastReceiver = new ProvisionNotificationBroadcastReceiver(
+                            cm.getMobileProvisioningUrl());
+                    mPhone.getContext().registerReceiver(mProvisionBroadcastReceiver,
+                            new IntentFilter(mProvisionActionName));
+                    // Put up user notification that sign-in is required.
+                    cm.setProvisioningNotificationVisible(true, ConnectivityManager.TYPE_MOBILE,
+                            mProvisionActionName);
+                    // 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);
+
                     Intent intent = new Intent(
                             TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN);
                     intent.putExtra(PhoneConstants.DATA_APN_KEY, apnContext.getApnSetting().apn);