Merge changes I229722fa,Iee8b5d8a

* changes:
  Formatting fixes to ScriptingLayerService (2/3)
  Formatting fixes to ScriptingLayerService (1/3)
diff --git a/Common/src/com/googlecode/android_scripting/facade/ConnectivityManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/ConnectivityManagerFacade.java
index 2b3697f..c0c09b8 100644
--- a/Common/src/com/googlecode/android_scripting/facade/ConnectivityManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/ConnectivityManagerFacade.java
@@ -34,6 +34,7 @@
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
+import android.net.ProxyInfo;
 import android.net.StringNetworkSpecifier;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -1104,6 +1105,50 @@
         }
     }
 
+    /**
+     * Sets the global proxy using the given information.
+     *
+     * @param hostname hostname of the proxy
+     * @param port     port set on the proxy server
+     * @param exclList List of hostnames excluded
+     */
+    @Rpc(description = "Set global proxy")
+    public void connectivitySetGlobalProxy(String hostname, Integer port, String exclList) {
+        ProxyInfo proxyInfo = new ProxyInfo(hostname, port.intValue(), exclList);
+        mManager.setGlobalProxy(proxyInfo);
+    }
+
+    /**
+     * Sets the global proxy using a PAC URI.
+     *
+     * @param pac PAC URI in string
+     */
+    @Rpc(description = "Set global proxy with proxy autoconfig")
+    public void connectivitySetGlobalPacProxy(String pac) {
+        ProxyInfo proxyInfo = new ProxyInfo(pac);
+        mManager.setGlobalProxy(proxyInfo);
+    }
+
+    /**
+     * Gets the global proxy settings.
+     *
+     * @return ProxyInfo object in dictionary
+     */
+    @Rpc(description = "Get global proxy")
+    public ProxyInfo connectivityGetGlobalProxy() {
+        ProxyInfo proxyInfo = mManager.getGlobalProxy();
+        if (proxyInfo == null) return null;
+        return proxyInfo;
+    }
+
+    /**
+     * Resets the global proxy settings.
+     */
+    @Rpc(description = "Reset global proxy")
+    public void connectivityResetGlobalProxy() {
+        mManager.setGlobalProxy(null);
+    }
+
     @Override
     public void shutdown() {
         connectivityStopTrackingConnectivityStateChange();
diff --git a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiAwareManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiAwareManagerFacade.java
index 17053ec..dc353e3 100644
--- a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiAwareManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiAwareManagerFacade.java
@@ -263,6 +263,10 @@
         if (j.has("TerminateNotificationEnabled")) {
             builder.setTerminateNotificationEnabled(j.getBoolean("TerminateNotificationEnabled"));
         }
+        if (j.has("RangingEnabled")) {
+            builder.setRangingEnabled(j.getBoolean("RangingEnabled"));
+        }
+
 
         return builder.build();
     }
@@ -305,6 +309,12 @@
         if (j.has("TerminateNotificationEnabled")) {
             builder.setTerminateNotificationEnabled(j.getBoolean("TerminateNotificationEnabled"));
         }
+        if (j.has("MinDistanceMm")) {
+            builder.setMinDistanceMm(j.getInt("MinDistanceMm"));
+        }
+        if (j.has("MaxDistanceMm")) {
+            builder.setMaxDistanceMm(j.getInt("MaxDistanceMm"));
+        }
 
         return builder.build();
     }
@@ -546,27 +556,6 @@
         session.sendMessage(new PeerHandle(peerId), messageId, bytes, retryCount);
     }
 
-    @Rpc(description = "Start peer-to-peer Aware ranging")
-    public void wifiAwareStartRanging(
-            @RpcParameter(name = "callbackId") Integer callbackId,
-            @RpcParameter(name = "sessionId", description = "The session ID returned when session was created using publish or subscribe") Integer sessionId,
-            @RpcParameter(name = "rttParams", description = "RTT session parameters.") JSONArray rttParams) throws RemoteException, JSONException {
-        DiscoverySession session;
-        synchronized (mLock) {
-            session = mDiscoverySessions.get(sessionId);
-        }
-        if (session == null) {
-            throw new IllegalStateException(
-                    "Calling WifiAwareStartRanging before session (session ID "
-                            + sessionId + " is ready");
-        }
-        RttManager.RttParams[] rParams = new RttManager.RttParams[rttParams.length()];
-        for (int i = 0; i < rttParams.length(); i++) {
-            rParams[i] = WifiRttManagerFacade.parseRttParam(rttParams.getJSONObject(i));
-        }
-        session.startRanging(rParams, new WifiAwareRangingListener(callbackId, sessionId));
-    }
-
     @Rpc(description = "Create a network specifier to be used when specifying a Aware network request")
     public String wifiAwareCreateNetworkSpecifier(
             @RpcParameter(name = "sessionId", description = "The session ID returned when session was created using publish or subscribe")
@@ -589,21 +578,30 @@
                     "Calling wifiAwareCreateNetworkSpecifier before session (session ID "
                             + sessionId + " is ready");
         }
