Fix power issues around wifi scans.

- Sending a broadcast indicating when scan requests could be serviced so that
apps don't request scans we won't do anything with.
- Fix our batt stats accounting so we only count it if we send the request to
the driver.

bug: 8868201
bug: 9496690
Change-Id: I64a4f1c294c848ac64c50d8854ed4a6a1a47f603
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 619a10b..9624f15 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -156,6 +156,7 @@
     <protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
     <protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
     <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
     <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index 66ef978..a70978e 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -114,10 +114,6 @@
     /* Tracks the persisted states for wi-fi & airplane mode */
     final WifiSettingsStore mSettingsStore;
 
-    /* The work source (UID) that triggered the current WIFI scan, synchronized
-     * on this */
-    private WorkSource mScanWorkSource;
-
     /**
      * Asynchronous channel to WifiStateMachine
      */
@@ -255,17 +251,6 @@
                 },
                 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
 
-        mContext.registerReceiver(
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        if (intent.getAction().equals(
-                                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
-                            noteScanEnd();
-                        }
-                    }
-                }, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
-
         // Adding optimizations of only receiving broadcasts when wifi is enabled
         // can result in race conditions when apps toggle wifi in the background
         // without active user involvement. Always receive broadcasts.
@@ -274,44 +259,6 @@
 
     private WifiController mWifiController;
 
-    /** Tell battery stats about a new WIFI scan */
-    private void noteScanStart() {
-        WorkSource scanWorkSource = null;
-        synchronized (WifiService.this) {
-            if (mScanWorkSource != null) {
-                // Scan already in progress, don't add this one to battery stats
-                return;
-            }
-            scanWorkSource = new WorkSource(Binder.getCallingUid());
-            mScanWorkSource = scanWorkSource;
-        }
-
-        long id = Binder.clearCallingIdentity();
-        try {
-            mBatteryStats.noteWifiScanStartedFromSource(scanWorkSource);
-        } catch (RemoteException e) {
-            Log.w(TAG, e);
-        } finally {
-            Binder.restoreCallingIdentity(id);
-        }
-    }
-
-    /** Tell battery stats that the current WIFI scan has completed */
-    private void noteScanEnd() {
-        WorkSource scanWorkSource = null;
-        synchronized (WifiService.this) {
-            scanWorkSource = mScanWorkSource;
-            mScanWorkSource = null;
-        }
-        if (scanWorkSource != null) {
-            try {
-                mBatteryStats.noteWifiScanStoppedFromSource(scanWorkSource);
-            } catch (RemoteException e) {
-                Log.w(TAG, e);
-            }
-        }
-    }
-
     /**
      * Check if Wi-Fi needs to be enabled and start
      * if needed
@@ -352,8 +299,7 @@
      */
     public void startScan() {
         enforceChangePermission();
-        mWifiStateMachine.startScan();
-        noteScanStart();
+        mWifiStateMachine.startScan(Binder.getCallingUid());
     }
 
     private void enforceAccessPermission() {
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ec76a8b..a3c172a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -70,6 +70,18 @@
     public static final int ERROR_AUTHENTICATING = 1;
 
     /**
+     * Broadcast intent action indicating whether Wi-Fi scanning is allowed currently
+     * @hide
+     */
+    public static final String WIFI_SCAN_AVAILABLE = "wifi_scan_available";
+
+    /**
+     * Extra int indicating scan availability, WIFI_STATE_ENABLED and WIFI_STATE_DISABLED
+     * @hide
+     */
+     public static final String EXTRA_SCAN_AVAILABLE = "scan_enabled";
+
+    /**
      * Broadcast intent action indicating that Wi-Fi has been enabled, disabled,
      * enabling, disabling, or unknown. One extra provides this state as an int.
      * Another extra provides the previous state, if available.
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 32d48ac..58ae8dc 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -141,6 +141,8 @@
     */
     private int mOperationalMode = CONNECT_MODE;
     private boolean mScanResultIsPending = false;
+    private WorkSource mScanWorkSource = null;
+    private static final int UNKNOWN_SCAN_SOURCE = -1;
     /* Tracks if state machine has received any screen state change broadcast yet.
      * We can miss one of these at boot.
      */
@@ -601,7 +603,7 @@
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
-                        startScan();
+                        startScan(UNKNOWN_SCAN_SOURCE);
                     }
                 },
                 new IntentFilter(ACTION_START_SCAN));
