Merge "WifiManager: Return empty scan list on remote exception" into mm-wireless-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index ca92c45..fc9ae9b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -21106,6 +21106,7 @@
     method public boolean reconnect();
     method public boolean removeNetwork(int);
     method public boolean saveConfiguration();
+    method public boolean setMetered(int, boolean);
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
     method public boolean setWifiEnabled(boolean);
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 06b6ee7..4fe97c1 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -61,7 +61,6 @@
  * @hide
  */
 public class IpManager extends StateMachine {
-    private static final String TAG = IpManager.class.getSimpleName();
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
@@ -92,13 +91,18 @@
          */
 
         // Implementations must call IpManager#completedPreDhcpAction().
+        // TODO: Remove this requirement, perhaps via some
+        // registerForPreDhcpAction()-style mechanism.
         public void onPreDhcpAction() {}
         public void onPostDhcpAction() {}
 
-        // TODO: Kill with fire once DHCP and static configuration are moved
-        // out of WifiStateMachine.
-        public void onIPv4ProvisioningSuccess(DhcpResults dhcpResults) {}
-        public void onIPv4ProvisioningFailure() {}
+        // This is purely advisory and not an indication of provisioning
+        // success or failure.  This is only here for callers that want to
+        // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
+        // DHCPv4 or static IPv4 configuration failure or success can be
+        // determined by whether or not the passed-in DhcpResults object is
+        // null or not.
+        public void onNewDhcpResults(DhcpResults dhcpResults) {}
 
         public void onProvisioningSuccess(LinkProperties newLp) {}
         public void onProvisioningFailure(LinkProperties newLp) {}
@@ -122,8 +126,10 @@
 
     private final Object mLock = new Object();
     private final State mStoppedState = new StoppedState();
+    private final State mStoppingState = new StoppingState();
     private final State mStartedState = new StartedState();
 
+    private final String mTag;
     private final Context mContext;
     private final String mInterfaceName;
     @VisibleForTesting
@@ -150,11 +156,11 @@
 
     public IpManager(Context context, String ifName, Callback callback)
                 throws IllegalArgumentException {
-        super(TAG + "." + ifName);
+        super(IpManager.class.getSimpleName() + "." + ifName);
+        mTag = getName();
 
         mContext = context;
         mInterfaceName = ifName;
-
         mCallback = callback;
 
         mNwService = INetworkManagementService.Stub.asInterface(
@@ -171,7 +177,7 @@
         try {
             mNwService.registerObserver(mNetlinkTracker);
         } catch (RemoteException e) {
-            Log.e(TAG, "Couldn't register NetlinkTracker: " + e.toString());
+            Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString());
         }
 
         resetLinkProperties();
@@ -179,6 +185,8 @@
         // Super simple StateMachine.
         addState(mStoppedState);
         addState(mStartedState);
+        addState(mStoppingState);
+
         setInitialState(mStoppedState);
         setLogRecSize(MAX_LOG_RECORDS);
         super.start();
@@ -192,7 +200,9 @@
      */
     @VisibleForTesting
     protected IpManager(String ifName, Callback callback) {
-        super(TAG + ".test-" + ifName);
+        super(IpManager.class.getSimpleName() + ".test-" + ifName);
+        mTag = getName();
+
         mInterfaceName = ifName;
         mCallback = callback;
 
@@ -203,13 +213,11 @@
 
     public void startProvisioning(StaticIpConfiguration staticIpConfig) {
         getInterfaceIndex();
-
         sendMessage(CMD_START, staticIpConfig);
     }
 
     public void startProvisioning() {
         getInterfaceIndex();
-
         sendMessage(CMD_START);
     }
 
@@ -236,12 +244,48 @@
      * Internals.
      */
 
+    @Override
+    protected String getWhatToString(int what) {
+        // TODO: Investigate switching to reflection.
+        switch (what) {
+            case CMD_STOP:
+                return "CMD_STOP";
+            case CMD_START:
+                return "CMD_START";
+            case CMD_CONFIRM:
+                return "CMD_CONFIRM";
+            case EVENT_PRE_DHCP_ACTION_COMPLETE:
+                return "EVENT_PRE_DHCP_ACTION_COMPLETE";
+            case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+                return "EVENT_NETLINK_LINKPROPERTIES_CHANGED";
+            case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                return "DhcpStateMachine.CMD_PRE_DHCP_ACTION";
+            case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                return "DhcpStateMachine.CMD_POST_DHCP_ACTION";
+            case DhcpStateMachine.CMD_ON_QUIT:
+                return "DhcpStateMachine.CMD_ON_QUIT";
+        }
+        return "UNKNOWN:" + Integer.toString(what);
+    }
+
+    @Override
+    protected String getLogRecString(Message msg) {
+        final String logLine = String.format(
+                "iface{%s/%d} arg1{%d} arg2{%d} obj{%s}",
+                mInterfaceName, mInterfaceIndex,
+                msg.arg1, msg.arg2, Objects.toString(msg.obj));
+        if (VDBG) {
+            Log.d(mTag, getWhatToString(msg.what) + " " + logLine);
+        }
+        return logLine;
+    }
+
     private void getInterfaceIndex() {
         try {
             mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
         } catch (SocketException | NullPointerException e) {
             // TODO: throw new IllegalStateException.
-            Log.e(TAG, "ALERT: Failed to get interface index: ", e);
+            Log.e(mTag, "ALERT: Failed to get interface index: ", e);
         }
     }
 
@@ -260,16 +304,93 @@
         }
     }
 
+    // For now: use WifiStateMachine's historical notion of provisioned.
+    private static boolean isProvisioned(LinkProperties lp) {
+        // For historical reasons, we should connect even if all we have is
+        // an IPv4 address and nothing else.
+        return lp.isProvisioned() || lp.hasIPv4Address();
+    }
+
+    // TODO: Investigate folding all this into the existing static function
+    // LinkProperties.compareProvisioning() or some other single function that
+    // takes two LinkProperties objects and returns a ProvisioningChange
+    // object that is a correct and complete assessment of what changed, taking
+    // account of the asymmetries described in the comments in this function.
+    // Then switch to using it everywhere (IpReachabilityMonitor, etc.).
+    private static ProvisioningChange compareProvisioning(
+            LinkProperties oldLp, LinkProperties newLp) {
+        ProvisioningChange delta;
+
+        final boolean wasProvisioned = isProvisioned(oldLp);
+        final boolean isProvisioned = isProvisioned(newLp);
+
+        if (!wasProvisioned && isProvisioned) {
+            delta = ProvisioningChange.GAINED_PROVISIONING;
+        } else if (wasProvisioned && isProvisioned) {
+            delta = ProvisioningChange.STILL_PROVISIONED;
+        } else if (!wasProvisioned && !isProvisioned) {
+            delta = ProvisioningChange.STILL_NOT_PROVISIONED;
+        } else {
+            // (wasProvisioned && !isProvisioned)
+            //
+            // Note that this is true even if we lose a configuration element
+            // (e.g., a default gateway) that would not be required to advance
+            // into provisioned state. This is intended: if we have a default
+            // router and we lose it, that's a sure sign of a problem, but if
+            // we connect to a network with no IPv4 DNS servers, we consider
+            // that to be a network without DNS servers and connect anyway.
+            //
+            // See the comment below.
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
+        // Additionally:
+        //
+        // Partial configurations (e.g., only an IPv4 address with no DNS
+        // servers and no default route) are accepted as long as DHCPv4
+        // succeeds. On such a network, isProvisioned() will always return
+        // false, because the configuration is not complete, but we want to
+        // connect anyway. It might be a disconnected network such as a
+        // Chromecast or a wireless printer, for example.
+        //
+        // Because on such a network isProvisioned() will always return false,
+        // delta will never be LOST_PROVISIONING. So check for loss of
+        // provisioning here too.
+        if ((oldLp.hasIPv4Address() && !newLp.hasIPv4Address()) ||
+                (oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned())) {
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
+        return delta;
+    }
+
+    private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
+        switch (delta) {
+            case GAINED_PROVISIONING:
+                if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
+                mCallback.onProvisioningSuccess(newLp);
+                break;
+
+            case LOST_PROVISIONING:
+                if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+                mCallback.onProvisioningFailure(newLp);
+                break;
+
+            default:
+                if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
+                mCallback.onLinkPropertiesChange(newLp);
+                break;
+        }
+    }
+
     private ProvisioningChange setLinkProperties(LinkProperties newLp) {
         if (mIpReachabilityMonitor != null) {
             mIpReachabilityMonitor.updateLinkProperties(newLp);
         }
 
-        // TODO: Figure out whether and how to incorporate static configuration
-        // into the notion of provisioning.
         ProvisioningChange delta;
         synchronized (mLock) {
-            delta = LinkProperties.compareProvisioning(mLinkProperties, newLp);
+            delta = compareProvisioning(mLinkProperties, newLp);
             mLinkProperties = new LinkProperties(newLp);
         }
 
@@ -277,7 +398,7 @@
             switch (delta) {
                 case GAINED_PROVISIONING:
                 case LOST_PROVISIONING:
-                    Log.d(TAG, "provisioning: " + delta);
+                    Log.d(mTag, "provisioning: " + delta);
                     break;
             }
         }
@@ -333,7 +454,7 @@
         }
 
         if (VDBG) {
-            Log.d(TAG, "newLp{" + newLp + "}");
+            Log.d(mTag, "newLp{" + newLp + "}");
         }
 
         return newLp;
@@ -345,21 +466,51 @@
             ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
             mNwService.setInterfaceConfig(mInterfaceName, ifcg);
         } catch (RemoteException e) {
-            Log.e(TAG, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
+            Log.e(mTag, "ALERT: Failed to clear IPv4 address on interface " + mInterfaceName, e);
         }
     }
 
     private void handleIPv4Success(DhcpResults dhcpResults) {
         mDhcpResults = new DhcpResults(dhcpResults);
-        setLinkProperties(assembleLinkProperties());
-        mCallback.onIPv4ProvisioningSuccess(dhcpResults);
+        final LinkProperties newLp = assembleLinkProperties();
+        final ProvisioningChange delta = setLinkProperties(newLp);
+
+        if (VDBG) {
+            Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
+        }
+        mCallback.onNewDhcpResults(dhcpResults);
+
+        dispatchCallback(delta, newLp);
     }
 
     private void handleIPv4Failure() {
+        // TODO: Figure out to de-dup this and the same code in DhcpClient.
         clearIPv4Address();
         mDhcpResults = null;
-        setLinkProperties(assembleLinkProperties());
-        mCallback.onIPv4ProvisioningFailure();
+        final LinkProperties newLp = assembleLinkProperties();
+        ProvisioningChange delta = setLinkProperties(newLp);
+        // If we've gotten here and we're still not provisioned treat that as
+        // a total loss of provisioning.
+        //
+        // Either (a) static IP configuration failed or (b) DHCPv4 failed AND
+        // there was no usable IPv6 obtained before the DHCPv4 timeout.
+        //
+        // Regardless: GAME OVER.
+        //
+        // TODO: Make the DHCP client not time out and just continue in
+        // exponential backoff. Callers such as Wi-Fi which need a timeout
+        // should implement it themselves.
+        if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) {
+            delta = ProvisioningChange.LOST_PROVISIONING;
+        }
+
+        if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
+        mCallback.onNewDhcpResults(null);
+
+        dispatchCallback(delta, newLp);
+        if (delta == ProvisioningChange.LOST_PROVISIONING) {
+            transitionTo(mStoppingState);
+        }
     }
 
     class StoppedState extends State {
@@ -369,7 +520,7 @@
                 mNwService.disableIpv6(mInterfaceName);
                 mNwService.clearInterfaceAddresses(mInterfaceName);
             } catch (Exception e) {
-                Log.e(TAG, "Failed to clear addresses or disable IPv6" + e);
+                Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
             }
 
             resetLinkProperties();
