Merge "Update generate_api_reference_md.py to work with Python 3" am: 47de3cf5e3

Original change: https://android-review.googlesource.com/c/platform/external/sl4a/+/1826966

Change-Id: If9b84f4bd56e7bd5d92a2b9e1ecf6c04650a0bd4
diff --git a/Common/Android.bp b/Common/Android.bp
index 3898b24..ad42d29 100644
--- a/Common/Android.bp
+++ b/Common/Android.bp
@@ -32,11 +32,13 @@
         "android-common",
         "sl4a.Utils",
         "junit",
+        "modules-utils-build",
     ],
 
     sdk_version: "core_platform",
     libs: [
         "framework-wifi.impl", // allow SL4A to access @hide Wifi APIs
+        "framework-connectivity.impl",
         "framework",
 
         "telephony-common",
diff --git a/Common/src/com/googlecode/android_scripting/facade/AndroidFacade.java b/Common/src/com/googlecode/android_scripting/facade/AndroidFacade.java
index 79ed163..1689c88 100644
--- a/Common/src/com/googlecode/android_scripting/facade/AndroidFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/AndroidFacade.java
@@ -44,6 +44,8 @@
 import android.widget.EditText;
 import android.widget.Toast;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import com.googlecode.android_scripting.BaseApplication;
 import com.googlecode.android_scripting.FileUtils;
 import com.googlecode.android_scripting.FutureActivityTaskExecutor;
@@ -600,6 +602,16 @@
     return Build.VERSION.SDK_INT;
   }
 
+  @Rpc(description = "Returns whether the device is running SDK at least R")
+  public boolean isSdkAtLeastR() {
+    return SdkLevel.isAtLeastR();
+  }
+
+  @Rpc(description = "Returns whether the device is running SDK at least S")
+  public boolean isSdkAtLeastS() {
+    return SdkLevel.isAtLeastS();
+  }
+
   @Rpc(description = "Returns the current device time.")
   public Long getBuildTime() {
     return Build.TIME;
diff --git a/Common/src/com/googlecode/android_scripting/facade/ConnectivityConstants.java b/Common/src/com/googlecode/android_scripting/facade/ConnectivityConstants.java
index c46a3a1..867f624 100644
--- a/Common/src/com/googlecode/android_scripting/facade/ConnectivityConstants.java
+++ b/Common/src/com/googlecode/android_scripting/facade/ConnectivityConstants.java
@@ -89,4 +89,10 @@
     public static final String PrivateDnsModeOff = "off";
     public static final String PrivateDnsModeOpportunistic = "opportunistic";
     public static final String PrivateDnsModeStrict = "hostname";
+
+    /**
+     * Constants for NetworkCapabilties/NetworkRequest
+     */
+    public static final String NET_CAPABILITIES_TRANSPORT_TYPE = "TransportType";
+    public static final String NET_CAPABILITIES_CAPABILITIES = "Capability";
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/ConnectivityEvents.java b/Common/src/com/googlecode/android_scripting/facade/ConnectivityEvents.java
index ec9834a..64c6e65 100644
--- a/Common/src/com/googlecode/android_scripting/facade/ConnectivityEvents.java
+++ b/Common/src/com/googlecode/android_scripting/facade/ConnectivityEvents.java
@@ -17,8 +17,8 @@
 package com.googlecode.android_scripting.facade;
 
 import android.net.NetworkCapabilities;
-import android.net.wifi.aware.WifiAwareNetworkInfo;
 
+import com.googlecode.android_scripting.jsonrpc.JsonBuilder;
 import com.googlecode.android_scripting.jsonrpc.JsonSerializable;
 
 import org.json.JSONException;
@@ -136,33 +136,7 @@
          */
         public JSONObject toJSON() throws JSONException {
             JSONObject json = super.toJSON();
-            json.put(ConnectivityConstants.NetworkCallbackContainer.RSSI,
-                    mNetworkCapabilities.getSignalStrength());
-
-            json.put(ConnectivityConstants.NetworkCallbackContainer.METERED,
-                    !mNetworkCapabilities.hasCapability(ConnectivityConstants.NET_CAPABILITY_TEMPORARILY_NOT_METERED));
-
-            if (mNetworkCapabilities.getNetworkSpecifier() != null) {
-                json.put("network_specifier",
-                        mNetworkCapabilities.getNetworkSpecifier().toString());
-            }
-            if (mNetworkCapabilities.getTransportInfo() instanceof WifiAwareNetworkInfo) {
-                WifiAwareNetworkInfo anc =
-                        (WifiAwareNetworkInfo) mNetworkCapabilities.getTransportInfo();
-
-                String ipv6 = anc.getPeerIpv6Addr().toString();
-                if (ipv6.charAt(0) == '/') {
-                    ipv6 = ipv6.substring(1);
-                }
-                json.put("aware_ipv6", ipv6);
-                if (anc.getPort() != 0) {
-                    json.put("aware_port", anc.getPort());
-                }
-                if (anc.getTransportProtocol() != -1) {
-                    json.put("aware_transport_protocol", anc.getTransportProtocol());
-                }
-            }
-            return json;
+            return JsonBuilder.buildNetworkCapabilities(json, mNetworkCapabilities);
         }
     }
 
diff --git a/Common/src/com/googlecode/android_scripting/facade/ConnectivityManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/ConnectivityManagerFacade.java
index 9040b83..26f0012 100644
--- a/Common/src/com/googlecode/android_scripting/facade/ConnectivityManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/ConnectivityManagerFacade.java
@@ -16,6 +16,8 @@
 
 package com.googlecode.android_scripting.facade;
 
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
 import android.app.Service;
 import android.app.usage.NetworkStats;
 import android.app.usage.NetworkStats.Bucket;
@@ -36,14 +38,18 @@
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.Uri;
+import android.net.wifi.WifiNetworkSpecifier;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.provider.Settings;
 
+import com.android.modules.utils.build.SdkLevel;
+
 import com.google.common.io.ByteStreams;
 import com.googlecode.android_scripting.FileUtils;
 import com.googlecode.android_scripting.Log;
 import com.googlecode.android_scripting.facade.wifi.WifiAwareManagerFacade;
+import com.googlecode.android_scripting.facade.wifi.WifiManagerFacade;
 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
 import com.googlecode.android_scripting.rpc.Rpc;
 import com.googlecode.android_scripting.rpc.RpcOptional;
@@ -66,6 +72,7 @@
 import java.net.SocketException;
 import java.net.URL;
 import java.net.URLConnection;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -122,7 +129,19 @@
         }
     }
 