-        NetworkSpecifier ns = null;
         PeerHandle peerHandle = null;
         if (peerId != null) {
             peerHandle = new PeerHandle(peerId);
         }
-        if (TextUtils.isEmpty(passphrase) && TextUtils.isEmpty(pmk)) {
-            ns = session.createNetworkSpecifierOpen(peerHandle);
-        } else if (TextUtils.isEmpty(pmk)){
-            ns = session.createNetworkSpecifierPassphrase(peerHandle, passphrase);
-        } else {
-            byte[] pmkDecoded = Base64.decode(pmk, Base64.DEFAULT);
-            ns = session.createNetworkSpecifierPmk(peerHandle, pmkDecoded);
+        byte[] pmkDecoded = null;
+        if (!TextUtils.isEmpty(pmk)) {
+            pmkDecoded = Base64.decode(pmk, Base64.DEFAULT);
         }
 
-        return getJsonString((WifiAwareNetworkSpecifier) ns);
+        WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(
+                (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
+                        : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB,
+                session instanceof SubscribeDiscoverySession
+                        ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+                        : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                session.getClientId(),
+                session.getSessionId(),
+                peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID
+                null, // peerMac (not used in this method)
+                pmkDecoded,
+                passphrase,
+                Process.myUid());
+
+        return getJsonString(ns);
     }
 
     @Rpc(description = "Create a network specifier to be used when specifying an OOB Aware network request")
@@ -631,21 +629,29 @@
                     "Calling wifiAwareCreateNetworkSpecifierOob before session (client ID "
                             + clientId + " is ready");
         }
-        NetworkSpecifier ns = null;
         byte[] peerMacBytes = null;
         if (peerMac != null) {
             peerMacBytes = HexEncoding.decode(peerMac.toCharArray(), false);
         }
-        if (TextUtils.isEmpty(passphrase) && TextUtils.isEmpty(pmk)) {
-            ns = session.createNetworkSpecifierOpen(role, peerMacBytes);
-        } else if (TextUtils.isEmpty(pmk)){
-            ns = session.createNetworkSpecifierPassphrase(role, peerMacBytes, passphrase);
-        } else {
-            byte[] pmkDecoded = Base64.decode(pmk, Base64.DEFAULT);
-            ns = session.createNetworkSpecifierPmk(role, peerMacBytes, pmkDecoded);
+        byte[] pmkDecoded = null;
+        if (!TextUtils.isEmpty(pmk)) {
+            pmkDecoded = Base64.decode(pmk, Base64.DEFAULT);
         }
 
-        return getJsonString((WifiAwareNetworkSpecifier) ns);
+        WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(
+                (peerMacBytes == null) ?
+                        WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER
+                        : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB,
+                role,
+                session.getClientId(),
+                0, // 0 is an invalid session ID
+                0, // 0 is an invalid peer ID
+                peerMacBytes,
+                pmkDecoded,
+                passphrase,
+                Process.myUid());
+
+        return getJsonString(ns);
     }
 
     private class AwareAttachCallbackPostsEvents extends AttachCallback {
@@ -782,8 +788,7 @@
             postEvent("WifiAwareSessionOnSessionTerminated", mResults);
         }
 
-        @Override
-        public void onServiceDiscovered(PeerHandle peerHandle,
+        private Bundle createServiceDiscoveredBaseBundle(PeerHandle peerHandle,
                 byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
             Bundle mResults = new Bundle();
             mResults.putInt("discoverySessionId", mDiscoverySessionId);
@@ -797,6 +802,24 @@
             }
             mResults.putStringArrayList("matchFilterList", matchFilterStrings);
             mResults.putLong("timestampMs", System.currentTimeMillis());
+            return mResults;
+        }
+
+        @Override
+        public void onServiceDiscovered(PeerHandle peerHandle,
+                byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
+            Bundle mResults = createServiceDiscoveredBaseBundle(peerHandle, serviceSpecificInfo,
+                    matchFilter);
+            postEvent("WifiAwareSessionOnServiceDiscovered", mResults);
+        }
+
+        @Override
+        public void onServiceDiscoveredWithinRange(PeerHandle peerHandle,
+                byte[] serviceSpecificInfo,
+                List<byte[]> matchFilter, int distanceMm) {
+            Bundle mResults = createServiceDiscoveredBaseBundle(peerHandle, serviceSpecificInfo,
+                    matchFilter);
+            mResults.putInt("distanceMm", distanceMm);
             postEvent("WifiAwareSessionOnServiceDiscovered", mResults);
         }
 
diff --git a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiJsonParser.java b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiJsonParser.java
new file mode 100644
index 0000000..0e6fe9d
--- /dev/null
+++ b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiJsonParser.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.android_scripting.facade.wifi;
+
+import static android.net.wifi.ScanResult.FLAG_80211mc_RESPONDER;
+import static android.net.wifi.ScanResult.FLAG_PASSPOINT_NETWORK;
+
+import android.net.wifi.ScanResult;
+
+import org.apache.commons.codec.binary.Base64Codec;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Set of utility methods to parse JSON constructs back into classes. Usually counterparts to
+ * utility methods which automatically do the conversion from classes to JSON constructs and
+ * are in JsonBuilder.java.
+ */
+public class WifiJsonParser {
+    /**
+     * Converts a JSON representation of a ScanResult to an actual ScanResult object. Mirror of
+     * the code in
+     * {@link com.googlecode.android_scripting.jsonrpc.JsonBuilder#buildJsonScanResult(ScanResult)}.
+     *
+     * @param j JSON object representing a ScanResult.
+     * @return a ScanResult object
+     * @throws JSONException on any JSON errors
+     */
+    public static ScanResult getScanResult(JSONObject j) throws JSONException {
+        if (j == null) {
+            return null;
+        }
+
+        ScanResult scanResult = new ScanResult();
+
+        if (j.has("BSSID")) {
+            scanResult.BSSID = j.getString("BSSID");
+        }
+        if (j.has("SSID")) {
+            scanResult.SSID = j.getString("SSID");
+        }
+        if (j.has("frequency")) {
+            scanResult.frequency = j.getInt("frequency");
+        }
+        if (j.has("level")) {
+            scanResult.level = j.getInt("level");
+        }
+        if (j.has("capabilities")) {
+            scanResult.capabilities = j.getString("capabilities");
+        }
+        if (j.has("timestamp")) {
+            scanResult.timestamp = j.getLong("timestamp");
+        }
+        if (j.has("centerFreq0")) {
+            scanResult.centerFreq0 = j.getInt("centerFreq0");
+        }
+        if (j.has("centerFreq1")) {
+            scanResult.centerFreq1 = j.getInt("centerFreq1");
+        }
+        if (j.has("channelWidth")) {
+            scanResult.channelWidth = j.getInt("channelWidth");
+        }
+        if (j.has("distanceCm")) {
+            scanResult.distanceCm = j.getInt("distanceCm");
+        }
+        if (j.has("distanceSdCm")) {
+            scanResult.distanceSdCm = j.getInt("distanceSdCm");
+        }
+        if (j.has("is80211McRTTResponder") && j.getBoolean("is80211McRTTResponder")) {
+            scanResult.setFlag(FLAG_80211mc_RESPONDER);
+        }
+        if (j.has("passpointNetwork") && j.getBoolean("passpointNetwork")) {
+            scanResult.setFlag(FLAG_PASSPOINT_NETWORK);
+        }
+        if (j.has("numUsage")) {
+            scanResult.numUsage = j.getInt("numUsage");
+        }
+        if (j.has("seen")) {
+            scanResult.seen = j.getLong("seen");
+        }
+        if (j.has("untrusted")) {
+            scanResult.untrusted = j.getBoolean("untrusted");
+        }
+        if (j.has("operatorFriendlyName")) {
+            scanResult.operatorFriendlyName = j.getString("operatorFriendlyName");
+        }
+        if (j.has("venueName")) {
+            scanResult.venueName = j.getString("venueName");
+        }
+        if (j.has("InformationElements")) {
+            JSONArray jIes = j.getJSONArray("InformationElements");
+            if (jIes.length() > 0) {
+                scanResult.informationElements = new ScanResult.InformationElement[jIes.length()];
+                for (int i = 0; i < jIes.length(); ++i) {
+                    ScanResult.InformationElement scanIe = new ScanResult.InformationElement();
+                    JSONObject jIe = jIes.getJSONObject(i);
+                    scanIe.id = jIe.getInt("id");
+                    scanIe.bytes = Base64Codec.decodeBase64(jIe.getString("bytes"));
+                    scanResult.informationElements[i] = scanIe;
+                }
+            }
+        }
+        if (j.has("radioChainInfos")) {
+            JSONArray jRinfos = j.getJSONArray("radioChainInfos");
+            if (jRinfos.length() > 0) {
+                scanResult.radioChainInfos = new ScanResult.RadioChainInfo[jRinfos.length()];
+                for (int i = 0; i < jRinfos.length(); i++) {
+                    ScanResult.RadioChainInfo item = new ScanResult.RadioChainInfo();
+                    JSONObject jRinfo = jRinfos.getJSONObject(i);
+                    item.id = jRinfo.getInt("id");
+                    item.level = jRinfo.getInt("level");
+                    scanResult.radioChainInfos[i] = item;
+                }
+            }
+        }
+
+        return scanResult;
+    }
+
+    /**
+     * Converts a JSONArray toa a list of ScanResult.
+     *
+     * @param j JSONArray representing a collection of ScanResult objects
+     * @return a list of ScanResult objects
+     * @throws JSONException on any JSON error
+     */
+    public static List<ScanResult> getScanResults(JSONArray j) throws JSONException {
+        if (j == null || j.length() == 0) {
+            return null;
+        }
+
+        ArrayList<ScanResult> scanResults = new ArrayList<>(j.length());
+        for (int i = 0; i < j.length(); ++i) {
+            scanResults.add(getScanResult(j.getJSONObject(i)));
+        }
+
+        return scanResults;
+    }
+}
diff --git a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiManagerFacade.java
index e032e55..9dc8bb9 100755
--- a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiManagerFacade.java
@@ -27,8 +27,6 @@
 import android.net.Network;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.wifi.hotspot2.ConfigParser;