@@ -391,13 +542,8 @@
                     break;
 
                 case DhcpStateMachine.CMD_ON_QUIT:
-                    // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
-                    // Shutting down DHCPv4 progresses simultaneously with
-                    // transitioning to StoppedState, so we can receive this
-                    // message after we've already transitioned here.
-                    //
-                    // TODO: Figure out if this is actually useful and if not
-                    // expunge it.
+                    // Everything is already stopped.
+                    Log.e(mTag, "Unexpected CMD_ON_QUIT (already stopped).");
                     break;
 
                 default:
@@ -407,6 +553,30 @@
         }
     }
 
+    class StoppingState extends State {
+        @Override
+        public void enter() {
+            if (mDhcpStateMachine == null) {
+                // There's no DHCPv4 for which to wait; proceed to stopped.
+                transitionTo(mStoppedState);
+            }
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case DhcpStateMachine.CMD_ON_QUIT:
+                    mDhcpStateMachine = null;
+                    transitionTo(mStoppedState);
+                    break;
+
+                default:
+                    deferMessage(msg);
+            }
+            return HANDLED;
+        }
+    }
+
     class StartedState extends State {
         @Override
         public void enter() {
@@ -416,9 +586,9 @@
                 mNwService.enableIpv6(mInterfaceName);
                 // TODO: Perhaps clearIPv4Address() as well.
             } catch (RemoteException re) {
-                Log.e(TAG, "Unable to change interface settings: " + re);
+                Log.e(mTag, "Unable to change interface settings: " + re);
             } catch (IllegalStateException ie) {
-                Log.e(TAG, "Unable to change interface settings: " + ie);
+                Log.e(mTag, "Unable to change interface settings: " + ie);
             }
 
             mIpReachabilityMonitor = new IpReachabilityMonitor(
@@ -439,7 +609,9 @@
                 if (applyStaticIpConfig()) {
                     handleIPv4Success(new DhcpResults(mStaticIpConfig));
                 } else {
-                    handleIPv4Failure();
+                    if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+                    mCallback.onProvisioningFailure(getLinkProperties());
+                    transitionTo(mStoppingState);
                 }
             } else {
                 // Start DHCPv4.
@@ -457,7 +629,6 @@
             if (mDhcpStateMachine != null) {
                 mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_STOP_DHCP);
                 mDhcpStateMachine.doQuit();
-                mDhcpStateMachine = null;
             }
 
             resetLinkProperties();
@@ -471,7 +642,7 @@
                     break;
 
                 case CMD_START:
-                    Log.e(TAG, "ALERT: START received in StartedState. Please fix caller.");
+                    Log.e(mTag, "ALERT: START received in StartedState. Please fix caller.");
                     break;
 
                 case CMD_CONFIRM:
@@ -500,28 +671,15 @@
                         break;
                     }
                     final ProvisioningChange delta = setLinkProperties(newLp);