@@ -694,6 +696,11 @@
 
         //start the state machine
         start();
+
+        final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
+        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
     /*********************************************************
@@ -716,8 +723,31 @@
     /**
      * TODO: doc
      */
-    public void startScan() {
-        sendMessage(CMD_START_SCAN);
+    public void startScan(int callingUid) {
+        sendMessage(CMD_START_SCAN, callingUid);
+    }
+
+    private void noteScanStart(int callingUid) {
+        if (mScanWorkSource == null && callingUid != UNKNOWN_SCAN_SOURCE) {
+            mScanWorkSource = new WorkSource(callingUid);
+            try {
+                mBatteryStats.noteWifiScanStartedFromSource(mScanWorkSource);
+            } catch (RemoteException e) {
+                log(e.toString());
+            }
+        }
+    }
+
+    private void noteScanEnd() {
+        if (mScanWorkSource != null) {
+            try {
+                mBatteryStats.noteWifiScanStoppedFromSource(mScanWorkSource);
+            } catch (RemoteException e) {
+                log(e.toString());
+            } finally {
+                mScanWorkSource = null;
+            }
+        }
     }
 
     private void startScanNative(int type) {
@@ -1544,6 +1574,7 @@
     }
 
     private void sendScanResultsAvailableBroadcast() {
+        noteScanEnd();
         Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
         mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
@@ -1888,6 +1919,7 @@
                     }
                     break;
                     /* Discard */
+                case CMD_START_SCAN:
                 case CMD_START_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT:
                 case CMD_STOP_SUPPLICANT_FAILED:
@@ -1902,7 +1934,6 @@
                 case CMD_STOP_AP:
                 case CMD_TETHER_STATE_CHANGE:
                 case CMD_TETHER_NOTIFICATION_TIMED_OUT:
-                case CMD_START_SCAN:
                 case CMD_DISCONNECT:
                 case CMD_RECONNECT:
                 case CMD_REASSOCIATE:
@@ -2405,11 +2436,18 @@
             mWifiNative.setPowerSave(true);
 
             if (mP2pSupported) mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);
+
+            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
         }
+
         @Override
         public boolean processMessage(Message message) {
             switch(message.what) {
                 case CMD_START_SCAN:
+                    noteScanStart(message.arg1);
                     startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
                     break;
                 case CMD_SET_COUNTRY_CODE:
@@ -2530,6 +2568,12 @@
             mIsRunning = false;
             updateBatteryWorkSource(null);
             mScanResults = new ArrayList<ScanResult>();
+
+            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);
+            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+            noteScanEnd(); // wrap up any pending request.
         }
     }
 
@@ -2680,6 +2724,7 @@
                 // Handle scan. All the connection related commands are
                 // handled only in ConnectModeState
                 case CMD_START_SCAN:
+                    noteScanStart(message.arg1);
                     startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
                     break;
                 default:
@@ -2943,6 +2988,7 @@
                     break;
                 case CMD_START_SCAN:
                     /* Do not attempt to connect when we are already connected */
+                    noteScanStart(message.arg1);
                     startScanNative(WifiNative.SCAN_WITHOUT_CONNECTION_SETUP);
                     break;
                     /* Ignore connection to same network */
@@ -3260,7 +3306,7 @@
                     if (mP2pConnected.get()) break;
                     if (message.arg1 == mPeriodicScanToken &&
                             mWifiConfigStore.getConfiguredNetworks().size() == 0) {
-                        sendMessage(CMD_START_SCAN);
+                        sendMessage(CMD_START_SCAN, UNKNOWN_SCAN_SOURCE);
                         sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
                                     ++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
                     }