-import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiConfiguration;
@@ -39,18 +37,18 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.WifiLock;
 import android.net.wifi.WpsInfo;
+import android.net.wifi.hotspot2.ConfigParser;
+import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Bundle;
 import android.provider.Settings.Global;
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Base64;
-import java.util.Arrays;
 
 import com.googlecode.android_scripting.Log;
 import com.googlecode.android_scripting.facade.EventFacade;
 import com.googlecode.android_scripting.facade.FacadeManager;
 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
 import com.googlecode.android_scripting.rpc.Rpc;
-import com.googlecode.android_scripting.rpc.RpcDeprecated;
 import com.googlecode.android_scripting.rpc.RpcOptional;
 import com.googlecode.android_scripting.rpc.RpcParameter;
 
@@ -224,12 +222,11 @@
             if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
                 Log.d("Wifi network state changed.");
                 NetworkInfo nInfo = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-                WifiInfo wInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
                 Log.d("NetworkInfo " + nInfo);
-                Log.d("WifiInfo " + wInfo);
                 // If network info is of type wifi, send wifi events.
                 if (nInfo.getType() == ConnectivityManager.TYPE_WIFI) {
-                    if (wInfo != null && nInfo.getDetailedState().equals(DetailedState.CONNECTED)) {
+                    if (nInfo.getDetailedState().equals(DetailedState.CONNECTED)) {
+                        WifiInfo wInfo = mWifi.getConnectionInfo();
                         String bssid = wInfo.getBSSID();
                         if (bssid != null && !mCachedWifiInfo.equals(wInfo.toString())) {
                             Log.d("WifiNetworkConnected");
@@ -883,8 +880,14 @@
         if (ssid != null) {
             config.SSID = ssid.substring(1, ssid.length() - 1);
         }
-        String pwd = config.preSharedKey;                                                                      if (pwd != null) {
+
+        config.allowedKeyManagement.clear();
+        String pwd = config.preSharedKey;
+        if (pwd != null) {
+            config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
             config.preSharedKey = pwd.substring(1, pwd.length() - 1);
+        } else {
+            config.allowedKeyManagement.set(KeyMgmt.NONE);
         }
         return config;
     }
@@ -896,24 +899,10 @@
         return mWifi.setWifiApConfiguration(config);
     }
 
-    @Rpc(description = "Start/stop wifi soft AP.")
-    public Boolean wifiSetApEnabled(
-            @RpcParameter(name = "enable") Boolean enable,
-            @RpcParameter(name = "configJson") JSONObject configJson) throws JSONException {
-        int wifiState = mWifi.getWifiState();
-        if (enable) {
-            WifiConfiguration config = createSoftApWifiConfiguration(configJson);
-            return mWifi.setWifiApEnabled(config, enable);
-        } else {
-            return mWifi.setWifiApEnabled(null, false);
-        }
-    }
-
     @Rpc(description = "Set the country code used by WiFi.")
     public void wifiSetCountryCode(
-            @RpcParameter(name = "country") String country,
-            @RpcParameter(name = "persist") Boolean persist) {
-        mWifi.setCountryCode(country, persist);
+            @RpcParameter(name = "country") String country) {
+        mWifi.setCountryCode(country);
     }
 
     @Rpc(description = "Enable/disable tdls with a mac address.")
diff --git a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiRtt2ManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiRtt2ManagerFacade.java
new file mode 100644
index 0000000..5197896
--- /dev/null
+++ b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiRtt2ManagerFacade.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.googlecode.android_scripting.facade.wifi;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.MacAddress;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.WifiRttManager;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.WorkSource;
+
+import com.android.internal.annotations.GuardedBy;
+
+import libcore.util.HexEncoding;
+
+import com.googlecode.android_scripting.facade.EventFacade;
+import com.googlecode.android_scripting.facade.FacadeManager;
+import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
+import com.googlecode.android_scripting.rpc.Rpc;
+import com.googlecode.android_scripting.rpc.RpcOptional;
+import com.googlecode.android_scripting.rpc.RpcParameter;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import java.util.List;
+
+/**
+ * Facade for RTTv2 manager.
+ */
+public class WifiRtt2ManagerFacade extends RpcReceiver {
+    private final Service mService;
+    private final EventFacade mEventFacade;
+    private final StateChangedReceiver mStateChangedReceiver;
+
+    private final Object mLock = new Object(); // lock access to the following vars
+
+    @GuardedBy("mLock")
+    private WifiRttManager mMgr;
+
+    @GuardedBy("mLock")
+    private int mNextRangingResultCallbackId = 1;
+
+    public WifiRtt2ManagerFacade(FacadeManager manager) {
+        super(manager);
+        mService = manager.getService();
+        mEventFacade = manager.getReceiver(EventFacade.class);
+
+        mMgr = (WifiRttManager) mService.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
+
+        mStateChangedReceiver = new StateChangedReceiver();
+        IntentFilter filter = new IntentFilter(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED);
+        mService.registerReceiver(mStateChangedReceiver, filter);
+    }
+
+    @Override
+    public void shutdown() {
+        // empty
+    }
+
+    @Rpc(description = "Does the device support the Wi-Fi RTT feature?")
+    public Boolean doesDeviceSupportWifiRttFeature() {
+        return mService.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT);
+    }
+
+    @Rpc(description = "Is Wi-Fi RTT Usage Enabled?")
+    public Boolean wifiIsRttAvailable() throws RemoteException {
+        synchronized (mLock) {
+            return mMgr.isAvailable();
+        }
+    }
+
+    @Rpc(description = "The maximum number of peers permitted in a single RTT request")
+    public Integer wifiRttMaxPeersInRequest() {
+      return RangingRequest.getMaxPeers();
+    }
+
+    /**
+     * Start Wi-Fi RTT ranging to an Acccess Point using the given scan results. Returns the id
+     * associated with the listener used for ranging. The ranging result event will be decorated
+     * with the listener id.
+     */
+    @Rpc(description = "Start ranging to an Access Points.", returns = "Id of the listener "
+            + "associated with the started ranging.")
+    public Integer wifiRttStartRangingToAccessPoints(
+            @RpcParameter(name = "scanResults") JSONArray scanResults,
+            @RpcParameter(name = "uidsOverride", description = "Overrides for the UID")
+                @RpcOptional JSONArray uidsOverride) throws JSONException {
+        synchronized (mLock) {
+            int id = mNextRangingResultCallbackId++;
+            RangingResultCallback callback = new RangingResultCallbackFacade(id);
+            mMgr.startRanging(getWorkSource(uidsOverride),
+                    new RangingRequest.Builder().addAccessPoints(
+                            WifiJsonParser.getScanResults(scanResults)).build(),
+                    mService.getMainExecutor(), callback);
+            return id;
+        }
+    }
+
+    @Rpc(description = "Start ranging to an Aware peer.", returns = "Id of the listener "
+            + "associated with the started ranging.")
+    public Integer wifiRttStartRangingToAwarePeerMac(
+            @RpcParameter(name = "peerMac") String peerMac,
+            @RpcParameter(name = "uidsOverride", description = "Overrides for the UID")
+            @RpcOptional JSONArray uidsOverride) throws JSONException {
+        synchronized (mLock) {
+            int id = mNextRangingResultCallbackId++;
+            RangingResultCallback callback = new RangingResultCallbackFacade(id);
+            byte[] peerMacBytes = HexEncoding.decode(peerMac); // since Aware returns string w/o ":"
+            mMgr.startRanging(getWorkSource(uidsOverride),
+                    new RangingRequest.Builder().addWifiAwarePeer(
+                            MacAddress.fromBytes(peerMacBytes)).build(), mService.getMainExecutor(),
+                    callback);
+            return id;
+        }
+    }
+
+    @Rpc(description = "Start ranging to an Aware peer.", returns = "Id of the listener "
+            + "associated with the started ranging.")
+    public Integer wifiRttStartRangingToAwarePeerId(
+            @RpcParameter(name = "peer ID") Integer peerId,
+            @RpcParameter(name = "uidsOverride", description = "Overrides for the UID")
+            @RpcOptional JSONArray uidsOverride) throws JSONException {
+        synchronized (mLock) {
+            int id = mNextRangingResultCallbackId++;
+            RangingResultCallback callback = new RangingResultCallbackFacade(id);
+            mMgr.startRanging(getWorkSource(uidsOverride),
+                    new RangingRequest.Builder().addWifiAwarePeer(
+                            new PeerHandle(peerId)).build(), mService.getMainExecutor(), callback);
+            return id;
+        }
+    }
+
+    @Rpc(description = "Cancel ranging requests for the specified UIDs")
+    public void wifiRttCancelRanging(@RpcParameter(name = "uids", description = "List of UIDs")
+        @RpcOptional JSONArray uids) throws JSONException {
+        synchronized (mLock) {
+            mMgr.cancelRanging(getWorkSource(uids));
+        }
+    }
+
+    private class RangingResultCallbackFacade extends RangingResultCallback {
+        private int mCallbackId;
+
+        RangingResultCallbackFacade(int callbackId) {
+            mCallbackId = callbackId;
+        }
+
+        @Override
+        public void onRangingFailure(int status) {
+            Bundle msg = new Bundle();
+            msg.putInt("status", status);
+            mEventFacade.postEvent("WifiRttRangingFailure_" + mCallbackId, msg);
+        }
+
+        @Override
+        public void onRangingResults(List<RangingResult> results) {
+            Bundle msg = new Bundle();
+            Parcelable[] resultBundles = new Parcelable[results.size()];
+            for (int i = 0; i < results.size(); i++) {
+                resultBundles[i] = packRttResult(results.get(i));
+            }
+            msg.putParcelableArray("Results", resultBundles);
+            mEventFacade.postEvent("WifiRttRangingResults_" + mCallbackId, msg);
+        }
+    }
+
+    // conversion utilities
+    private static Bundle packRttResult(RangingResult result) {
+        Bundle bundle = new Bundle();
+        bundle.putInt("status", result.getStatus());
+        if (result.getStatus() == RangingResult.STATUS_SUCCESS) { // only valid on SUCCESS
+            bundle.putInt("distanceMm", result.getDistanceMm());
+            bundle.putInt("distanceStdDevMm", result.getDistanceStdDevMm());
+            bundle.putInt("rssi", result.getRssi());
+            bundle.putInt("numAttemptedMeasurements", result.getNumAttemptedMeasurements());
+            bundle.putInt("numSuccessfulMeasurements", result.getNumSuccessfulMeasurements());
+            bundle.putByteArray("lci", result.getLci());
+            bundle.putByteArray("lcr", result.getLcr());
+            bundle.putLong("timestamp", result.getRangingTimestampMillis());
+        }
+        if (result.getPeerHandle() != null) {
+            bundle.putInt("peerId", result.getPeerHandle().peerId);
+        }
+        if (result.getMacAddress() != null) {
+            bundle.putByteArray("mac", result.getMacAddress().toByteArray());
+            bundle.putString("macAsString", result.getMacAddress().toString());
+        }
+        return bundle;
+    }
+
+    private static WorkSource getWorkSource(JSONArray uids) throws JSONException {
+        if (uids == null) {
+            return null;
+        }
+        WorkSource ws = new WorkSource();
+        for (int i = 0; i < uids.length(); ++i) {
+            ws.add(uids.getInt(i));
+        }
+        return ws;
+    }
+
+    class StateChangedReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context c, Intent intent) {
+            boolean isAvailable = mMgr.isAvailable();
+            mEventFacade.postEvent(isAvailable ? "WifiRttAvailable" : "WifiRttNotAvailable",
+                    new Bundle());
+        }
+    }
+}
diff --git a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiScannerFacade.java b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiScannerFacade.java
index c12526a..51d9981 100644
--- a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiScannerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiScannerFacade.java
@@ -16,26 +16,6 @@
 
 package com.googlecode.android_scripting.facade.wifi;
 
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import com.googlecode.android_scripting.Log;
-import com.googlecode.android_scripting.MainThread;
-import com.googlecode.android_scripting.facade.EventFacade;
-import com.googlecode.android_scripting.facade.FacadeManager;
-import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
-import com.googlecode.android_scripting.rpc.Rpc;
-import com.googlecode.android_scripting.rpc.RpcOptional;
-import com.googlecode.android_scripting.rpc.RpcParameter;
-
 import android.app.Service;
 import android.content.Context;
 import android.net.wifi.ScanResult;