-
-                    // NOTE: The only receiver of these callbacks currently
-                    // treats all three of them identically, namely it calls
-                    // IpManager#getLinkProperties() and makes its own determination.
-                    switch (delta) {
-                        case GAINED_PROVISIONING:
-                            mCallback.onProvisioningSuccess(newLp);
-                            break;
-
-                        case LOST_PROVISIONING:
-                            mCallback.onProvisioningFailure(newLp);
-                            break;
-
-                        default:
-                            // TODO: Only notify on STILL_PROVISIONED?
-                            mCallback.onLinkPropertiesChange(newLp);
-                            break;
+                    dispatchCallback(delta, newLp);
+                    if (delta == ProvisioningChange.LOST_PROVISIONING) {
+                        transitionTo(mStoppedState);
                     }
                     break;
                 }
 
                 case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                    if (VDBG) { Log.d(mTag, "onPreDhcpAction()"); }
                     mCallback.onPreDhcpAction();
                     break;
 
@@ -529,6 +687,7 @@
                     // Note that onPostDhcpAction() is likely to be
                     // asynchronous, and thus there is no guarantee that we
                     // will be able to observe any of its effects here.
+                    if (VDBG) { Log.d(mTag, "onPostDhcpAction()"); }
                     mCallback.onPostDhcpAction();
 
                     final DhcpResults dhcpResults = (DhcpResults) msg.obj;
