DO NOT MERGE WifiLock extensions for high performance mode

Add extension to WifiLock to allow apps to operate
in high performance mode (high power & disable suspend
optimizations for battery consumption).

Bug: 2834260
Change-Id: I8b33d307f3d569bc92ba2139b9ed224ffc147547
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index 7392442..903283e 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -493,6 +493,15 @@
     return doBooleanCommand("BLACKLIST clear", "OK");
 }
 
+static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject clazz, jboolean enabled)
+{
+    char cmdstr[25];
+
+    snprintf(cmdstr, sizeof(cmdstr), "DRIVER SETSUSPEND %d", enabled ? 0 : 1);
+    return doBooleanCommand(cmdstr, "OK");
+}
+
+
 static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info)
 {
     jint ipaddr, gateway, mask, dns1, dns2, server, lease;
@@ -571,6 +580,7 @@
     { "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand },
     { "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand },
     { "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand },
+    { "setSuspendOptimizationsCommand", "(Z)Z", (void*) android_net_wifi_setSuspendOptimizationsCommand},
 
     { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError },
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 509c789..af4d7e4 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -116,6 +116,8 @@
 
     private final LockList mLocks = new LockList();
     // some wifi lock statistics
+    private int mFullHighPerfLocksAcquired;
+    private int mFullHighPerfLocksReleased;
     private int mFullLocksAcquired;
     private int mFullLocksReleased;
     private int mScanLocksAcquired;
@@ -1782,8 +1784,8 @@
         msg.sendToTarget();
     }
 
-    private void sendStartMessage(boolean scanOnlyMode) {
-        Message.obtain(mWifiHandler, MESSAGE_START_WIFI, scanOnlyMode ? 1 : 0, 0).sendToTarget();
+    private void sendStartMessage(int lockMode) {
+        Message.obtain(mWifiHandler, MESSAGE_START_WIFI, lockMode, 0).sendToTarget();
     }
 
     private void sendAccessPointMessage(boolean enable, WifiConfiguration wifiConfig, int uid) {
@@ -1801,12 +1803,15 @@
         boolean wifiEnabled = getPersistedWifiEnabled();
         boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
         boolean lockHeld = mLocks.hasLocks();
-        int strongestLockMode;
+        int strongestLockMode = WifiManager.WIFI_MODE_FULL;
         boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
         boolean wifiShouldBeStarted = !mDeviceIdle || lockHeld;
-        if (mDeviceIdle && lockHeld) {
+
+        if (lockHeld) {
             strongestLockMode = mLocks.getStrongestLockMode();
-        } else {
+        }
+        /* If device is not idle, lockmode cannot be scan only */
+        if (!mDeviceIdle && strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY) {
             strongestLockMode = WifiManager.WIFI_MODE_FULL;
         }
 
@@ -1827,7 +1832,7 @@
                     sWakeLock.acquire();
                     sendEnableMessage(true, false, mLastEnableUid);
                     sWakeLock.acquire();
-                    sendStartMessage(strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY);
+                    sendStartMessage(strongestLockMode);
                 } else if (!mWifiStateTracker.isDriverStopped()) {
                     int wakeLockTimeout =
                             Settings.Secure.getInt(
@@ -1905,8 +1910,10 @@
                     break;
 
                 case MESSAGE_START_WIFI:
-                    mWifiStateTracker.setScanOnlyMode(msg.arg1 != 0);
+                    mWifiStateTracker.setScanOnlyMode(msg.arg1 == WifiManager.WIFI_MODE_SCAN_ONLY);
                     mWifiStateTracker.restart();
+                    mWifiStateTracker.setHighPerfMode(msg.arg1 ==
+                            WifiManager.WIFI_MODE_FULL_HIGH_PERF);
                     sWakeLock.release();
                     break;
 
@@ -1986,8 +1993,10 @@
         }
         pw.println();
         pw.println("Locks acquired: " + mFullLocksAcquired + " full, " +
+                mFullHighPerfLocksAcquired + " full high perf, " +
                 mScanLocksAcquired + " scan");
         pw.println("Locks released: " + mFullLocksReleased + " full, " +
+                mFullHighPerfLocksReleased + " full high perf, " +
                 mScanLocksReleased + " scan");
         pw.println();
         pw.println("Locks held:");
@@ -2042,11 +2051,15 @@
             if (mList.isEmpty()) {
                 return WifiManager.WIFI_MODE_FULL;
             }
-            for (WifiLock l : mList) {
-                if (l.mMode == WifiManager.WIFI_MODE_FULL) {
-                    return WifiManager.WIFI_MODE_FULL;
-                }
+
+            if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
+                return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
             }
+
+            if (mFullLocksAcquired > mFullLocksReleased) {
+                return WifiManager.WIFI_MODE_FULL;
+            }
+
             return WifiManager.WIFI_MODE_SCAN_ONLY;
         }
 
@@ -2085,7 +2098,11 @@
 
     public boolean acquireWifiLock(IBinder binder, int lockMode, String tag) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
-        if (lockMode != WifiManager.WIFI_MODE_FULL && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY) {
+        if (lockMode != WifiManager.WIFI_MODE_FULL &&
+                lockMode != WifiManager.WIFI_MODE_SCAN_ONLY &&
+                lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
+            Slog.e(TAG, "Illegal argument, lockMode= " + lockMode);
+            if (DBG) throw new IllegalArgumentException("lockMode=" + lockMode);
             return false;
         }
         WifiLock wifiLock = new WifiLock(lockMode, tag, binder);
@@ -2107,6 +2124,12 @@
                 ++mFullLocksAcquired;
                 mBatteryStats.noteFullWifiLockAcquired(uid);
                 break;
+            case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+                ++mFullHighPerfLocksAcquired;
+                /* Treat high power as a full lock for battery stats */
+                mBatteryStats.noteFullWifiLockAcquired(uid);
+                break;
+
             case WifiManager.WIFI_MODE_SCAN_ONLY:
                 ++mScanLocksAcquired;
                 mBatteryStats.noteScanWifiLockAcquired(uid);
@@ -2146,6 +2169,10 @@
                         ++mFullLocksReleased;
                         mBatteryStats.noteFullWifiLockReleased(uid);
                         break;
+                    case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
+                        ++mFullHighPerfLocksReleased;
+                        mBatteryStats.noteFullWifiLockReleased(uid);
+                        break;
                     case WifiManager.WIFI_MODE_SCAN_ONLY:
                         ++mScanLocksReleased;
                         mBatteryStats.noteScanWifiLockReleased(uid);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 4a22b68..3b41dc1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -307,6 +307,16 @@
     public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
 
     /**
+     * In this Wi-Fi lock mode, Wi-Fi will behave as in the mode
+     * {@link #WIFI_MODE_FULL} but it operates at high performance
+     * at the expense of power. This mode should be used
+     * only when the wifi connection needs to have minimum loss and low
+     * latency as it can impact the battery life.
+     * @hide
+     */
+    public static final int WIFI_MODE_FULL_HIGH_PERF = 3;
+
+    /**
      * In this Wi-Fi lock mode, Wi-Fi will be kept active,
      * and will behave normally, i.e., it will attempt to automatically
      * establish a connection to a remembered access point that is
@@ -993,8 +1003,9 @@
     /**
      * Creates a new WifiLock.
      *
-     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL} and
-     * {@link #WIFI_MODE_SCAN_ONLY} for descriptions of the types of Wi-Fi locks.
+     * @param lockType the type of lock to create. See {@link #WIFI_MODE_FULL},
+     * {@link #WIFI_MODE_SCAN_ONLY} and {@link #WIFI_MODE_FULL_HIGH_PERF} for descriptions
+     * of the types of Wi-Fi locks.
      * @param tag a tag for the WifiLock to identify it in debugging messages.  This string is 
      *            never shown to the user under normal conditions, but should be descriptive 
      *            enough to identify your application and the specific WifiLock within it, if it
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 7a3282c..25f05c0 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -149,6 +149,8 @@
 
     public native static String getDhcpError();
 
+    public native static boolean setSuspendOptimizationsCommand(boolean enabled);
+
     /**
      * Wait for the supplicant to send an event, returning the event string.
      * @return the event string sent by the supplicant.
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index f57df62..0cc1f46 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -276,6 +276,9 @@
     
     private boolean mIsScanModeActive;
     private boolean mEnableRssiPolling;
+    private boolean mIsHighPerfEnabled;
+    private int mPowerModeRefCount = 0;
+    private int mOptimizationsDisabledRefCount = 0;
 
     /**
      * One of  {@link WifiManager#WIFI_STATE_DISABLED},
@@ -659,6 +662,67 @@
         }
     }
 
+    /**
+     * Set suspend mode optimizations. These include:
+     * - packet filtering
+     * - turn off roaming
+     * - DTIM settings
+     *
+     * Uses reference counting to keep the suspend optimizations disabled
+     * as long as one entity wants optimizations disabled.
+     *
+     * For example, WifiLock can keep suspend optimizations disabled
+     * or the user setting (wifi never sleeps) can keep suspend optimizations
+     * disabled. As long as one entity wants it disabled, it should stay
+     * that way
+     *
+     * @param enabled true if optimizations need enabled, false otherwise
+     */
+    public synchronized void setSuspendModeOptimizations(boolean enabled) {
+
+        /* It is good to plumb suspend optimization enable
+         * or disable even if ref count indicates already done
+         * since we could have a case of previous failure.
+         */
+        if (!enabled) {
+            mOptimizationsDisabledRefCount++;
+        } else {
+            mOptimizationsDisabledRefCount--;
+            if (mOptimizationsDisabledRefCount > 0) {
+                return;
+            } else {
+                /* Keep refcount from becoming negative */
+                mOptimizationsDisabledRefCount = 0;
+            }
+        }
+
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
+            return;
+        }
+
+        WifiNative.setSuspendOptimizationsCommand(enabled);
+    }
+
+
+    /**
+     * Set high performance mode of operation. This would mean
+     * use active power mode and disable suspend optimizations
+     * @param enabled true if enabled, false otherwise
+     */
+    public synchronized void setHighPerfMode(boolean enabled) {
+        if (mIsHighPerfEnabled != enabled) {
+            if (enabled) {
+                setPowerMode(DRIVER_POWER_MODE_ACTIVE);
+                setSuspendModeOptimizations(false);
+            } else {
+                setPowerMode(DRIVER_POWER_MODE_AUTO);
+                setSuspendModeOptimizations(true);
+            }
+            mIsHighPerfEnabled = enabled;
+            Log.d(TAG,"high performance mode: " + enabled);
+        }
+    }
+
 
     private void checkIsBluetoothPlaying() {
         boolean isBluetoothPlaying = false;
@@ -744,6 +808,9 @@
                 dhcpThread.start();
                 mDhcpTarget = new DhcpHandler(dhcpThread.getLooper(), this);
                 mIsScanModeActive = true;
+                mIsHighPerfEnabled = false;
+                mOptimizationsDisabledRefCount = 0;
+                mPowerModeRefCount = 0;
                 mTornDownByConnMgr = false;
                 mLastBssid = null;
                 mLastSsid = null;
@@ -1947,13 +2014,41 @@
      * @param mode
      *     DRIVER_POWER_MODE_AUTO
      *     DRIVER_POWER_MODE_ACTIVE
-     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     *
+     * Uses reference counting to keep power mode active
+     * as long as one entity wants power mode to be active.
+     *
+     * For example, WifiLock high perf mode can keep power mode active
+     * or a DHCP session can keep it active. As long as one entity wants
+     * it enabled, it should stay that way
+     *
      */
-    public synchronized boolean setPowerMode(int mode) {
-        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
-            return false;
+    private synchronized void setPowerMode(int mode) {
+
+        /* It is good to plumb power mode change
+         * even if ref count indicates already done
+         * since we could have a case of previous failure.
+         */
+        switch(mode) {
+            case DRIVER_POWER_MODE_ACTIVE:
+                mPowerModeRefCount++;
+                break;
+            case DRIVER_POWER_MODE_AUTO:
+                mPowerModeRefCount--;
+                if (mPowerModeRefCount > 0) {
+                    return;
+                } else {
+                    /* Keep refcount from becoming negative */
+                    mPowerModeRefCount = 0;
+                }
+                break;
         }
-        return WifiNative.setPowerModeCommand(mode);
+
+        if (mWifiState.get() != WIFI_STATE_ENABLED || isDriverStopped()) {
+            return;
+        }
+
+        WifiNative.setPowerModeCommand(mode);
     }
 
     /**