@@ -49,6 +29,26 @@
 import android.provider.Settings.Global;
 import android.provider.Settings.SettingNotFoundException;
 
+import com.googlecode.android_scripting.Log;
+import com.googlecode.android_scripting.MainThread;
+import com.googlecode.android_scripting.facade.EventFacade;
+import com.googlecode.android_scripting.facade.FacadeManager;
+import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
+import com.googlecode.android_scripting.rpc.Rpc;
+import com.googlecode.android_scripting.rpc.RpcOptional;
+import com.googlecode.android_scripting.rpc.RpcParameter;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+
 /**
  * WifiScanner functions.
  */
@@ -363,6 +363,9 @@
         if (j.has("numBssidsPerScan")) {
             result.numBssidsPerScan = j.getInt("numBssidsPerScan");
         }
+        if (j.has("type")) {
+            result.type = j.getInt("type");
+        }
         return result;
     }
 
diff --git a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
index 78d4d72..94a96d4 100644
--- a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
+++ b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
@@ -35,12 +35,12 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkInfo;
+import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.Uri;
 import android.net.wifi.RttManager.RttCapabilities;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiActivityEnergyInfo;
-import android.net.wifi.WifiChannel;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
@@ -263,9 +263,6 @@
         if (data instanceof WifiActivityEnergyInfo) {
             return buildWifiActivityEnergyInfo((WifiActivityEnergyInfo) data);
         }
-        if (data instanceof WifiChannel) {
-            return buildWifiChannel((WifiChannel) data);
-        }
         if (data instanceof WifiConfiguration) {
             return buildWifiConfiguration((WifiConfiguration) data);
         }
@@ -290,6 +287,9 @@
         if (data instanceof IpPrefix) {
             return buildIpPrefix((IpPrefix) data);
         }
+        if (data instanceof ProxyInfo) {
+            return buildProxyInfo((ProxyInfo) data);
+        }
         if (data instanceof byte[]) {
             JSONArray result = new JSONArray();
             for (byte b : (byte[]) data) {
@@ -614,16 +614,13 @@
         result.put("level", scanResult.level);
         result.put("capabilities", scanResult.capabilities);
         result.put("timestamp", scanResult.timestamp);
-        result.put("blackListTimestamp", scanResult.blackListTimestamp);
         result.put("centerFreq0", scanResult.centerFreq0);
         result.put("centerFreq1", scanResult.centerFreq1);
         result.put("channelWidth", scanResult.channelWidth);
         result.put("distanceCm", scanResult.distanceCm);
         result.put("distanceSdCm", scanResult.distanceSdCm);
         result.put("is80211McRTTResponder", scanResult.is80211mcResponder());
-        result.put("numConnection", scanResult.numConnection);
         result.put("passpointNetwork", scanResult.isPasspointNetwork());
-        result.put("numIpConfigFailures", scanResult.numIpConfigFailures);
         result.put("numUsage", scanResult.numUsage);
         result.put("seen", scanResult.seen);
         result.put("untrusted", scanResult.untrusted);
@@ -634,12 +631,24 @@
             for (ScanResult.InformationElement ie : scanResult.informationElements) {
                 JSONObject infoEle = new JSONObject();
                 infoEle.put("id", ie.id);
-                infoEle.put("bytes", Base64Codec.encodeBase64(ie.bytes).toString());
+                infoEle.put("bytes", Base64Codec.encodeBase64String(ie.bytes));
                 infoEles.put(infoEle);
             }
-            result.put("InfomationElements", infoEles);
+            result.put("InformationElements", infoEles);
         } else {
-            result.put("InfomationElements", null);
+            result.put("InformationElements", null);
+        }
+        if (scanResult.radioChainInfos != null) {
+            JSONArray radioChainEles = new JSONArray();
+            for (ScanResult.RadioChainInfo item : scanResult.radioChainInfos) {
+                JSONObject radioChainEle = new JSONObject();
+                radioChainEle.put("id", item.id);
+                radioChainEle.put("level", item.level);
+                radioChainEles.put(radioChainEle);
+            }
+            result.put("radioChainInfos", radioChainEles);
+        } else {
+            result.put("radioChainInfos", null);
         }
         return result;
     }
@@ -667,6 +676,7 @@
         result.put("rssi", data.getRssi());
         result.put("BSSID", data.getBSSID());
         result.put("mac_address", data.getMacAddress());
+        result.put("frequency", data.getFrequency());
         // Trim the double quotes if exist
         String ssid = data.getSSID();
         if (ssid.charAt(0) == '"'
@@ -958,15 +968,6 @@
         return result;
     }
 
-    private static Object buildWifiChannel(WifiChannel data) throws JSONException {
-        JSONObject channel = new JSONObject();
-        channel.put("channelNum", data.channelNum);
-        channel.put("freqMHz", data.freqMHz);
-        channel.put("isDFS", data.isDFS);
-        channel.put("isValid", data.isValid());
-        return channel;
-    }
-
     private static Object buildWifiConfiguration(WifiConfiguration data)
             throws JSONException {
         JSONObject config = new JSONObject();
@@ -1089,6 +1090,15 @@
         return info;
     }
 
+    private static JSONObject buildProxyInfo(ProxyInfo data) throws JSONException {
+        JSONObject info = new JSONObject();
+        info.put("Hostname", data.getHost());
+        info.put("Port", data.getPort());
+        info.put("ExclList", data.getExclusionListAsString());
+        info.put("PacUrl", data.getPacFileUrl().toString());
+        return info;
+    }
+
     private static <T> JSONObject buildCallEvent(InCallServiceImpl.CallEvent<T> callEvent)
             throws JSONException {
         JSONObject jsonEvent = new JSONObject();
diff --git a/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java b/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
index 90aced0..81e3037 100644
--- a/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
+++ b/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
@@ -60,6 +60,7 @@
 import com.googlecode.android_scripting.facade.wifi.WifiAwareManagerFacade;
 import com.googlecode.android_scripting.facade.wifi.WifiManagerFacade;
 import com.googlecode.android_scripting.facade.wifi.WifiP2pManagerFacade;
+import com.googlecode.android_scripting.facade.wifi.WifiRtt2ManagerFacade;
 import com.googlecode.android_scripting.facade.wifi.WifiRttManagerFacade;
 import com.googlecode.android_scripting.facade.wifi.WifiScannerFacade;
 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
@@ -164,6 +165,10 @@
             sFacadeClassList.add(SocketFacade.class);
         }
 
+        if (sSdkLevel >= 27) {
+            sFacadeClassList.add(WifiRtt2ManagerFacade.class);
+        }
+
         for (Class<? extends RpcReceiver> recieverClass : sFacadeClassList) {
             for (MethodDescriptor rpcMethod : MethodDescriptor.collectFrom(recieverClass)) {
                 sRpcs.put(rpcMethod.getName(), rpcMethod);
diff --git a/ScriptingLayerForAndroid/Android.mk b/ScriptingLayerForAndroid/Android.mk
index 93a2edb..66af116 100644
--- a/ScriptingLayerForAndroid/Android.mk
+++ b/ScriptingLayerForAndroid/Android.mk
@@ -19,6 +19,8 @@
 
 include $(CLEAR_VARS)
 
+LOCAL_COMPATIBILITY_SUITE := pts device-tests
+LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG := true
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_PACKAGE_NAME := sl4a