Fix wifi disable during restore

The main looper needs to run freely for a moment after disabling
wifi in order for various signals (content observers, broadcast) to
propagate to all the listeners that need to take action for the
wifi stack to shut all the way down.  This patch breaks up the
disable-and-rewrite-config sequence of wifi AP restore in to two
distinct operations separated by a moment so as not to block those
necessary messages.

Bug 22979342

Change-Id: I271766cad0e454669a194652fb67f835bb022cd1
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index e74334b..b9a9c24 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -148,7 +148,10 @@
     private WifiManager mWfm;
     private static String mWifiConfigFile;
 
+    // Chain of asynchronous operations used when rewriting the wifi supplicant config file
+    WifiDisableRunnable mWifiDisable = null;
     WifiRestoreRunnable mWifiRestore = null;
+    int mRetainedWifiState; // used only during config file rewrite
 
     // Class for capturing a network definition from the wifi supplicant config file
     static class Network {
@@ -407,9 +410,47 @@
         writeNewChecksums(stateChecksums, newState);
     }
 
+    class WifiDisableRunnable implements Runnable {
+        final WifiRestoreRunnable mNextPhase;
+
+        public WifiDisableRunnable(WifiRestoreRunnable next) {
+            mNextPhase = next;
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG_BACKUP) {
+                Log.v(TAG, "Disabling wifi during restore");
+            }
+            final ContentResolver cr = getContentResolver();
+            final int scanAlways = Settings.Global.getInt(cr,
+                    Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
+            final int retainedWifiState = enableWifi(false);
+            if (scanAlways != 0) {
+                Settings.Global.putInt(cr,
+                        Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
+            }
+
+            // Tell the final stage how to clean up after itself
+            mNextPhase.setPriorState(retainedWifiState, scanAlways);
+
+            // And run it after a modest pause to give broadcasts and content
+            // observers time an opportunity to run on this looper thread, so
+            // that the wifi stack actually goes all the way down.
+            new Handler(getMainLooper()).postDelayed(mNextPhase, 2500);
+        }
+    }
+
     class WifiRestoreRunnable implements Runnable {
         private byte[] restoredSupplicantData;
         private byte[] restoredWifiConfigFile;
+        private int retainedWifiState;  // provided by disable stage
+        private int scanAlways; // provided by disable stage
+
+        void setPriorState(int retainedState, int always) {
+            retainedWifiState = retainedState;
+            scanAlways = always;
+        }
 
         void incorporateWifiSupplicant(BackupDataInput data) {
             restoredSupplicantData = new byte[data.getDataSize()];
@@ -437,20 +478,8 @@
         public void run() {
             if (restoredSupplicantData != null || restoredWifiConfigFile != null) {
                 if (DEBUG_BACKUP) {
-                    Log.v(TAG, "Starting deferred restore of wifi data");
+                    Log.v(TAG, "Applying restored wifi data");
                 }
-                final ContentResolver cr = getContentResolver();
-                final int scanAlways = Settings.Global.getInt(cr,
-                        Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
-                final int retainedWifiState = enableWifi(false);
-                if (scanAlways != 0) {
-                    Settings.Global.putInt(cr,
-                            Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0);
-                }
-                // !!! Give the wifi stack a moment to quiesce.  We've observed the
-                // response to disabling WIFI_SCAN_ALWAYS_AVAILABLE taking more
-                // than 1500ms, so we wait a generous 2500 here before proceeding.
-                try { Thread.sleep(2500); } catch (InterruptedException e) {}
                 if (restoredSupplicantData != null) {
                     restoreWifiSupplicant(FILE_WIFI_SUPPLICANT,
                             restoredSupplicantData, restoredSupplicantData.length);
@@ -465,7 +494,7 @@
                 }
                 // restore the previous WIFI state.
                 if (scanAlways != 0) {
-                    Settings.Global.putInt(cr,
+                    Settings.Global.putInt(getContentResolver(),
                             Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, scanAlways);
                 }
                 enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
@@ -479,6 +508,7 @@
     void initWifiRestoreIfNecessary() {
         if (mWifiRestore == null) {
             mWifiRestore = new WifiRestoreRunnable();
+            mWifiDisable = new WifiDisableRunnable(mWifiRestore);
         }
     }
 
@@ -518,13 +548,16 @@
         }
 
         // If we have wifi data to restore, post a runnable to perform the
-        // bounce-and-update operation a little ways in the future.
+        // bounce-and-update operation a little ways in the future.  The
+        // 'disable' runnable brings down the stack and remembers its state,
+        // and in turn schedules the 'restore' runnable to do the rewrite
+        // and cleanup operations.
         if (mWifiRestore != null) {
             long wifiBounceDelayMillis = Settings.Global.getLong(
                     getContentResolver(),
                     Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
                     WIFI_BOUNCE_DELAY_MILLIS);
-            new Handler(getMainLooper()).postDelayed(mWifiRestore, wifiBounceDelayMillis);
+            new Handler(getMainLooper()).postDelayed(mWifiDisable, wifiBounceDelayMillis);
         }
     }