@@ -540,17 +699,15 @@
                             handleIPv4Failure();
                             break;
                         default:
-                            Log.e(TAG, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
+                            Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
                     }
                     break;
                 }
 
                 case DhcpStateMachine.CMD_ON_QUIT:
-                    // CMD_ON_QUIT is really more like "EVENT_ON_QUIT".
-                    // Regardless, we ignore it.
-                    //
-                    // TODO: Figure out if this is actually useful and if not
-                    // expunge it.
+                    // DHCPv4 quit early for some reason.
+                    Log.e(mTag, "Unexpected CMD_ON_QUIT.");
+                    mDhcpStateMachine = null;
                     break;
 
                 default:
@@ -565,9 +722,9 @@
             ifcg.setInterfaceUp();
             try {
                 mNwService.setInterfaceConfig(mInterfaceName, ifcg);
-                if (DBG) Log.d(TAG, "Static IP configuration succeeded");
+                if (DBG) Log.d(mTag, "Static IP configuration succeeded");
             } catch (IllegalStateException | RemoteException e) {
-                Log.e(TAG, "Static IP configuration failed: ", e);
+                Log.e(mTag, "Static IP configuration failed: ", e);
                 return false;
             }
 
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 429839f..0d6c70b 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -92,7 +92,33 @@
     int NO_SMS_TO_ACK = 48;                   /* ACK received when there is no SMS to ack */
     int NETWORK_ERR = 49;                     /* Received error from network */
     int REQUEST_RATE_LIMITED = 50;            /* Operation denied due to overly-frequent requests */
-
+    // Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
+    // reveal particular replacement for Generic failure
+    int OEM_ERROR_1 = 501;
+    int OEM_ERROR_2 = 502;
+    int OEM_ERROR_3 = 503;
+    int OEM_ERROR_4 = 504;
+    int OEM_ERROR_5 = 505;
+    int OEM_ERROR_6 = 506;
+    int OEM_ERROR_7 = 507;
+    int OEM_ERROR_8 = 508;
+    int OEM_ERROR_9 = 509;
+    int OEM_ERROR_10 = 510;
+    int OEM_ERROR_11 = 511;
+    int OEM_ERROR_12 = 512;
+    int OEM_ERROR_13 = 513;
+    int OEM_ERROR_14 = 514;
+    int OEM_ERROR_15 = 515;
+    int OEM_ERROR_16 = 516;
+    int OEM_ERROR_17 = 517;
+    int OEM_ERROR_18 = 518;
+    int OEM_ERROR_19 = 519;
+    int OEM_ERROR_20 = 520;
+    int OEM_ERROR_21 = 521;
+    int OEM_ERROR_22 = 522;
+    int OEM_ERROR_23 = 523;
+    int OEM_ERROR_24 = 524;
+    int OEM_ERROR_25 = 525;
 
     /* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
     int NETWORK_MODE_WCDMA_PREF     = 0; /* GSM/WCDMA (WCDMA preferred) */
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 09040f5..55b9182 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -885,6 +885,24 @@
     }
 
     /**
+     * Sets whether or not the given network is metered from a network policy
+     * point of view. A network should be classified as metered when the user is
+     * sensitive to heavy data usage on that connection due to monetary costs,
+     * data limitations or battery/performance issues. A typical example would
+     * be a wifi connection where the user was being charged for usage.
+     * @param netId the integer that identifies the network configuration
+     * to the supplicant.
+     * @param isMetered True to mark the network as metered.
+     * @return {@code true} if the operation succeeded.
+     * @hide
+     */
+    @SystemApi
+    public boolean setMetered(int netId, boolean isMetered) {
+        // TODO(jjoslin): Implement
+        return false;
+    }
+
+    /**
      * Remove the specified network from the list of configured networks.
      * This may result in the asynchronous delivery of state change
      * events.