-    class NetworkCallback extends ConnectivityManager.NetworkCallback {
+    /**
+     * Used to dispatch to a different constructor depending on R or S, since
+     * {@link ConnectivityManager.NetworkCallback#NetworkCallback(int)} was added in S.
+     */
+    private NetworkCallback newNetworkCallback(int events) {
+        if (SdkLevel.isAtLeastS()) {
+            return new NetworkCallback(events);
+        } else {
+            return new NetworkCallback(events, false);
+        }
+    }
+
+    private class NetworkCallback extends ConnectivityManager.NetworkCallback {
         public static final int EVENT_INVALID = -1;
         public static final int EVENT_NONE = 0;
         public static final int EVENT_PRECHECK = 1 << 0;
@@ -135,23 +154,39 @@
         public static final int EVENT_RESUMED = 1 << 7;
         public static final int EVENT_LINK_PROPERTIES_CHANGED = 1 << 8;
         public static final int EVENT_BLOCKED_STATUS_CHANGED = 1 << 9;
-        public static final int EVENT_ALL = EVENT_PRECHECK |
-                EVENT_AVAILABLE |
-                EVENT_LOSING |
-                EVENT_LOST |
-                EVENT_UNAVAILABLE |
-                EVENT_CAPABILITIES_CHANGED |
-                EVENT_SUSPENDED |
-                EVENT_RESUMED |
-                EVENT_LINK_PROPERTIES_CHANGED
-                | EVENT_BLOCKED_STATUS_CHANGED;
+        public static final int EVENT_ALL =
+                EVENT_PRECHECK
+                        | EVENT_AVAILABLE
+                        | EVENT_LOSING
+                        | EVENT_LOST
+                        | EVENT_UNAVAILABLE
+                        | EVENT_CAPABILITIES_CHANGED
+                        | EVENT_SUSPENDED
+                        | EVENT_RESUMED
+                        | EVENT_LINK_PROPERTIES_CHANGED
+                        | EVENT_BLOCKED_STATUS_CHANGED;
 
         private int mEvents;
         public String mId;
         private long mCreateTimestamp;
 
-        public NetworkCallback(int events) {
+        /** Called in >= Android S where super(int) does exist. */
+        private NetworkCallback(int events) {
+            super(ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO);
+            init(events);
+        }
+
+        /**
+         * Called in <= Android R where super(int) doesn't exist.
+         * @param ignore placeholder argument to differentiate between R and S constructors' method
+         *              signatures.
+         */
+        private NetworkCallback(int events, boolean ignore) {
             super();
+            init(events);
+        }
+
+        private void init(int events) {
             mEvents = events;
             mId = this.toString();
             mCreateTimestamp = System.currentTimeMillis();
@@ -431,7 +466,7 @@
         builder.setSignalStrength((int) rssi);
         builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
         NetworkRequest networkRequest = builder.build();
-        mNetworkCallback = new NetworkCallback(NetworkCallback.EVENT_ALL);
+        mNetworkCallback = newNetworkCallback(NetworkCallback.EVENT_ALL);
         mManager.registerNetworkCallback(networkRequest, mNetworkCallback);
         String key = mNetworkCallback.mId;
         mNetworkCallbackMap.put(key, mNetworkCallback);
@@ -473,16 +508,19 @@
             Log.d("build ClearCapabilities");
             builder.clearCapabilities();
         }
-        if (configJson.has("TransportType")) {
-            Log.d("build TransportType" + configJson.getInt("TransportType"));
-            builder.addTransportType(configJson.getInt("TransportType"));
+        if (configJson.has(ConnectivityConstants.NET_CAPABILITIES_TRANSPORT_TYPE)) {
+            Log.d("build TransportType"
+                    + configJson.getInt(ConnectivityConstants.NET_CAPABILITIES_TRANSPORT_TYPE));
+            builder.addTransportType(
+                    configJson.getInt(ConnectivityConstants.NET_CAPABILITIES_TRANSPORT_TYPE));
         }
         if (configJson.has("SignalStrength")) {
             Log.d("build SignalStrength" + configJson.getInt("SignalStrength"));
             builder.setSignalStrength(configJson.getInt("SignalStrength"));
         }
-        if (configJson.has("Capability")) {
-            JSONArray capabilities = configJson.getJSONArray("Capability");
+        if (configJson.has(ConnectivityConstants.NET_CAPABILITIES_CAPABILITIES)) {
+            JSONArray capabilities =
+                    configJson.getJSONArray(ConnectivityConstants.NET_CAPABILITIES_CAPABILITIES);
             for (int i = 0; i < capabilities.length(); i++) {
                 Log.d("build Capability" + capabilities.getInt(i));
                 builder.addCapability(capabilities.getInt(i));
@@ -507,7 +545,7 @@
     public String connectivityRegisterNetworkCallback(@RpcParameter(name = "configJson")
     JSONObject configJson) throws JSONException {
         NetworkRequest networkRequest = buildNetworkRequestFromJson(configJson);
-        mNetworkCallback = new NetworkCallback(NetworkCallback.EVENT_ALL);
+        mNetworkCallback = newNetworkCallback(NetworkCallback.EVENT_ALL);
         mManager.registerNetworkCallback(networkRequest, mNetworkCallback);
         String key = mNetworkCallback.mId;
         mNetworkCallbackMap.put(key, mNetworkCallback);
@@ -529,7 +567,7 @@
 
     @Rpc(description = "register a default network callback")
     public String connectivityRegisterDefaultNetworkCallback() {
-        mNetworkCallback = new NetworkCallback(NetworkCallback.EVENT_ALL);
+        mNetworkCallback = newNetworkCallback(NetworkCallback.EVENT_ALL);
         mManager.registerDefaultNetworkCallback(mNetworkCallback);
         String key = mNetworkCallback.mId;
         mNetworkCallbackMap.put(key, mNetworkCallback);
@@ -540,7 +578,7 @@
     public String connectivityRequestNetwork(@RpcParameter(name = "configJson")
     JSONObject configJson) throws JSONException {
         NetworkRequest networkRequest = buildNetworkRequestFromJson(configJson);
-        mNetworkCallback = new NetworkCallback(NetworkCallback.EVENT_ALL);
+        mNetworkCallback = newNetworkCallback(NetworkCallback.EVENT_ALL);
         mManager.requestNetwork(networkRequest, mNetworkCallback);
         String key = mNetworkCallback.mId;
         mNetworkCallbackMap.put(key, mNetworkCallback);
@@ -551,13 +589,41 @@
     public String connectivityRequestWifiAwareNetwork(@RpcParameter(name = "configJson")
             JSONObject configJson) throws JSONException {
         NetworkRequest networkRequest = buildWifiAwareNetworkRequestFromJson(configJson);
-        mNetworkCallback = new NetworkCallback(NetworkCallback.EVENT_ALL);
+        mNetworkCallback = newNetworkCallback(NetworkCallback.EVENT_ALL);
         mManager.requestNetwork(networkRequest, mNetworkCallback);
         String key = mNetworkCallback.mId;
         mNetworkCallbackMap.put(key, mNetworkCallback);
         return key;
     }
 
+    /**
+     * Initiates a network request {@link NetworkRequest} using {@link WifiNetworkSpecifier}.
+     *
+     * @param wNs JSONObject Dictionary of wifi network specifier parameters
+     * @param timeoutInMs Timeout for the request. 0 indicates no timeout.
+     * @throws JSONException
+     * @throws GeneralSecurityException
+     */
+    @Rpc(description = "Initiates a network request using the provided network specifier")
+    public String connectivityRequestWifiNetwork(
+            @RpcParameter(name = "wifiNetworkSpecifier") JSONObject wNs,
+            @RpcParameter(name = "timeoutInMS") Integer timeoutInMs)
+            throws JSONException, GeneralSecurityException {
+        NetworkRequest networkRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI)
+                .setNetworkSpecifier(WifiManagerFacade.genWifiNetworkSpecifier(wNs))
+                .build();
+        mNetworkCallback = newNetworkCallback(NetworkCallback.EVENT_ALL);
+        if (timeoutInMs != 0) {
+            mManager.requestNetwork(networkRequest, mNetworkCallback, timeoutInMs);
+        } else {
+            mManager.requestNetwork(networkRequest, mNetworkCallback);
+        }
+        String key = mNetworkCallback.mId;
+        mNetworkCallbackMap.put(key, mNetworkCallback);
+        return key;
+    }
+
     @Rpc(description = "Stop listening for connectivity changes")
     public void connectivityStopTrackingConnectivityStateChange() {
         if (mTrackingConnectivityStateChange) {
@@ -601,6 +667,16 @@
         return mManager.getAllNetworkInfo();
     }
 
+    @Rpc(description = "Get connection status information about all network types supported by the device.")
+    public NetworkCapabilities[] connectivityNetworkGetAllCapabilities() {
+        Network[] networks = mManager.getAllNetworks();
+        NetworkCapabilities[] networkCapabilties = new NetworkCapabilities[networks.length];
+        for (int i = 0; i < networks.length; i++) {
+            networkCapabilties[i] = mManager.getNetworkCapabilities(networks[i]);
+        }
+        return networkCapabilties;
+    }
+
     @Rpc(description = "Check whether the active network is connected to the Internet.")
     public Boolean connectivityNetworkIsConnected() {
         NetworkInfo current = mManager.getActiveNetworkInfo();
@@ -1042,5 +1118,8 @@
     @Override
     public void shutdown() {
         connectivityStopTrackingConnectivityStateChange();
+        for (NetworkCallback networkCallback : mNetworkCallbackMap.values()) {
+            mManager.unregisterNetworkCallback(networkCallback);
+        }
     }
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/media/MediaSessionFacade.java b/Common/src/com/googlecode/android_scripting/facade/media/MediaSessionFacade.java
index ad16dc9..5b4e199 100644
--- a/Common/src/com/googlecode/android_scripting/facade/media/MediaSessionFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/media/MediaSessionFacade.java
@@ -16,19 +16,15 @@
 
 package com.googlecode.android_scripting.facade.media;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Callable;
-
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
+import android.media.session.MediaSession.Callback;
 import android.media.session.MediaSessionManager;
 import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
 import android.media.session.PlaybackState;
-import android.media.session.MediaSession.Callback;
 import android.view.KeyEvent;
 
 import com.googlecode.android_scripting.Log;
@@ -40,6 +36,10 @@
 import com.googlecode.android_scripting.rpc.RpcDefault;
 import com.googlecode.android_scripting.rpc.RpcParameter;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
 /**
  * Expose functionalities of MediaSession related classes
  * like MediaSession, MediaSessionManager, MediaController.
@@ -146,8 +146,8 @@
         }
         KeyEvent keyDown = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
         KeyEvent keyUp = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
-        mManager.dispatchMediaKeyEvent(keyDown);
-        mManager.dispatchMediaKeyEvent(keyUp);
+        mManager.dispatchMediaKeyEvent(keyDown, false);
+        mManager.dispatchMediaKeyEvent(keyUp, false);
     }
 
     private MediaController getMediaController(int idx) {
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/SmsFacade.java b/Common/src/com/googlecode/android_scripting/facade/telephony/SmsFacade.java
index fa9170a..1de1716 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/SmsFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/SmsFacade.java
@@ -741,7 +741,7 @@
 
     private PendingIntent createBroadcastPendingIntent(String intentAction, Uri messageUri) {
         Intent intent = new Intent(intentAction, messageUri);
-        return PendingIntent.getBroadcast(mService, 0, intent, 0);
+        return PendingIntent.getBroadcast(mService, 0, intent, PendingIntent.FLAG_MUTABLE);
     }
 
     private static String getSmsFailureReason(int resultCode) {
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java
index f09d0e2..e60e5f1 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java
@@ -805,7 +805,7 @@
         boolean wasAlwaysAllow = mTelephonyManager.isMobileDataPolicyEnabled(
                 TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED);
         mTelephonyManager.createForSubscriptionId(subId)
-                .setMobileDataPolicyEnabledStatus(
+                .setMobileDataPolicyEnabled(
                         TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, alwaysAllow);
         return wasAlwaysAllow == alwaysAllow;
     }
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 af07b3c..c690a38 100644
--- a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiAwareManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiAwareManagerFacade.java
@@ -882,6 +882,16 @@
             mResults.putString("messageAsString", new String(message));
             postEvent("WifiAwareSessionOnMessageReceived", mResults);
         }
+
+        @Override
+        public void onServiceLost(PeerHandle peerHandle, @WifiAwareManager.DiscoveryLostReasonCode
+                int reason) {
+            Bundle mResults = new Bundle();
+            mResults.putInt("discoverySessionId", mDiscoverySessionId);
+            mResults.putInt("peerId", peerHandle.peerId);
+            mResults.putInt("lostReason", reason);
+            postEvent("WifiAwareSessionOnServiceLost", mResults);
+        }
     }
 
     class WifiAwareRangingListener implements RttManager.RttListener {
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 c02a886..0b1318f 100755
--- a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiManagerFacade.java
@@ -16,10 +16,15 @@
 
 package com.googlecode.android_scripting.facade.wifi;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ;
+import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ;
 
 import static com.googlecode.android_scripting.jsonrpc.JsonBuilder.build;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Service;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -30,11 +35,13 @@
 import android.net.DhcpInfo;
 import android.net.MacAddress;
 import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.Uri;
+import android.net.wifi.CoexUnsafeChannel;
 import android.net.wifi.EasyConnectStatusCallback;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApCapability;
@@ -49,6 +56,7 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
 import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
+import android.net.wifi.WifiManager.SubsystemRestartTrackingCallback;
 import android.net.wifi.WifiManager.WifiLock;
 import android.net.wifi.WifiNetworkSpecifier;
 import android.net.wifi.WifiNetworkSuggestion;
@@ -67,8 +75,11 @@
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
+
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.modules.utils.build.SdkLevel;
 
 import com.googlecode.android_scripting.Log;
 import com.googlecode.android_scripting.facade.EventFacade;
@@ -101,8 +112,8 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
@@ -133,15 +144,17 @@
     private final WifiScanResultsReceiver mWifiScanResultsReceiver;
     private final WifiStateChangeReceiver mStateChangeReceiver;
     private final WifiNetworkSuggestionStateChangeReceiver mNetworkSuggestionStateChangeReceiver;
+    private SubsystemRestartTrackingCallbackFacade mSubsystemRestartTrackingCallback = null;
     private final HandlerThread mCallbackHandlerThread;
     private final Object mCallbackLock = new Object();
-    private final Map<NetworkSpecifier, NetworkCallback> mNetworkCallbacks = new HashMap<>();
     private boolean mTrackingWifiStateChange;
     private boolean mTrackingTetherStateChange;
     private boolean mTrackingNetworkSuggestionStateChange;
     @GuardedBy("mCallbackLock")
     private NetworkRequestUserSelectionCallback mNetworkRequestUserSelectionCallback;
     private final SparseArray<SoftApCallbackImp> mSoftapCallbacks;
+    // This is null if SdkLevel is not at least S
+    @Nullable private WifiManager.CoexCallback mCoexCallback;
 
     private final BroadcastReceiver mTetherStateReceiver = new BroadcastReceiver() {
         @Override
@@ -208,25 +221,6 @@
                 }
             };
 
-    private final class NetworkCallbackImpl extends NetworkCallback {
-        private static final String EVENT_TAG = mEventType + "NetworkCallback";
-
-        @Override
-        public void onAvailable(Network network) {
-            mEventFacade.postEvent(EVENT_TAG + "OnAvailable", mWifi.getConnectionInfo());
-        }
-
-        @Override
-        public void onUnavailable() {
-            mEventFacade.postEvent(EVENT_TAG + "OnUnavailable", null);
-        }
-
-        @Override
-        public void onLost(Network network) {
-            mEventFacade.postEvent(EVENT_TAG + "OnLost", null);
-        }
-    };
-
     private static class SoftApCallbackImp implements WifiManager.SoftApCallback {
         // A monotonic increasing counter for softap callback ids.
         private static int sCount = 0;
@@ -252,21 +246,36 @@
 
         @Override
         public void onConnectedClientsChanged(List<WifiClient> clients) {
-            ArrayList<String> macAddresses = new ArrayList<>();
-            clients.forEach(x -> macAddresses.add(x.getMacAddress().toString()));
+            ArrayList<MacAddress> macAddresses = new ArrayList<>();
+            clients.forEach(x -> macAddresses.add(x.getMacAddress()));
             Bundle msg = new Bundle();
             msg.putInt("NumClients", clients.size());
-            msg.putStringArrayList("MacAddresses", macAddresses);
+            msg.putParcelableArrayList("MacAddresses", macAddresses);
             mEventFacade.postEvent(mEventStr + "OnNumClientsChanged", msg);
             mEventFacade.postEvent(mEventStr + "OnConnectedClientsChanged", clients);
         }
 
         @Override
+        public void onConnectedClientsChanged(SoftApInfo info, List<WifiClient> clients) {
+            ArrayList<MacAddress> macAddresses = new ArrayList<>();
+            clients.forEach(x -> macAddresses.add(x.getMacAddress()));
+            Bundle msg = new Bundle();
+            msg.putParcelable("Info", info);
+            msg.putParcelableArrayList("ClientsMacAddress", macAddresses);
+            mEventFacade.postEvent(mEventStr + "OnConnectedClientsChangedWithInfo", msg);
+        }
+
+        @Override
         public void onInfoChanged(SoftApInfo softApInfo) {
             mEventFacade.postEvent(mEventStr + "OnInfoChanged", softApInfo);
         }
 
         @Override
+        public void onInfoChanged(List<SoftApInfo> infos) {
+            mEventFacade.postEvent(mEventStr + "OnInfoListChanged", infos);
+        }
+
+        @Override
         public void onCapabilityChanged(SoftApCapability softApCapability) {
             mEventFacade.postEvent(mEventStr + "OnCapabilityChanged", softApCapability);
         }
@@ -280,6 +289,31 @@
         }
     };
 
+    private static class CoexCallbackImpl extends WifiManager.CoexCallback {
+        private final EventFacade mEventFacade;
+        private final String mEventStr;
+
+        CoexCallbackImpl(EventFacade eventFacade) {
+            mEventFacade = eventFacade;
+            mEventStr = mEventType + "CoexCallback";
+        }
+
+        @Override
+        public void onCoexUnsafeChannelsChanged(
+                @NonNull List<CoexUnsafeChannel> unsafeChannels, int restrictions) {
+            Bundle event = new Bundle();
+            try {
+                event.putString("KEY_COEX_UNSAFE_CHANNELS",
+                        coexUnsafeChannelsToJson(unsafeChannels).toString());
+                event.putString("KEY_COEX_RESTRICTIONS",
+                        coexRestrictionsToJson(restrictions).toString());
+                mEventFacade.postEvent(mEventStr + "#onCoexUnsafeChannelsChanged", event);
+            } catch (JSONException e) {
+                Log.e("Failed to post event for onCoexUnsafeChannelsChanged: " + e);
+            }
+        }
+    };
+
     private WifiLock mLock = null;
     private boolean mIsConnected = false;
 
@@ -313,6 +347,9 @@
         mTrackingNetworkSuggestionStateChange = false;
         mCallbackHandlerThread.start();
         mSoftapCallbacks = new SparseArray<>();
+        if (SdkLevel.isAtLeastS()) {
+            mCoexCallback = new CoexCallbackImpl(mEventFacade);
+        }
     }
 
     private void makeLock(int wifiMode) {
@@ -395,6 +432,53 @@
     public class WifiStateChangeReceiver extends BroadcastReceiver {
         String mCachedWifiInfo = "";
 
+        /**
+         * When a peer to peer request is active, WifiManager.getConnectionInfo() returns
+         * the peer to peer connection details. Hence use networking API's to retrieve the
+         * internet connection details.
+         *
+         * But on Android R, we will need to fallback to the legacy getConnectionInfo() API since
+         * WifiInfo doesn't implement TransportInfo.
+         */
+        private WifiInfo getInternetConnectivityWifiInfo() {
+            if (!SdkLevel.isAtLeastS()) {
+                return mWifi.getConnectionInfo();
+            }
+            // TODO (b/156867433): We need a location sensitive synchronous API proposed
+            // in aosp/1629501.
+            final CountDownLatch waitForNetwork = new CountDownLatch(1);
+            final class AnswerBox {
+                public WifiInfo wifiInfo;
+            }
+            final AnswerBox answerBox = new AnswerBox();
+            final NetworkCallback networkCallback =
+                    new NetworkCallback(NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
+                @Override
+                public void onCapabilitiesChanged(@NonNull Network network,
+                        @NonNull NetworkCapabilities networkCapabilities) {
+                    answerBox.wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
+                    waitForNetwork.countDown();
+                }
+            };
+            mCm.registerNetworkCallback(
+                    new NetworkRequest.Builder()
+                            .addTransportType(TRANSPORT_WIFI)
+                            .addCapability(NET_CAPABILITY_INTERNET)
+                            .build(), networkCallback);
+            try {
+                if (!waitForNetwork.await(5, TimeUnit.SECONDS)) {
+                    Log.e("Timed out waiting for network to connect");
+                    return null;
+                }
+                return answerBox.wifiInfo;
+            } catch (InterruptedException e) {
+                Log.e("Waiting for onAvailable failed", e);
+                return null;
+            } finally {
+                mCm.unregisterNetworkCallback(networkCallback);
+            }
+        }
+
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -405,7 +489,12 @@
                 // If network info is of type wifi, send wifi events.
                 if (nInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                     if (nInfo.getDetailedState().equals(DetailedState.CONNECTED)) {
-                        WifiInfo wInfo = mWifi.getConnectionInfo();
+                        WifiInfo wInfo = getInternetConnectivityWifiInfo();
+                        if (wInfo == null) {
+                            Log.e("Failed to get WifiInfo for internet connection. "
+                                    + "Not sending wifi network connection event");
+                            return;
+                        }
                         String bssid = wInfo.getBSSID();
                         if (bssid != null && !mCachedWifiInfo.equals(wInfo.toString())) {
                             Log.d("WifiNetworkConnected");
@@ -519,28 +608,26 @@
             // Check if new security type SAE (WPA3) is present. Default to PSK
             if (j.has("security")) {
                 if (TextUtils.equals(j.getString("security"), "SAE")) {
-                    config.allowedKeyManagement.set(KeyMgmt.SAE);
-                    config.requirePmf = true;
+                    config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
                 } else {
-                    config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+                    config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
                 }
             } else {
-                config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
+                config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
             }
             config.preSharedKey = "\"" + j.getString("password") + "\"";
         } else if (j.has("preSharedKey")) {
-            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+            config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
             config.preSharedKey = j.getString("preSharedKey");
         } else {
             if (j.has("security")) {
                 if (TextUtils.equals(j.getString("security"), "OWE")) {
-                    config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
-                    config.requirePmf = true;
+                    config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
                 } else {
-                    config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+                    config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
                 }
             } else {
-                config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+                config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
             }
         }
         if (j.has("BSSID")) {
@@ -557,9 +644,7 @@
         }
         if (j.has("wepKeys")) {
             // Looks like we only support static WEP.
-            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
-            config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
-            config.allowedAuthAlgorithms.set(AuthAlgorithm.SHARED);
+            config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
             JSONArray keys = j.getJSONArray("wepKeys");
             String[] wepKeys = new String[keys.length()];
             for (int i = 0; i < keys.length(); i++) {
@@ -582,7 +667,7 @@
         return config;
     }
 
-    private WifiEnterpriseConfig genWifiEnterpriseConfig(JSONObject j) throws JSONException,
+    private static WifiEnterpriseConfig genWifiEnterpriseConfig(JSONObject j) throws JSONException,
             GeneralSecurityException {
         WifiEnterpriseConfig eConfig = new WifiEnterpriseConfig();
         if (j.has(WifiEnterpriseConfig.EAP_KEY)) {
@@ -605,7 +690,11 @@
             Log.v("Client Cert String is " + certStr);
             Log.v("Client Key String is " + keyStr);
             X509Certificate cert = strToX509Cert(certStr);
-            PrivateKey privKey = strToPrivateKey(keyStr);
+            String certAlgo = "RSA";
+            if (j.has("cert_algo")) {
+                certAlgo = j.getString("cert_algo");
+            }
+            PrivateKey privKey = strToPrivateKey(keyStr, certAlgo);
             Log.v("Cert is " + cert);
             Log.v("Private Key is " + privKey);
             eConfig.setClientKeyEntry(privKey, cert);
@@ -649,13 +738,11 @@
             return null;
         }
         WifiConfiguration config = new WifiConfiguration();
-        config.allowedKeyManagement.set(KeyMgmt.WPA_EAP);
-        config.allowedKeyManagement.set(KeyMgmt.IEEE8021X);
+        config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
 
         if (j.has("security")) {
             if (TextUtils.equals(j.getString("security"), "SUITE_B_192")) {
-                config.allowedKeyManagement.set(KeyMgmt.SUITE_B_192);
-                config.requirePmf = true;
+                config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
             }
         }
 
@@ -685,7 +772,10 @@
         return config;
     }
 
-    private NetworkSpecifier genWifiNetworkSpecifier(JSONObject j) throws JSONException,
+    /**
+     * Generate {@link WifiNetworkSpecifier} from the specified json.
+     */
+    public static NetworkSpecifier genWifiNetworkSpecifier(JSONObject j) throws JSONException,
             GeneralSecurityException {
         if (j == null) {
             return null;
@@ -794,6 +884,11 @@
                 }
             }
         }
+        if (j.has("enhancedMacRandomizationEnabled")
+                && j.getBoolean("enhancedMacRandomizationEnabled")) {
+            builder = builder.setMacRandomizationSetting(
+                WifiNetworkSuggestion.RANDOMIZATION_NON_PERSISTENT);
+        }
 
         return builder.build();
     }
@@ -837,22 +932,22 @@
         return info;
     }
 
-    private byte[] base64StrToBytes(String input) {
+    private static byte[] base64StrToBytes(String input) {
         return Base64.decode(input, Base64.DEFAULT);
     }
 
-    private X509Certificate strToX509Cert(String certStr) throws CertificateException {
+    private static X509Certificate strToX509Cert(String certStr) throws CertificateException {
         byte[] certBytes = base64StrToBytes(certStr);
         InputStream certStream = new ByteArrayInputStream(certBytes);
         CertificateFactory cf = CertificateFactory.getInstance("X509");
         return (X509Certificate) cf.generateCertificate(certStream);
     }
 
-    private PrivateKey strToPrivateKey(String key) throws NoSuchAlgorithmException,
-            InvalidKeySpecException {
+    private static PrivateKey strToPrivateKey(String key, String algo)
+            throws NoSuchAlgorithmException, InvalidKeySpecException {
         byte[] keyBytes = base64StrToBytes(key);
         PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
-        KeyFactory fact = KeyFactory.getInstance("RSA");
+        KeyFactory fact = KeyFactory.getInstance(algo);
         PrivateKey priv = fact.generatePrivate(keySpec);
         return priv;
     }
@@ -1083,6 +1178,27 @@
         }
     }
 
+    private class SubsystemRestartTrackingCallbackFacade extends SubsystemRestartTrackingCallback {
+        private final EventFacade mEventFacade;
+
+        SubsystemRestartTrackingCallbackFacade(EventFacade eventFacade) {
+            super();
+            mEventFacade = eventFacade;
+        }
+
+        @Override
+        public void onSubsystemRestarting() {
+            Log.v("onSubsystemRestarting");
+            mEventFacade.postEvent("WifiSubsystemRestarting", null);
+        }
+
+        @Override
+        public void onSubsystemRestarted() {
+            Log.v("onSubsystemRestarted");
+            mEventFacade.postEvent("WifiSubsystemRestarted", null);
+        }
+    }
+
     private OsuProvider buildTestOsuProvider(JSONObject config) {
         String osuServiceDescription = "Google Passpoint Test Service";
         List<Integer> osuMethodList =
@@ -1233,6 +1349,41 @@
         return mWifi.getConnectionInfo();
     }
 
+    /**
+     * Check if wifi network is temporary disabled.
+     * @param config JSONObject Dictionary of wifi connection parameters.
+     * @return True if network is disabled temporarily, False if not.
+     */
+    @Rpc(description = "Check if network is temporary disabled")
+    public boolean wifiIsNetworkTemporaryDisabledForNetwork(
+            @RpcParameter(name = "config") JSONObject config)
+                throws JSONException, GeneralSecurityException {
+        WifiConfiguration wifiConfig;
+        if (config.has(WifiEnterpriseConfig.EAP_KEY)) {
+            wifiConfig = genWifiConfigWithEnterpriseConfig(config);
+        } else {
+            wifiConfig = genWifiConfig(config);
+        }
+        List<WifiConfiguration> wifiConfigList = wifiGetConfiguredNetworks();
+        for (WifiConfiguration conf : wifiConfigList) {
+            if (conf.getSsidAndSecurityTypeString().equals(
+                    wifiConfig.getSsidAndSecurityTypeString())) {
+                Log.d("Found matching config in the configured networks.");
+                return conf.getNetworkSelectionStatus().isNetworkTemporaryDisabled();
+            }
+        }
+        Log.d("Wifi config is not in list of configured wifi networks.");
+        return false;
+    }
+
+    /**
+     * Get wifi standard for wifi connection.
+     */
+    @Rpc(description = "Return connection WiFi standard")
+    public Integer wifiGetConnectionStandard() {
+        return mWifi.getConnectionInfo().getWifiStandard();
+    }
+
     @Rpc(description = "Returns wifi activity and energy usage info.")
     public WifiActivityEnergyInfo wifiGetControllerActivityEnergyInfo() {
         WifiActivityEnergyInfo[] mutable = {null};
@@ -1296,14 +1447,51 @@
         return mWifi.isVerboseLoggingEnabled() ? 1 : 0;
     }
 
+    /**
+     * Query whether or not the device supports concurrency of Station (STA) + multiple access
+     * points (AP) (where the APs bridged together).
+     *
+     * @return true if this device supports concurrency of STA + multiple APs which are bridged
+     *         together, false otherwise.
+     */
+    @Rpc(description = "true if this adapter supports STA + bridged Soft AP concurrency.")
+    public Boolean wifiIsStaBridgedApConcurrencySupported() {
+        return mWifi.isStaBridgedApConcurrencySupported();
+    }
+
+    /**
+     * Query whether or not the device supports multiple Access point (AP) which are bridged
+     * together.
+     *
+     * @return true if this device supports concurrency of multiple AP which bridged together,
+     *         false otherwise.
+     */
+    @Rpc(description = "true if this adapter supports bridged Soft AP concurrency.")
+    public Boolean wifiIsBridgedApConcurrencySupported() {
+        return mWifi.isBridgedApConcurrencySupported();
+    }
+
     @Rpc(description = "true if this adapter supports 5 GHz band.")
     public Boolean wifiIs5GHzBandSupported() {
         return mWifi.is5GHzBandSupported();
     }
 
-    @Rpc(description = "true if this adapter supports multiple simultaneous connections.")
-    public Boolean wifiIsAdditionalStaSupported() {
-        return mWifi.isAdditionalStaSupported();
+    @Rpc(description = "true if this adapter supports multiple simultaneous connections for"
+            + "local only use-case.")
+    public Boolean wifiIsStaConcurrencyForLocalOnlyConnectionsSupported() {
+        return mWifi.isStaConcurrencyForLocalOnlyConnectionsSupported();
+    }
+
+    @Rpc(description = "true if this adapter supports multiple simultaneous connections for mbb "
+            + "wifi to wifi switching.")
+    public Boolean wifiIsMakeBeforeBreakWifiSwitchingSupported() {
+        return mWifi.isMakeBeforeBreakWifiSwitchingSupported();
+    }
+
+    @Rpc(description = "true if this adapter supports multiple simultaneous connections for "
+            + "restricted connection use-case.")
+    public Boolean wifiIsStaConcurrencyForRestrictedConnectionsSupported() {
+        return mWifi.isStaConcurrencyForRestrictedConnectionsSupported();
     }
 
     @Rpc(description = "Check if the chipset supports a certain Wi-Fi standard.", returns = "true if standard is supported")
@@ -1418,6 +1606,17 @@
     public Boolean wifiIsEnhancedOpenSupported() {
         return mWifi.isEnhancedOpenSupported();
     }
+
+    /**
+     * @return true if this device supports Wi-Fi Device Provisioning Protocol (Easy-connect)
+     * Enrollee Responder mode
+     */
+    @Rpc(description = "Check if Easy Connect (DPP) Enrollee responder mode is supported "
+            + "on this device.")
+    public Boolean wifiIsEasyConnectEnrolleeResponderModeSupported() {
+        return mWifi.isEasyConnectEnrolleeResponderModeSupported();
+    }
+
     /**
      * @return true if this device supports Wi-Fi Device Provisioning Protocol (Easy-connect)
      */
@@ -1469,6 +1668,30 @@
         return mWifi.removeNetwork(netId);
     }
 
+    private int getApBandFromChannelFrequency(int freq) {
+        if (ScanResult.is24GHz(freq)) {
+            return SoftApConfiguration.BAND_2GHZ;
+        } else if (ScanResult.is5GHz(freq)) {
+            return SoftApConfiguration.BAND_5GHZ;
+        } else if (ScanResult.is6GHz(freq)) {
+            return SoftApConfiguration.BAND_6GHZ;
+        } else if (ScanResult.is60GHz(freq)) {
+            return SoftApConfiguration.BAND_60GHZ;
+        }
+        return -1;
+    }
+
+    private int[] convertJSONArrayToIntArray(JSONArray jArray) throws JSONException {
+        if (jArray == null) {
+            return null;
+        }
+        int[] iArray = new int[jArray.length()];
+        for (int i = 0; i < jArray.length(); i++) {
+            iArray[i] = jArray.getInt(i);
+        }
+        return iArray;
+    }
+
     private SoftApConfiguration createSoftApConfiguration(JSONObject configJson)
             throws JSONException {
         if (configJson == null) {
@@ -1539,10 +1762,48 @@
             for (int j = 0; j < blockedList.length(); j++) {
                 blockedClientList.add(MacAddress.fromString(blockedList.getString(j)));
             }
-
         }
+
         configBuilder.setAllowedClientList(allowedClientList);
         configBuilder.setBlockedClientList(blockedClientList);
+
+        if (SdkLevel.isAtLeastS()) {
+            if (configJson.has("apBands")) {
+                JSONArray jBands = configJson.getJSONArray("apBands");
+                int[] bands = convertJSONArrayToIntArray(jBands);
+                configBuilder.setBands(bands);
+            }
+
+            if (configJson.has("apChannelFrequencies")) {
+                JSONArray jChannelFrequencys = configJson.getJSONArray("apChannelFrequencies");
+                int[] channelFrequencies = convertJSONArrayToIntArray(jChannelFrequencys);
+                SparseIntArray channels = new SparseIntArray();
+                for (int channelFrequency : channelFrequencies) {
+                    if (channelFrequency != 0) {
+                        channels.put(getApBandFromChannelFrequency(channelFrequency),
+                                ScanResult.convertFrequencyMhzToChannelIfSupported(
+                                        channelFrequency));
+                    }
+                }
+                if (channels.size() != 0) {
+                    configBuilder.setChannels(channels);
+                }
+            }
+
+            if (configJson.has("MacRandomizationSetting")) {
+                configBuilder.setMacRandomizationSetting(
+                        configJson.getInt("MacRandomizationSetting"));
+            }
+
+            if (configJson.has("BridgedModeOpportunisticShutdownEnabled")) {
+                configBuilder.setBridgedModeOpportunisticShutdownEnabled(
+                        configJson.getBoolean("BridgedModeOpportunisticShutdownEnabled"));
+            }
+
+            if (configJson.has("Ieee80211axEnabled")) {
+                configBuilder.setIeee80211axEnabled(configJson.getBoolean("Ieee80211axEnabled"));
+            }
+        }
         return configBuilder.build();
     }
 
@@ -1699,6 +1960,16 @@
         return enabled;
     }
 
+    @Rpc(description = "Restart the WiFi subsystem.")
+    public void restartWifiSubsystem() {
+        if (mSubsystemRestartTrackingCallback == null) {
+            // one-time registration if needed
+            mSubsystemRestartTrackingCallback = new SubsystemRestartTrackingCallbackFacade(
+                    mEventFacade);
+        }
+        mWifi.restartWifiSubsystem();
+    }
+
     @Rpc(description = "Toggle Wifi scan always available on and off.", returns = "True if Wifi scan is always available.")
     public Boolean wifiToggleScanAlwaysAvailable(
             @RpcParameter(name = "enabled") @RpcOptional Boolean enabled)
@@ -1714,91 +1985,6 @@
         mWifi.allowAutojoinGlobal(enable);
     }
 
-    private void wifiRequestNetworkWithSpecifierInternal(NetworkSpecifier wns, int timeoutInMs)
-            throws GeneralSecurityException {
-        NetworkRequest networkRequest = new NetworkRequest.Builder()
-                .addTransportType(TRANSPORT_WIFI)
-                .setNetworkSpecifier(wns)
-                .build();
-        NetworkCallback networkCallback = new NetworkCallbackImpl();
-        if (timeoutInMs != 0) {
-            mCm.requestNetwork(networkRequest, networkCallback,
-                    new Handler(mCallbackHandlerThread.getLooper()), timeoutInMs);
-        } else {
-            mCm.requestNetwork(networkRequest, networkCallback,
-                    new Handler(mCallbackHandlerThread.getLooper()));
-        }
-        // Store the callback for release later.
-        mNetworkCallbacks.put(wns, networkCallback);
-    }
-
-    /**
-     * Initiates a network request {@link NetworkRequest} using {@link WifiNetworkSpecifier}.
-     *
-     * @param wifiNetworkSpecifier JSONObject Dictionary of wifi network specifier parameters
-     * @throws JSONException
-     * @throws GeneralSecurityException
-     */
-    @Rpc(description = "Initiates a network request using the provided network specifier")
-    public void wifiRequestNetworkWithSpecifier(
-            @RpcParameter(name = "wifiNetworkSpecifier") JSONObject wifiNetworkSpecifier)
-            throws JSONException, GeneralSecurityException {
-        wifiRequestNetworkWithSpecifierInternal(genWifiNetworkSpecifier(wifiNetworkSpecifier), 0);
-    }
-
-    /**
-     * Initiates a network request {@link NetworkRequest} using {@link WifiNetworkSpecifier}.
-     *
-     * @param wifiNetworkSpecifier JSONObject Dictionary of wifi network specifier parameters
-     * @param timeoutInMs Timeout for the request.
-     * @throws JSONException
-     * @throws GeneralSecurityException
-     */
-    @Rpc(description = "Initiates a network request using the provided network specifier")
-    public void wifiRequestNetworkWithSpecifierWithTimeout(
-            @RpcParameter(name = "wifiNetworkSpecifier") JSONObject wifiNetworkSpecifier,
-            @RpcParameter(name = "timeout") Integer timeoutInMs)
-            throws JSONException, GeneralSecurityException {
-        wifiRequestNetworkWithSpecifierInternal(
-                genWifiNetworkSpecifier(wifiNetworkSpecifier), timeoutInMs);
-    }
-
-    /**
-     * Releases network request using {@link WifiNetworkSpecifier}.
-     *
-     * @throws JSONException
-     * @throws GeneralSecurityException
-     */
-    @Rpc(description = "Releases network request corresponding to the network specifier")
-    public void wifiReleaseNetwork(
-            @RpcParameter(name = "wifiNetworkSpecifier") JSONObject wifiNetworkSpecifier)
-            throws JSONException, GeneralSecurityException {
-        NetworkSpecifier wns = genWifiNetworkSpecifier(wifiNetworkSpecifier);
-        NetworkCallback networkCallback = mNetworkCallbacks.remove(wns);
-        if (networkCallback == null) {
-            throw new IllegalArgumentException("network callback is null");
-        }
-        mCm.unregisterNetworkCallback(networkCallback);
-    }
-
-    /**
-     * Releases all pending network requests.
-     *
-     * @throws JSONException
-     * @throws GeneralSecurityException
-     */
-    @Rpc(description = "Releases all pending network requests")
-    public void wifiReleaseNetworkAll() {
-        Iterator<Map.Entry<NetworkSpecifier, NetworkCallback>> it =
-                mNetworkCallbacks.entrySet().iterator();
-        while (it.hasNext()) {
-            Map.Entry<NetworkSpecifier, NetworkCallback> entry = it.next();
-            NetworkCallback networkCallback = entry.getValue();
-            it.remove();
-            mCm.unregisterNetworkCallback(networkCallback);
-        }
-    }
-
     /**
      * Register network request match callback to simulate the UI flow.
      *
@@ -1889,7 +2075,6 @@
 
     @Override
     public void shutdown() {
-        wifiReleaseNetworkAll();
         wifiLockRelease();
         if (mTrackingWifiStateChange == true) {
             wifiStopTrackingStateChange();
@@ -1989,6 +2174,36 @@
             Log.d("Posting event: onProgress");
             mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg);
         }
+
+        @Override
+        public void onBootstrapUriGenerated(@NonNull Uri dppUri) {
+            Bundle msg = new Bundle();
+            msg.putString("Type", "onBootstrapUriGenerated");
+            Log.d("onBootstrapUriGenerated uri: " + dppUri.toString());
+            msg.putString("generatedUri", dppUri.toString());
+            Log.d("Posting event: onBootstrapUriGenerated");
+            mEventFacade.postEvent(EASY_CONNECT_CALLBACK_TAG, msg);
+        }
+    }
+
+    private static @WifiManager.EasyConnectCryptographyCurve
+            int getEasyConnectCryptographyCurve(String curve) {
+
+        switch (curve) {
+            case "secp384r1":
+                return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1;
+            case "secp521r1":
+                return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1;
+            case "brainpoolP256r1":
+                return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1;
+            case "brainpoolP384r1":
+                return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1;
+            case "brainpoolP512r1":
+                return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1;
+            case "prime256v1":
+            default:
+                return WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1;
+        }
     }
 
     /**
@@ -2032,6 +2247,24 @@
     }
 
     /**
+     * Start Easy Connect (DPP) in Responder-Enrollee role: Receive Wi-Fi configuration from a peer
+     *
+     * @param deviceInfo The device specific info to attach in the generated URI
+     * @param cryptographyCurve Elliptic curve cryptography used to generate DPP
+     *        public/private key pair
+     */
+    @Rpc(description = "Easy Connect Responder-Enrollee: Receive Wi-Fi configuration from peer")
+    public void startEasyConnectAsEnrolleeResponder(@RpcParameter(name = "deviceInfo") String
+            deviceInfo, @RpcParameter(name = "cryptographyCurve") String cryptographyCurve) {
+        EasyConnectCallback dppStatusCallback = new EasyConnectCallback();
+
+        // Start Easy Connect
+        mWifi.startEasyConnectAsEnrolleeResponder(deviceInfo,
+                getEasyConnectCryptographyCurve(cryptographyCurve), mService.getMainExecutor(),
+                dppStatusCallback);
+    }
+
+    /**
      * Stop Easy Connect (DPP) session
      *
      */
@@ -2058,4 +2291,165 @@
             @RpcParameter(name = "enableAutojoin") Boolean enableAutojoin) {
         mWifi.allowAutojoinPasspoint(fqdn, enableAutojoin);
     }
+
+    private static CoexUnsafeChannel genCoexUnsafeChannel(JSONObject j) throws JSONException {
+        if (j == null || !j.has("band") || !j.has("channel")) {
+            return null;
+        }
+
+        final int band;
+        final String jsonBand = j.getString("band");
+        if (TextUtils.equals(jsonBand, "24_GHZ")) {
+            band = WIFI_BAND_24_GHZ;
+        } else if (TextUtils.equals(jsonBand, "5_GHZ")) {
+            band = WIFI_BAND_5_GHZ;
+        } else {
+            return null;
+        }
+        if (j.has("powerCapDbm")) {
+            return new CoexUnsafeChannel(band, j.getInt("channel"), j.getInt("powerCapDbm"));
+        }
+        return new CoexUnsafeChannel(band, j.getInt("channel"));
+    }
+
+    private static List<CoexUnsafeChannel> genCoexUnsafeChannels(
+            JSONArray jsonCoexUnsafeChannelsArray) throws JSONException {
+        if (jsonCoexUnsafeChannelsArray == null) {
+            return Collections.emptyList();
+        }
+        List<CoexUnsafeChannel> unsafeChannels = new ArrayList<>();
+        for (int i = 0; i < jsonCoexUnsafeChannelsArray.length(); i++) {
+            unsafeChannels.add(
+                    genCoexUnsafeChannel(jsonCoexUnsafeChannelsArray.getJSONObject(i)));
+        }
+        return unsafeChannels;
+    }
+
+    private static int genCoexRestrictions(JSONArray jsonCoexRestrictionArray)
+            throws JSONException {
+        if (jsonCoexRestrictionArray == null) {
+            return 0;
+        }
+        int coexRestrictions = 0;
+        for (int i = 0; i < jsonCoexRestrictionArray.length(); i++) {
+            final String jsonRestriction = jsonCoexRestrictionArray.getString(i);
+            if (TextUtils.equals(jsonRestriction, "WIFI_DIRECT")) {
+                coexRestrictions |= WifiManager.COEX_RESTRICTION_WIFI_DIRECT;
+            }
+            if (TextUtils.equals(jsonRestriction, "SOFTAP")) {
+                coexRestrictions |= WifiManager.COEX_RESTRICTION_SOFTAP;
+            }
+            if (TextUtils.equals(jsonRestriction, "WIFI_AWARE")) {
+                coexRestrictions |= WifiManager.COEX_RESTRICTION_WIFI_AWARE;
+            }
+        }
+        return coexRestrictions;
+    }
+
+    /**
+     * Converts a set of {@link CoexUnsafeChannel} to a {@link JSONArray} of {@link JSONObject} of
+     * format:
+     *     {
+     *         "band": <"24_GHZ" or "5_GHZ">
+     *         "channel" : <Channel Number>
+     *         (Optional) "powerCapDbm" : <Power Cap in Dbm>
+     *     }
+     */
+    private static JSONArray coexUnsafeChannelsToJson(List<CoexUnsafeChannel> unsafeChannels)
+            throws JSONException {
+        final JSONArray jsonCoexUnsafeChannelArray = new JSONArray();
+        for (CoexUnsafeChannel unsafeChannel : unsafeChannels) {
+            final String jsonBand;
+            if (unsafeChannel.getBand() == WIFI_BAND_24_GHZ) {
+                jsonBand = "24_GHZ";
+            } else if (unsafeChannel.getBand() == WIFI_BAND_5_GHZ) {
+                jsonBand = "5_GHZ";
+            } else {
+                continue;
+            }
+            final JSONObject jsonUnsafeChannel = new JSONObject();
+            jsonUnsafeChannel.put("band", jsonBand);
+            jsonUnsafeChannel.put("channel", unsafeChannel.getChannel());
+            final int powerCapDbm = unsafeChannel.getPowerCapDbm();
+            if (powerCapDbm != CoexUnsafeChannel.POWER_CAP_NONE) {
+                jsonUnsafeChannel.put("powerCapDbm", powerCapDbm);
+            }
+            jsonCoexUnsafeChannelArray.put(jsonUnsafeChannel);
+        }
+        return jsonCoexUnsafeChannelArray;
+    }
+
+    /**
+     * Converts a coex restriction bitmask {@link WifiManager#getCoexRestrictions()} to a JSON array
+     * of possible values "WIFI_DIRECT", "SOFTAP", "WIFI_AWARE".
+     */
+    private static JSONArray coexRestrictionsToJson(int coexRestrictions) {
+        final JSONArray jsonCoexRestrictionArray = new JSONArray();
+        if ((coexRestrictions & WifiManager.COEX_RESTRICTION_WIFI_DIRECT) != 0) {
+            jsonCoexRestrictionArray.put("WIFI_DIRECT");
+        }
+        if ((coexRestrictions & WifiManager.COEX_RESTRICTION_SOFTAP) != 0) {
+            jsonCoexRestrictionArray.put("SOFTAP");
+        }
+        if ((coexRestrictions & WifiManager.COEX_RESTRICTION_WIFI_AWARE) != 0) {
+            jsonCoexRestrictionArray.put("WIFI_AWARE");
+        }
+        return jsonCoexRestrictionArray;
+    }
+
+    /**
+     * Returns whether the default coex algorithm is enabled or not
+     *
+     * @return {@code true} if the default coex algorithm is enabled, {@code false} otherwise.
+     */
+    @Rpc(description = "Returns whether the default coex algorithm is enabled or not")
+    public boolean wifiIsDefaultCoexAlgorithmEnabled() {
+        if (!SdkLevel.isAtLeastS()) {
+            return false;
+        }
+        return mWifi.isDefaultCoexAlgorithmEnabled();
+    }
+
+    /**
+     * Sets the active list of unsafe channels to avoid for coex and the restricted Wifi interfaces.
+     *
+     * @param unsafeChannels JSONArray representation of {@link CoexUnsafeChannel}.
+     *                       See {@link #coexUnsafeChannelsToJson(List)}.
+     * @param restrictions JSONArray representation of coex restrictions.
+     *                     See {@link #coexRestrictionsToJson(int)}.
+     * @throws JSONException
+     */
+    @Rpc(description = "Set the unsafe channels to avoid for coex")
+    public void wifiSetCoexUnsafeChannels(
+            @RpcParameter(name = "unsafeChannels") JSONArray unsafeChannels,
+            @RpcParameter(name = "restrictions") JSONArray restrictions) throws JSONException {
+        if (!SdkLevel.isAtLeastS()) {
+            return;
+        }
+        mWifi.setCoexUnsafeChannels(
+                genCoexUnsafeChannels(unsafeChannels), genCoexRestrictions(restrictions));
+    }
+
+    /**
+     * Registers a coex callback to start receiving coex update events.
+     */
+    @Rpc(description = "Registers a coex callback to start receiving coex update events")
+    public void wifiRegisterCoexCallback() {
+        if (!SdkLevel.isAtLeastS()) {
+            return;
+        }
+        mWifi.registerCoexCallback(
+                new HandlerExecutor(mCallbackHandlerThread.getThreadHandler()), mCoexCallback);
+    }
+
+    /**
+     * Unregisters the coex callback to stop receiving coex update events.
+     */
+    @Rpc(description = "Unregisters the coex callback to stop receiving coex update events")
+    public void wifiUnregisterCoexCallback() {
+        if (!SdkLevel.isAtLeastS()) {
+            return;
+        }
+        mWifi.unregisterCoexCallback(mCoexCallback);
+    }
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiRtt2ManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiRtt2ManagerFacade.java
index 5197896..7c1cc00 100644
--- a/Common/src/com/googlecode/android_scripting/facade/wifi/WifiRtt2ManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/wifi/WifiRtt2ManagerFacade.java
@@ -79,7 +79,7 @@
 
     @Override
     public void shutdown() {
-        // empty
+        mService.unregisterReceiver(mStateChangedReceiver);
     }
 
     @Rpc(description = "Does the device support the Wi-Fi RTT feature?")
diff --git a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
index 99f45d8..739028c 100644
--- a/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
+++ b/Common/src/com/googlecode/android_scripting/jsonrpc/JsonBuilder.java
@@ -36,6 +36,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
@@ -49,8 +50,10 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiScanner;
 import android.net.wifi.WifiScanner.ScanData;
 import android.net.wifi.WpsInfo;
+import android.net.wifi.aware.WifiAwareNetworkInfo;
 import android.net.wifi.p2p.WifiP2pConfig;
 import android.net.wifi.p2p.WifiP2pDevice;
 import android.net.wifi.p2p.WifiP2pGroup;
@@ -93,12 +96,15 @@
 import android.util.Base64;
 import android.util.DisplayMetrics;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import com.android.internal.net.LegacyVpnInfo;
+import com.android.modules.utils.build.SdkLevel;
 
 import com.googlecode.android_scripting.ConvertUtils;
 import com.googlecode.android_scripting.Log;
 import com.googlecode.android_scripting.event.Event;
+import com.googlecode.android_scripting.facade.ConnectivityConstants;
 import com.googlecode.android_scripting.facade.DataUsageController.DataUsageInfo;
 import com.googlecode.android_scripting.facade.telephony.InCallServiceImpl;
 import com.googlecode.android_scripting.facade.telephony.TelephonyConstants;
@@ -239,6 +245,9 @@
         if (data instanceof Network) {
             return buildNetwork((Network) data);
         }
+        if (data instanceof NetworkCapabilities) {
+            return buildNetworkCapabilities((NetworkCapabilities) data);
+        }
         if (data instanceof NetworkInfo) {
             return buildNetworkInfo((NetworkInfo) data);
         }
@@ -929,6 +938,48 @@
         return nw;
     }
 
+    public static JSONObject buildNetworkCapabilities(JSONObject nc, NetworkCapabilities data)
+            throws JSONException {
+        nc.put(ConnectivityConstants.NetworkCallbackContainer.RSSI,
+                data.getSignalStrength());
+        nc.put(ConnectivityConstants.NetworkCallbackContainer.METERED,
+                !data.hasCapability(ConnectivityConstants.NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+        nc.put(ConnectivityConstants.NET_CAPABILITIES_TRANSPORT_TYPE,
+                new JSONArray(data.getTransportTypes()));
+        nc.put(ConnectivityConstants.NET_CAPABILITIES_CAPABILITIES,
+                new JSONArray(data.getCapabilities()));
+
+        if (data.getNetworkSpecifier() != null) {
+            nc.put("network_specifier",
+                    data.getNetworkSpecifier().toString());
+        }
+        if (data.getTransportInfo() != null) {
+            nc.put("transport_info",
+                    JsonBuilder.build(data.getTransportInfo()));
+            if (data.getTransportInfo() instanceof WifiAwareNetworkInfo) {
+                WifiAwareNetworkInfo anc =
+                        (WifiAwareNetworkInfo) data.getTransportInfo();
+
+                String ipv6 = anc.getPeerIpv6Addr().toString();
+                if (ipv6.charAt(0) == '/') {
+                    ipv6 = ipv6.substring(1);
+                }
+                nc.put("aware_ipv6", ipv6);
+                if (anc.getPort() != 0) {
+                    nc.put("aware_port", anc.getPort());
+                }
+                if (anc.getTransportProtocol() != -1) {
+                    nc.put("aware_transport_protocol", anc.getTransportProtocol());
+                }
+            }
+        }
+        return nc;
+    }
+
+    private static Object buildNetworkCapabilities(NetworkCapabilities data) throws JSONException {
+        return buildNetworkCapabilities(new JSONObject(), data);
+    }
+
     private static Object buildNetworkInfo(NetworkInfo data)
             throws JSONException {
         JSONObject info = new JSONObject();
@@ -1057,6 +1108,22 @@
         return result;
     }
 
+    private static @WifiScanner.WifiBand int apBand2wifiScannerBand(
+            @SoftApConfiguration.BandType int band) {
+        switch(band) {
+            case SoftApConfiguration.BAND_2GHZ:
+                return WifiScanner.WIFI_BAND_24_GHZ;
+            case SoftApConfiguration.BAND_5GHZ:
+                return WifiScanner.WIFI_BAND_5_GHZ;
+            case SoftApConfiguration.BAND_6GHZ:
+                return WifiScanner.WIFI_BAND_6_GHZ;
+            case SoftApConfiguration.BAND_60GHZ:
+                return WifiScanner.WIFI_BAND_60_GHZ;
+            default:
+                return WifiScanner.WIFI_BAND_UNSPECIFIED;
+        }
+    }
+
     private static Object buildSoftApConfiguration(SoftApConfiguration data)
             throws JSONException {
         JSONObject config = new JSONObject();
@@ -1084,6 +1151,26 @@
         config.put("ClientControlByUserEnabled", data.isClientControlByUserEnabled());
         config.put("AllowedClientList", build(data.getAllowedClientList()));
         config.put("BlockedClientList", build(data.getBlockedClientList()));
+        if (SdkLevel.isAtLeastS()) {
+            config.put("apBands", buildJSONArray(
+                    IntStream.of(data.getBands()).boxed().toArray(Integer[]::new)));
+            SparseIntArray channels = data.getChannels();
+            int[] channelFrequencies = new int[channels.size()];
+            for (int i = 0; i < channels.size(); i++) {
+                int channel = channels.valueAt(i);
+                channelFrequencies[i] = channel == 0 ? 0
+                        : ScanResult.convertChannelToFrequencyMhzIfSupported(
+                        channel, apBand2wifiScannerBand(channels.keyAt(i)));
+            }
+            if (channelFrequencies.length != 0) {
+                config.put("apChannelFrequencies", build(
+                        IntStream.of(channelFrequencies).boxed().toArray(Integer[]::new)));
+            }
+            config.put("MacRandomizationSetting", build(data.getMacRandomizationSetting()));
+            config.put("BridgedModeOpportunisticShutdownEnabled",
+                    build(data.isBridgedModeOpportunisticShutdownEnabled()));
+            config.put("Ieee80211axEnabled", build(data.isIeee80211axEnabled()));
+        }
         return config;
     }
 
@@ -1222,6 +1309,29 @@
                 SoftApCapability.SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT));
         info.put("wpa3SaeSupported", data.areFeaturesSupported(
                 SoftApCapability.SOFTAP_FEATURE_WPA3_SAE));
+        info.put("ieee80211axSupported", data.areFeaturesSupported(
+                SoftApCapability.SOFTAP_FEATURE_IEEE80211_AX));
+        info.put("24gSupported", data.areFeaturesSupported(
+                SoftApCapability.SOFTAP_FEATURE_BAND_24G_SUPPORTED));
+        info.put("5gSupported", data.areFeaturesSupported(
+                SoftApCapability.SOFTAP_FEATURE_BAND_5G_SUPPORTED));
+        info.put("6gSupported", data.areFeaturesSupported(
+                SoftApCapability.SOFTAP_FEATURE_BAND_6G_SUPPORTED));
+        info.put("60gSupported", data.areFeaturesSupported(
+                SoftApCapability.SOFTAP_FEATURE_BAND_60G_SUPPORTED));
+        info.put("supported2GHzChannellist", build(
+                IntStream.of(data.getSupportedChannelList(SoftApConfiguration.BAND_2GHZ))
+                .boxed().toArray(Integer[]::new)));
+
+        info.put("supported5GHzChannellist", build(
+                IntStream.of(data.getSupportedChannelList(SoftApConfiguration.BAND_5GHZ))
+                .boxed().toArray(Integer[]::new)));
+        info.put("supported6GHzChannellist", build(
+                IntStream.of(data.getSupportedChannelList(SoftApConfiguration.BAND_6GHZ))
+                .boxed().toArray(Integer[]::new)));
+        info.put("supported60GHzChannellist", build(
+                IntStream.of(data.getSupportedChannelList(SoftApConfiguration.BAND_60GHZ))
+                .boxed().toArray(Integer[]::new)));
         return info;
     }
 
@@ -1231,6 +1341,11 @@
         Log.d("build softAp info.");
         info.put("frequency", data.getFrequency());
         info.put("bandwidth", data.getBandwidth());
+        info.put("autoShutdownTimeoutMillis", data.getAutoShutdownTimeoutMillis());
+        if (SdkLevel.isAtLeastS()) {
+            info.put("wifiStandard", data.getWifiStandard());
+            info.put("bssid", data.getBssid());
+        }
         return info;
     }
 
diff --git a/ScriptingLayerForAndroid/Android.bp b/ScriptingLayerForAndroid/Android.bp
index 569cdcf..46797e7 100644
--- a/ScriptingLayerForAndroid/Android.bp
+++ b/ScriptingLayerForAndroid/Android.bp
@@ -66,6 +66,9 @@
     },
 
     jni_libs: ["libcom_googlecode_android_scripting_Exec"],
+
+    min_sdk_version: "30",
+    target_sdk_version: "31",
 }
 
 java_import {
diff --git a/ScriptingLayerForAndroid/AndroidManifest.xml b/ScriptingLayerForAndroid/AndroidManifest.xml
index c5f8ec6..4b19943 100644
--- a/ScriptingLayerForAndroid/AndroidManifest.xml
+++ b/ScriptingLayerForAndroid/AndroidManifest.xml
@@ -126,7 +126,7 @@
         android:name=".Sl4aApplication"
         android:theme="@android:style/Theme.DeviceDefault"
         android:usesCleartextTraffic="true">
-        <activity android:name=".activity.ScriptManager" android:configChanges="keyboardHidden|orientation" android:windowSoftInputMode="adjustResize" android:launchMode="singleTop">
+        <activity android:name=".activity.ScriptManager" android:configChanges="keyboardHidden|orientation" android:windowSoftInputMode="adjustResize" android:launchMode="singleTop" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -136,7 +136,7 @@
             </intent-filter>
             <meta-data android:name="android.app.searchable" android:resource="@xml/searchable_scripts" />
         </activity>
-        <activity android:name=".activity.ScriptPicker" android:configChanges="keyboardHidden|orientation" android:label="Scripts">
+        <activity android:name=".activity.ScriptPicker" android:configChanges="keyboardHidden|orientation" android:label="Scripts" android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.CREATE_SHORTCUT" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -147,31 +147,36 @@
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <activity android:name=".activity.InterpreterPicker" android:configChanges="keyboardHidden|orientation" android:label="Interpreters">
+        <activity android:name=".activity.InterpreterPicker" android:configChanges="keyboardHidden|orientation" android:label="Interpreters"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.CREATE_SHORTCUT" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <activity-alias android:name="LocalePlugin" android:targetActivity=".activity.ScriptPicker" android:label="@string/application_title" android:icon="@drawable/sl4a_logo_32">
+        <activity-alias android:name="LocalePlugin" android:targetActivity=".activity.ScriptPicker" android:label="@string/application_title" android:icon="@drawable/sl4a_logo_32"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
             </intent-filter>
         </activity-alias>
-        <receiver android:name=".locale.LocaleReceiver">
+        <receiver android:name=".locale.LocaleReceiver"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING" />
             </intent-filter>
         </receiver>
         <activity android:name=".activity.Preferences" android:theme="@android:style/Theme.DeviceDefault.Settings" />
         <activity android:name="org.connectbot.ConsoleActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:configChanges="keyboardHidden|orientation" android:windowSoftInputMode="stateAlwaysVisible|adjustResize" android:finishOnTaskLaunch="true" android:launchMode="singleTask" />
-        <activity android:name=".activity.ScriptEditor" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:configChanges="keyboardHidden|orientation" android:windowSoftInputMode="stateAlwaysVisible|adjustResize">
+        <activity android:name=".activity.ScriptEditor" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:configChanges="keyboardHidden|orientation" android:windowSoftInputMode="stateAlwaysVisible|adjustResize"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.googlecode.android_scripting.action.EDIT_SCRIPT" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
-        <activity android:name=".activity.ApiBrowser" android:configChanges="keyboardHidden|orientation" android:launchMode="singleTop" android:windowSoftInputMode="adjustResize">
+        <activity android:name=".activity.ApiBrowser" android:configChanges="keyboardHidden|orientation" android:launchMode="singleTop" android:windowSoftInputMode="adjustResize"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.SEARCH" />
             </intent-filter>
@@ -180,7 +185,8 @@
         <activity android:name=".activity.ApiPrompt" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:configChanges="keyboardHidden|orientation" />
         <activity android:name=".activity.TriggerManager" android:launchMode="singleTask" android:configChanges="keyboardHidden|orientation" />
         <activity android:name=".activity.BluetoothDeviceList" android:configChanges="keyboardHidden|orientation" />
-        <activity android:name=".activity.ScriptingLayerServiceLauncher" android:taskAffinity="" android:theme="@android:style/Theme.DeviceDefault.NoActionBar.TranslucentDecor">
+        <activity android:name=".activity.ScriptingLayerServiceLauncher" android:taskAffinity="" android:theme="@android:style/Theme.DeviceDefault.NoActionBar.TranslucentDecor"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -192,7 +198,8 @@
         <service android:name=".service.ScriptingLayerService" />
         <service android:name=".service.TriggerService" />
         <service android:name="com.googlecode.android_scripting.facade.telephony.InCallServiceImpl"
-                 android:permission="android.permission.BIND_INCALL_SERVICE" >
+                 android:permission="android.permission.BIND_INCALL_SERVICE"
+                 android:exported="true">
             <intent-filter>
                 <action android:name="android.telecom.InCallService"/>
             </intent-filter>
@@ -204,14 +211,16 @@
                 <action android:name="com.googlecode.android_scripting.service.FacadeService.ACTION_BIND" />
             </intent-filter>
         </service>
-        <service android:name=".facade.bluetooth.media.BluetoothSL4AAudioSrcMBS">
+        <service android:name=".facade.bluetooth.media.BluetoothSL4AAudioSrcMBS"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.media.browse.MediaBrowserService"/>
             </intent-filter>
         </service>
         <activity android:name=".activity.InterpreterManager" android:launchMode="singleTask" android:configChanges="keyboardHidden|orientation" />
         <activity android:name=".activity.LogcatViewer" android:launchMode="singleTask" android:configChanges="keyboardHidden|orientation" />
-        <activity android:name=".activity.ScriptsLiveFolder" android:label="Scripts" android:icon="@drawable/live_folder" android:configChanges="keyboardHidden|orientation">
+        <activity android:name=".activity.ScriptsLiveFolder" android:label="Scripts" android:icon="@drawable/live_folder" android:configChanges="keyboardHidden|orientation"
+            android:exported="true">
             <intent-filter>
                 <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -233,7 +242,8 @@
         </provider>
         <uses-library android:name="android.test.runner" />
         <activity android:name=".activity.ScriptProcessMonitor" android:launchMode="singleTask" android:finishOnTaskLaunch="true" />
-        <activity android:configChanges="keyboardHidden|orientation" android:name="org.connectbot.util.ColorsActivity" android:theme="@android:style/Theme.DeviceDefault.Dialog">
+        <activity android:configChanges="keyboardHidden|orientation" android:name="org.connectbot.util.ColorsActivity" android:theme="@android:style/Theme.DeviceDefault.Dialog"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.googlecode.android_scripting.PICK_TERMINAL_COLORS" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/ScriptingLayerService.java b/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/ScriptingLayerService.java
index ac6880a..154c3e5 100644
--- a/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/ScriptingLayerService.java
+++ b/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/ScriptingLayerService.java
@@ -120,7 +120,9 @@
     protected Notification createNotification() {
         Intent notificationIntent = new Intent(this, ScriptingLayerService.class);
         notificationIntent.setAction(Constants.ACTION_SHOW_RUNNING_SCRIPTS);
-        mNotificationPendingIntent = PendingIntent.getService(this, 0, notificationIntent, 0);
+        mNotificationPendingIntent =
+                PendingIntent.getService(this, 0, notificationIntent,
+                        PendingIntent.FLAG_IMMUTABLE);
 
         createNotificationChannel();
         Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID);