Adjust captive portal test for setup wizard

During setup wizard, perform captive portal test without the typical
delays.

Change-Id: If596948e732966817aae6201440e87e19be0c2f8
diff --git a/core/java/android/net/CaptivePortalTracker.java b/core/java/android/net/CaptivePortalTracker.java
index 354a8c4..21995c0 100644
--- a/core/java/android/net/CaptivePortalTracker.java
+++ b/core/java/android/net/CaptivePortalTracker.java
@@ -25,14 +25,15 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
+import android.os.Handler;
 import android.os.UserHandle;
 import android.os.Message;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
-import android.util.Log;
 
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
@@ -81,15 +82,21 @@
     private State mActiveNetworkState = new ActiveNetworkState();
     private State mDelayedCaptiveCheckState = new DelayedCaptiveCheckState();
 
+    private static final String SETUP_WIZARD_PACKAGE = "com.google.android.setupwizard";
+    private boolean mDeviceProvisioned = false;
+    private ProvisioningObserver mProvisioningObserver;
+
     private CaptivePortalTracker(Context context, IConnectivityManager cs) {
         super(TAG);
 
         mContext = context;
         mConnService = cs;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        mProvisioningObserver = new ProvisioningObserver();
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
         mContext.registerReceiver(mReceiver, filter);
 
         mServer = Settings.Global.getString(mContext.getContentResolver(),
@@ -106,11 +113,31 @@
         setInitialState(mNoActiveNetworkState);
     }
 
+    private class ProvisioningObserver extends ContentObserver {
+        ProvisioningObserver() {
+            super(new Handler());
+            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.DEVICE_PROVISIONED), false, this);
+            onChange(false); // load initial value
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            mDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+        }
+    }
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+            // Normally, we respond to CONNECTIVITY_ACTION, allowing time for the change in
+            // connectivity to stabilize, but if the device is not yet provisioned, respond
+            // immediately to speed up transit through the setup wizard.
+            if ((mDeviceProvisioned && action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
+                    || (!mDeviceProvisioned
+                            && action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE))) {
                 NetworkInfo info = intent.getParcelableExtra(
                         ConnectivityManager.EXTRA_NETWORK_INFO);
                 sendMessage(obtainMessage(CMD_CONNECTIVITY_CHANGE, info));
@@ -222,8 +249,12 @@
         @Override
         public void enter() {
             if (DBG) log(getName() + "\n");
-            sendMessageDelayed(obtainMessage(CMD_DELAYED_CAPTIVE_CHECK,
-                        ++mDelayedCheckToken, 0), DELAYED_CHECK_INTERVAL_MS);
+            Message message = obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, ++mDelayedCheckToken, 0);
+            if (mDeviceProvisioned) {
+                sendMessageDelayed(message, DELAYED_CHECK_INTERVAL_MS);
+            } else {
+                sendMessage(message);
+            }
         }
 
         @Override
@@ -233,13 +264,26 @@
                 case CMD_DELAYED_CAPTIVE_CHECK:
                     if (message.arg1 == mDelayedCheckToken) {
                         InetAddress server = lookupHost(mServer);
-                        if (server != null) {
-                            if (isCaptivePortal(server)) {
-                                if (DBG) log("Captive network " + mNetworkInfo);
+                        boolean captive = server != null && isCaptivePortal(server);
+                        if (captive) {
+                            if (DBG) log("Captive network " + mNetworkInfo);
+                        } else {
+                            if (DBG) log("Not captive network " + mNetworkInfo);
+                        }
+                        if (mDeviceProvisioned) {
+                            if (captive) {
+                                // Setup Wizard will assist the user in connecting to a captive
+                                // portal, so make the notification visible unless during setup
                                 setNotificationVisible(true);
                             }
+                        } else {
+                            Intent intent = new Intent(
+                                    ConnectivityManager.ACTION_CAPTIVE_PORTAL_TEST_COMPLETED);
+                            intent.putExtra(ConnectivityManager.EXTRA_IS_CAPTIVE_PORTAL, captive);
+                            intent.setPackage(SETUP_WIZARD_PACKAGE);
+                            mContext.sendBroadcast(intent);
                         }
-                        if (DBG) log("Not captive network " + mNetworkInfo);
+
                         transitionTo(mActiveNetworkState);
                     }
                     break;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index a8a68d0..000c56c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -228,6 +228,21 @@
     public static final String EXTRA_ERRORED_TETHER = "erroredArray";
 
     /**
+     * Broadcast Action: The captive portal tracker has finished its test.
+     * Sent only while running Setup Wizard, in lieu of showing a user
+     * notification.
+     * @hide
+     */
+    public static final String ACTION_CAPTIVE_PORTAL_TEST_COMPLETED =
+            "android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED";
+    /**
+     * The lookup key for a boolean that indicates whether a captive portal was detected.
+     * Retrieve it with {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+     * @hide
+     */
+    public static final String EXTRA_IS_CAPTIVE_PORTAL = "captivePortal";
+
+    /**
      * The absence of APN..
      * @hide
      */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5d0614c..d77b504 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -129,6 +129,7 @@
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
     <protected-broadcast android:name="android.net.conn.DATA_ACTIVITY_CHANGE" />
+    <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED" />
 
     <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
     <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_ON_DETECTED" />
@@ -641,7 +642,7 @@
         android:protectionLevel="normal"
         android:description="@string/permdesc_accessWifiState"
         android:label="@string/permlab_accessWifiState" />
-        
+
     <!-- Allows applications to change Wi-Fi connectivity state -->
     <permission android:name="android.permission.CHANGE_WIFI_STATE"
         android:permissionGroup="android.permission-group.NETWORK"
@@ -681,14 +682,14 @@
         android:protectionLevel="dangerous"
         android:description="@string/permdesc_bluetooth"
         android:label="@string/permlab_bluetooth" />
-        
+
     <!-- Allows applications to discover and pair bluetooth devices -->
     <permission android:name="android.permission.BLUETOOTH_ADMIN"
         android:permissionGroup="android.permission-group.BLUETOOTH_NETWORK"
         android:protectionLevel="dangerous"
         android:description="@string/permdesc_bluetoothAdmin"
         android:label="@string/permlab_bluetoothAdmin" />
-   
+
     <!-- Allows bluetooth stack to access files
          @hide This should only be used by Bluetooth apk.
     -->
@@ -719,7 +720,7 @@
     <permission android:name="android.permission.LOOP_RADIO"
 	android:permissionGroup="android.permission-group.NETWORK"
 	android:protectionLevel="signature|system" />
-    
+
     <!-- ================================== -->
     <!-- Permissions for accessing accounts -->
     <!-- ================================== -->
@@ -1129,7 +1130,7 @@
         android:protectionLevel="signature|system"
         android:label="@string/permlab_manageUsers"
         android:description="@string/permdesc_manageUsers" />
-    
+
     <!-- Allows an application to get full detailed information about
          recently running tasks, with full fidelity to the real state.
          @hide -->
@@ -1671,7 +1672,7 @@
         android:label="@string/permlab_freezeScreen"
         android:description="@string/permdesc_freezeScreen"
         android:protectionLevel="signature" />
-    
+
     <!-- Allows an application to inject user events (keys, touch, trackball)
          into the event stream and deliver them to ANY window.  Without this
          permission, you can only deliver events to windows in your own process.