Snap for 7478028 from 0ac9c049970a46d60b586a96425a686e7b4bae7f to mainline-adbd-release

Change-Id: If18bcfaae4d5aff70f35f54af3aee620d4a6d7c3
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..8563037
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2021 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 {
+    default_applicable_licenses: ["external_sl4a_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "external_sl4a_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-GPL-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
diff --git a/Common/Android.bp b/Common/Android.bp
index 6578c14..ad42d29 100644
--- a/Common/Android.bp
+++ b/Common/Android.bp
@@ -14,6 +14,15 @@
 //  limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_sl4a_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_sl4a_license"],
+}
+
 java_library {
     name: "sl4a.Common",
     owner: "google",
@@ -23,19 +32,25 @@
         "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",
         "ims-common",
+        "bouncycastle-repackaged-unbundled",
     ],
 
     srcs: [
         "src/com/googlecode/android_scripting/**/*.java",
         "src/org/apache/commons/codec/**/*.java",
+        ":cellbroadcast-shared-with-sl4a",
+        ":cellbroadcast-util-shared-srcs",
+        ":cellbroadcast-constants-shared-srcs",
     ],
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/AndroidFacade.java b/Common/src/com/googlecode/android_scripting/facade/AndroidFacade.java
index 87b50cd..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;
@@ -857,7 +869,8 @@
       @RpcParameter(name = "message") String message) {
     createNotificationChannel();
     // This contentIntent is a noop.
-    PendingIntent contentIntent = PendingIntent.getService(mService, 0, new Intent(), 0);
+    PendingIntent contentIntent = PendingIntent.getService(mService, 0, new Intent(),
+            PendingIntent.FLAG_IMMUTABLE);
     Notification.Builder builder = new Notification.Builder(mService, CHANNEL_ID);
     builder.setSmallIcon(mResources.getLogo48())
            .setTicker(message)
diff --git a/Common/src/com/googlecode/android_scripting/facade/CertInstallerHelper.java b/Common/src/com/googlecode/android_scripting/facade/CertInstallerHelper.java
index e4c36c1..d3d69c0 100644
--- a/Common/src/com/googlecode/android_scripting/facade/CertInstallerHelper.java
+++ b/Common/src/com/googlecode/android_scripting/facade/CertInstallerHelper.java
@@ -17,33 +17,31 @@
 package com.googlecode.android_scripting.facade;
 
 import android.os.Environment;
-import android.security.Credentials;
-import android.security.KeyStore;
 import android.util.Log;
 
 import com.android.internal.net.VpnProfile;
-import com.android.org.bouncycastle.asn1.ASN1InputStream;
-import com.android.org.bouncycastle.asn1.ASN1Sequence;
-import com.android.org.bouncycastle.asn1.DEROctetString;
-import com.android.org.bouncycastle.asn1.x509.BasicConstraints;
-
-import junit.framework.Assert;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.DEROctetString;
+import com.android.internal.org.bouncycastle.asn1.x509.BasicConstraints;
 
 import libcore.io.Streams;
 
+import junit.framework.Assert;
+
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
+import java.security.KeyStore;
 import java.security.KeyStore.PasswordProtection;
 import java.security.KeyStore.PrivateKeyEntry;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.UnrecoverableEntryException;
 import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -58,21 +56,27 @@
 public class CertInstallerHelper {
     private static final String TAG = "CertInstallerHelper";
     /* Define a password to unlock keystore after it is reset */
-    private static final String CERT_STORE_PASSWORD = "password";
-    private final int mUid = KeyStore.UID_SELF;
     private PrivateKey mUserKey;  // private key
     private X509Certificate mUserCert;  // user certificate
     private List<X509Certificate> mCaCerts = new ArrayList<X509Certificate>();
-    private KeyStore mKeyStore = KeyStore.getInstance();
+    private final KeyStore mKeyStore;
 
     /**
-     * Unlock keystore and set password
+     * Delete all key for caller.
      */
     public CertInstallerHelper() {
-        for (String key : mKeyStore.list("")) {
-            mKeyStore.delete(key, KeyStore.UID_SELF);
+        try {
+            mKeyStore = KeyStore.getInstance("AndroidKeyStore");
+            mKeyStore.load(null);
+            final Enumeration<String> aliases = mKeyStore.aliases();
+            while (aliases.hasMoreElements()) {
+                mKeyStore.deleteEntry(aliases.nextElement());
+            }
+        } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException
+                | IOException e) {
+            Log.e(TAG, "Failed to open and cleanup Keystore.", e);
+            throw new RuntimeException("Failed to open and cleanup Keystore.", e);
         }
-        mKeyStore.onUserPasswordChanged(CERT_STORE_PASSWORD);
     }
 
     private void extractCertificate(String certFile, String password) {
@@ -166,7 +170,7 @@
 
     /**
      * Extract certificate from the given file, and install it to keystore
-     * @param name certificate name
+     * @param profile VpnProfile
      * @param certFile .p12 file which includes certificates
      * @param password password to extract the .p12 file
      */
@@ -174,51 +178,39 @@
         // extract private keys, certificates from the provided file
         extractCertificate(certFile, password);
         // install certificate to the keystore
-        int flags = KeyStore.FLAG_ENCRYPTED;
         try {
+            boolean caInstalledWithUserKey = false;
+
             if (mUserKey != null) {
                 Log.v(TAG, "has private key");
-                String key = Credentials.USER_PRIVATE_KEY + profile.ipsecUserCert;
-                byte[] value = mUserKey.getEncoded();
-
-                if (!mKeyStore.importKey(key, value, mUid, flags)) {
-                    Log.e(TAG, "Failed to install " + key + " as user " + mUid);
-                    return;
+                if (mUserCert == null) {
+                    throw new AssertionError("Must have user cert if user key is installed.");
                 }
-                Log.v(TAG, "install " + key + " as user " + mUid + " is successful");
+                final List<Certificate> certChain = new ArrayList<Certificate>();
+                certChain.add(mUserCert);
+                if (profile.ipsecUserCert.equals(profile.ipsecCaCert)) {
+                    // If the CA certs should be installed under the same alias they have to be
+                    // added to the end of the certificate chain.
+                    certChain.addAll(mCaCerts);
+                    // Make a note that we have installed the CA cert chain along with the
+                    // user key and cert.
+                    caInstalledWithUserKey = true;
+                }
+                mKeyStore.setKeyEntry(profile.ipsecUserCert, mUserKey, null,
+                        certChain.toArray(new Certificate[0]));
+                Log.v(TAG, "install " + profile.ipsecUserCert + " is successful");
             }
 
-            if (mUserCert != null) {
-                String certName = Credentials.USER_CERTIFICATE + profile.ipsecUserCert;
-                byte[] certData = Credentials.convertToPem(mUserCert);
-
-                if (!mKeyStore.put(certName, certData, mUid, flags)) {
-                    Log.e(TAG, "Failed to install " + certName + " as user " + mUid);
-                    return;
+            if (!(mCaCerts.isEmpty() || caInstalledWithUserKey)) {
+                if (mCaCerts.size() != 1) {
+                    throw new AssertionError("Trusted certificate cannot be a cert chain.");
                 }
-                Log.v(TAG, "install " + certName + " as user" + mUid + " is successful.");
+                mKeyStore.setCertificateEntry(profile.ipsecCaCert, mCaCerts.get(0));
+                Log.v(TAG, " install " + profile.ipsecCaCert + " is successful");
             }
-
-            if (!mCaCerts.isEmpty()) {
-                String caListName = Credentials.CA_CERTIFICATE + profile.ipsecCaCert;
-                X509Certificate[] caCerts = mCaCerts.toArray(new X509Certificate[mCaCerts.size()]);
-                byte[] caListData = Credentials.convertToPem(caCerts);
-
-                if (!mKeyStore.put(caListName, caListData, mUid, flags)) {
-                    Log.e(TAG, "Failed to install " + caListName + " as user " + mUid);
-                    return;
-                }
-                Log.v(TAG, " install " + caListName + " as user " + mUid + " is successful");
-            }
-        } catch (CertificateEncodingException e) {
-            Log.e(TAG, "Exception while convert certificates to pem " + e);
+        } catch (KeyStoreException e) {
+            Log.e(TAG, "Exception trying to import certificates. " + e);
             throw new AssertionError(e);
-        } catch (IOException e) {
-            Log.e(TAG, "IOException while convert to pem: " + e);
         }
     }
-
-    public int getUid() {
-        return mUid;
-    }
 }
diff --git a/Common/src/com/googlecode/android_scripting/facade/ConnectivityConstants.java b/Common/src/com/googlecode/android_scripting/facade/ConnectivityConstants.java
index 8e5297b..867f624 100644
--- a/Common/src/com/googlecode/android_scripting/facade/ConnectivityConstants.java
+++ b/Common/src/com/googlecode/android_scripting/facade/ConnectivityConstants.java
@@ -66,6 +66,7 @@
         public static final String NETWORK_CALLBACK_EVENT = "networkCallbackEvent";
         public static final String MAX_MS_TO_LIVE = "maxMsToLive";
         public static final String RSSI = "rssi";
+        public static final String METERED = "metered";
         public static final String INTERFACE_NAME = "interfaceName";
         public static final String CREATE_TIMESTAMP = "creation_timestamp";
         public static final String CURRENT_TIMESTAMP = "current_timestamp";
@@ -78,9 +79,20 @@
     public static final String TetheringFailedCallback = "ConnectivityManagerOnTetheringFailed";
 
     /**
+     * Constants for Meteredness
+     */
+    public static final Integer NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25;
+
+    /**
      * Constants for PrivateDnsMode
      */
     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 5c12b7c..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,29 +136,7 @@
          */
         public JSONObject toJSON() throws JSONException {
             JSONObject json = super.toJSON();
-            json.put(ConnectivityConstants.NetworkCallbackContainer.RSSI,
-                    mNetworkCapabilities.getSignalStrength());
-            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 fb66b26..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;
@@ -32,18 +34,22 @@
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
-import android.net.StringNetworkSpecifier;
 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);
@@ -446,6 +481,24 @@
 
     private NetworkRequest buildNetworkRequestFromJson(JSONObject configJson)
             throws JSONException {
+        return makeNetworkRequestBuilderFromJson(configJson).build();
+    }
+
+    private NetworkRequest buildWifiAwareNetworkRequestFromJson(JSONObject configJson)
+            throws JSONException {
+        final NetworkRequest.Builder builder = makeNetworkRequestBuilderFromJson(configJson);
+        if (configJson.has("NetworkSpecifier")) {
+            final String strSpecifier = configJson.getString("NetworkSpecifier");
+            Log.d("build NetworkSpecifier" + strSpecifier);
+            final NetworkSpecifier specifier = WifiAwareManagerFacade.getNetworkSpecifier(
+                    new JSONObject(strSpecifier));
+            builder.setNetworkSpecifier(specifier);
+        }
+        return builder.build();
+    }
+
+    private NetworkRequest.Builder makeNetworkRequestBuilderFromJson(JSONObject configJson)
+            throws JSONException {
         NetworkRequest.Builder builder = new NetworkRequest.Builder();
 
         if (configJson.has("ClearCapabilities")) {
@@ -455,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));
@@ -482,20 +538,14 @@
             builder.setLinkDownstreamBandwidthKbps(configJson.getInt(
                     "LinkDownstreamBandwidthKbps"));
         }
-        if (configJson.has("NetworkSpecifier")) {
-            Log.d("build NetworkSpecifier" + configJson.getString("NetworkSpecifier"));
-            builder.setNetworkSpecifier(configJson.getString(
-                    "NetworkSpecifier"));
-        }
-        NetworkRequest networkRequest = builder.build();
-        return networkRequest;
+        return builder;
     }
 
     @Rpc(description = "register a network callback")
     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);
@@ -517,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);
@@ -528,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);
@@ -538,23 +588,42 @@
     @Rpc(description = "Request a Wi-Fi Aware network")
     public String connectivityRequestWifiAwareNetwork(@RpcParameter(name = "configJson")
             JSONObject configJson) throws JSONException {
-        NetworkRequest networkRequest = buildNetworkRequestFromJson(configJson);
-        if (networkRequest.networkCapabilities.getNetworkSpecifier() instanceof
-                StringNetworkSpecifier) {
-            String ns =
-                    ((StringNetworkSpecifier) networkRequest.networkCapabilities
-                            .getNetworkSpecifier()).specifier;
-            JSONObject j = new JSONObject(ns);
-            networkRequest.networkCapabilities.setNetworkSpecifier(
-                    WifiAwareManagerFacade.getNetworkSpecifier(j));
-        }
-        mNetworkCallback = new NetworkCallback(NetworkCallback.EVENT_ALL);
+        NetworkRequest networkRequest = buildWifiAwareNetworkRequestFromJson(configJson);
+        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) {
@@ -598,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();
@@ -1039,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/VpnFacade.java b/Common/src/com/googlecode/android_scripting/facade/VpnFacade.java
index 732cc60..bed8cc8 100644
--- a/Common/src/com/googlecode/android_scripting/facade/VpnFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/VpnFacade.java
@@ -16,50 +16,50 @@
 
 package com.googlecode.android_scripting.facade;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import org.json.JSONObject;
+import android.app.Service;
+import android.net.VpnManager;
+import android.os.RemoteException;
+import android.security.Credentials;
+import android.security.KeyStore;
+import android.security.LegacyVpnProfileStore;
 
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.internal.util.ArrayUtils;
+
 import com.google.android.collect.Lists;
 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
 import com.googlecode.android_scripting.rpc.Rpc;
 import com.googlecode.android_scripting.rpc.RpcParameter;
 
-import android.app.Service;
-import android.content.Context;
-import android.net.IConnectivityManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.security.Credentials;
-import android.security.KeyStore;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Access NFC functions.
+ * Access VPN functions.
  */
 public class VpnFacade extends RpcReceiver {
 
     private final Service mService;
-    private final IConnectivityManager mConService;
+    private final VpnManager mVpnManager;
     private CertInstallerHelper mCertHelper;
 
     public VpnFacade(FacadeManager manager) {
         super(manager);
         mService = manager.getService();
         mCertHelper = new CertInstallerHelper();
-        mConService = IConnectivityManager.Stub
-                .asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+        mVpnManager = mService.getSystemService(VpnManager.class);
     }
 
     static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) {
         final ArrayList<VpnProfile> result = Lists.newArrayList();
 
-        for (String key : keyStore.list(Credentials.VPN)) {
-            final VpnProfile profile = VpnProfile.decode(key, keyStore.get(Credentials.VPN + key));
+        for (String key : LegacyVpnProfileStore.list(Credentials.VPN)) {
+            final VpnProfile profile = VpnProfile.decode(key,
+                    LegacyVpnProfileStore.get(Credentials.VPN + key));
             if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) {
                 result.add(profile);
             }
@@ -92,17 +92,17 @@
     public void vpnStartLegacyVpn(@RpcParameter(name = "vpnProfile") JSONObject vpnProfile)
             throws RemoteException {
         VpnProfile profile = genLegacyVpnProfile(vpnProfile);
-        mConService.startLegacyVpn(profile);
+        mVpnManager.startLegacyVpn(profile);
     }
 
     @Rpc(description = "Stop the current legacy VPN connection.")
     public void vpnStopLegacyVpn() throws RemoteException {
-        mConService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, mService.getUserId());
+        mVpnManager.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN, mService.getUserId());
     }
 
     @Rpc(description = "Get the info object of the currently active legacy VPN connection.")
     public LegacyVpnInfo vpnGetLegacyVpnInfo() throws RemoteException {
-        return mConService.getLegacyVpnInfo(mService.getUserId());
+        return mVpnManager.getLegacyVpnInfo(mService.getUserId());
     }
 
     @Override
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHfpClientFacade.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHfpClientFacade.java
index 17e7b57..8c6f93f 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHfpClientFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothHfpClientFacade.java
@@ -28,6 +28,7 @@
 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.RpcDefault;
 import com.googlecode.android_scripting.rpc.RpcParameter;
 
 import java.util.ArrayList;
@@ -237,27 +238,32 @@
         return sHfpClientProfile.getAudioState(device);
     }
 
+    private BluetoothDevice getDevice(String deviceStr) {
+        if (sHfpClientProfile == null) return null;
+        BluetoothDevice device;
+        try {
+            device =  BluetoothFacade.getDevice(sHfpClientProfile.getConnectedDevices(), deviceStr);
+        } catch (Exception e) {
+            // Do nothing since it is disconnect and this function should force disconnect.
+            Log.e("bluetoothHfpClientConnect getDevice failed " + e);
+            return null;
+        }
+        return device;
+    }
+
     /**
-     * Start Voice Recognition on remote device
+     * Starts Voice Recognition on remote device
      *
      * @param deviceStr the Bluetooth MAC address of remote device
-     * @return True if command was sent
+     * @return True if command has been issued successfully
      */
     @Rpc(description = "Start Remote device Voice Recognition through HFP Client.")
     public boolean bluetoothHfpClientStartVoiceRecognition(
             @RpcParameter(name = "device",
                 description = "MAC address of a bluetooth device.")
                     String deviceStr) {
-        if (sHfpClientProfile == null) return false;
-        BluetoothDevice device;
-        try {
-            device = BluetoothFacade.getDevice(
-                    sHfpClientProfile.getConnectedDevices(), deviceStr);
-        } catch (Exception e) {
-            // Do nothing since it is disconnect and this function should force disconnect.
-            Log.e("bluetoothHfpClientConnect getDevice failed " + e);
-            return false;
-        }
+        BluetoothDevice device = getDevice(deviceStr);
+        if (device == null) return false;
         return sHfpClientProfile.startVoiceRecognition(device);
     }
 
@@ -265,49 +271,89 @@
      * Stops Voice Recognition in the remote device through Bluetooth HFP client
      *
      * @param deviceStr the Bluetooth MAC address of remote device
-     * @return True if command was sent
+     * @return True if command has been issued successfully
      */
     @Rpc(description = "Stops Remote device Voice Recognition through HFP Client.")
     public boolean bluetoothHfpClientStopVoiceRecognition(
             @RpcParameter(name = "device",
                 description = "MAC address of a bluetooth device.")
                     String deviceStr) {
-        if (sHfpClientProfile == null) return false;
-        BluetoothDevice device;
-        try {
-            device = BluetoothFacade.getDevice(
-                    sHfpClientProfile.getConnectedDevices(), deviceStr);
-        } catch (Exception e) {
-            // Do nothing since it is disconnect
-            // and this function should force disconnect.
-            Log.e("bluetoothHfpClientConnect getDevice failed " + e);
-            return false;
-        }
+        BluetoothDevice device = getDevice(deviceStr);
+        if (device == null) return false;
         return sHfpClientProfile.stopVoiceRecognition(device);
     }
 
     /**
+     * Initiates a connection of audio channel.
+     *
+     * It setup SCO channel with remote connected Handsfree Audio Gateway device.
+     *
+     * @param deviceStr the Bluetooth MAC address of remote device
+     * @return True if command has been issued successfully
+     */
+    @Rpc(description = "Initiates a connection of audio channel.")
+    public boolean bluetoothHfpClientConnectAudio(
+            @RpcParameter(name = "device",
+                description = "MAC address of a bluetooth device.")
+                String deviceStr) {
+        BluetoothDevice device = getDevice(deviceStr);
+        if (device == null) return false;
+        return sHfpClientProfile.connectAudio(device);
+    }
+
+    /**
+     * Dials a call in the remote device through Bluetooth HFP Client
+     *
+     * The last number dial will be placed if the number is an empty string
+     *
+     * @param deviceStr the Bluetooth MAC address of remote device
+     * @param number phone number to dial, the value of null or empty string for last number redial
+     * @return The string of <code>{@link BluetoothHeadsetClientCall} call</code> or null if no
+     *         device was not found.
+     */
+    @Rpc(description = "Dials a call in the remote device through HFP Client.")
+    public String bluetoothHfpClientDial(
+            @RpcParameter(name = "device",
+                description = "MAC address of a bluetooth device.") String deviceStr,
+            @RpcParameter(name = "phone_number", description =
+                "phone number to dial, default for last number redial")
+                @RpcDefault("") String number) {
+        BluetoothDevice device = getDevice(deviceStr);
+        if (device == null) return null;
+        return sHfpClientProfile.dial(device, number).toString(true);
+    }
+
+    /**
+     * Disconnects audio channel.
+     *
+     * It tears down the SCO channel from remote AuG device.
+     *
+     * @param deviceStr the Bluetooth MAC address of remote device
+     * @return True if command has been issued successfully
+     */
+    @Rpc(description = "Disconnects audio channel.")
+    public boolean bluetoothHfpClientDisconnectAudio(
+            @RpcParameter(name = "device",
+                description = "MAC address of a bluetooth device.")
+                String deviceStr) {
+        BluetoothDevice device = getDevice(deviceStr);
+        if (device == null) return false;
+        return sHfpClientProfile.disconnectAudio(device);
+    }
+
+    /**
      * Accepts a call in the remote device through Bluetooth HFP Client
      *
      * @param deviceStr the Bluetooth MAC address of remote device
-     * @return True if command was sent
+     * @return True if command has been issued successfully
      */
     @Rpc(description = "Accepts a call in the remote device through HFP Client.")
     public boolean bluetoothHfpClientAcceptCall(
             @RpcParameter(name = "device",
                 description = "MAC address of a bluetooth device.")
                     String deviceStr) {
-        if (sHfpClientProfile == null) return false;
-        BluetoothDevice device;
-        try {
-            device = BluetoothFacade.getDevice(
-                    sHfpClientProfile.getConnectedDevices(), deviceStr);
-        } catch (Exception e) {
-            // Do nothing since it is disconnect
-            // and this function should force disconnect.
-            Log.e("bluetoothHfpClientConnect getDevice failed " + e);
-            return false;
-        }
+        BluetoothDevice device = getDevice(deviceStr);
+        if (device == null) return false;
         return sHfpClientProfile.acceptCall(device, BluetoothHeadsetClient.CALL_ACCEPT_NONE);
     }
 
@@ -315,24 +361,15 @@
      * Terminates all calls in the remote device through Bluetooth HFP Client
      *
      * @param deviceStr the Bluetooth MAC address of remote device
-     * @return True if command was sent
+     * @return True if command has been issued successfully
      */
     @Rpc(description = "Terminates all calls in the remote device through HFP Client.")
     public boolean bluetoothHfpClientTerminateAllCalls(
             @RpcParameter(name = "device",
                 description = "MAC address of a bluetooth device.")
                     String deviceStr) {
-        if (sHfpClientProfile == null) return false;
-        BluetoothDevice device;
-        try {
-            device = BluetoothFacade.getDevice(
-                    sHfpClientProfile.getConnectedDevices(), deviceStr);
-        } catch (Exception e) {
-            // Do nothing since it is disconnect
-            // and this function should force disconnect.
-            Log.e("bluetoothHfpClientConnect getDevice failed " + e);
-            return false;
-        }
+        BluetoothDevice device = getDevice(deviceStr);
+        if (device == null) return false;
         return sHfpClientProfile.terminateCall(device, null);
     }
 
@@ -340,28 +377,19 @@
      * Rejects a call in the remote device through Bluetooth HFP Client
      *
      * @param deviceStr the Bluetooth MAC address of remote device
-     * @return True if command was sent
+     * @return True if command has been issued successfully
      */
     @Rpc(description = "Rejects a call in the remote device through HFP Client.")
     public boolean bluetoothHfpClientRejectCall(
             @RpcParameter(name = "device",
                 description = "MAC address of a bluetooth device.")
                     String deviceStr) {
-        if (sHfpClientProfile == null) return false;
-        BluetoothDevice device;
-        try {
-            device = BluetoothFacade.getDevice(
-                    sHfpClientProfile.getConnectedDevices(), deviceStr);
-        } catch (Exception e) {
-            // Do nothing since it is disconnect
-            // and this function should force disconnect.
-            Log.e("bluetoothHfpClientConnect getDevice failed " + e);
-            return false;
-        }
+        BluetoothDevice device = getDevice(deviceStr);
+        if (device == null) return false;
         return sHfpClientProfile.rejectCall(device);
     }
 
     @Override
     public void shutdown() {
     }
-}
+}
\ No newline at end of file
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothSocketConnFacade.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothSocketConnFacade.java
index c08fc96..18ca858 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothSocketConnFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/BluetoothSocketConnFacade.java
@@ -33,6 +33,7 @@
 
 import org.apache.commons.codec.binary.Base64Codec;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.util.HashMap;
@@ -555,9 +556,19 @@
         byte bufIndex = (byte) 0x00FF;
 
         try {
+
+            ByteArrayOutputStream tmp_array = new ByteArrayOutputStream();
             for (int i = 0; i < numBuffers; i++) {
                 // Read one buffer
-                byte[] readBuf = conn.readBinary(bufferSize);
+                while (tmp_array.size() < bufferSize) {
+                    byte[] tmp_read = conn.readBinary(bufferSize);
+                    if (tmp_read.length == 0) {
+                        break;
+                    }
+                    tmp_array.write(tmp_read);
+                }
+                byte[] readBuf = tmp_array.toByteArray();
+                tmp_array.reset();
 
                 // Make sure the contents are valid
                 int nextInChar = 'a';
@@ -587,6 +598,9 @@
                     nextInChar = getNextOutputChar(nextInChar);
                 }
                 Log.d("bluetoothConnectionThroughputRead: Buffer Read index=" + i);
+                if (bufferSize < readBuf.length) {
+                    tmp_array.write(readBuf, bufferSize, readBuf.length - bufferSize);
+                }
             }
 
             long endTesttime = System.currentTimeMillis();
diff --git a/Common/src/com/googlecode/android_scripting/facade/bluetooth/GattClientFacade.java b/Common/src/com/googlecode/android_scripting/facade/bluetooth/GattClientFacade.java
index f6edde8..e228672 100644
--- a/Common/src/com/googlecode/android_scripting/facade/bluetooth/GattClientFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/bluetooth/GattClientFacade.java
@@ -1740,7 +1740,7 @@
      * @param index the bluetooth gatt index
      * @param minConnectionInterval minimum connection interval
      * @param maxConnectionInterval maximum connection interval
-     * @param slaveLatency maximum slave latency
+     * @param peripheralLatency maximum peripheral latency
      * @param supervisionTimeout supervision timeout
      * @return boolean True if successful False otherwise.
      * @throws Exception
@@ -1750,7 +1750,7 @@
             @RpcParameter(name = "index") Integer index,
             @RpcParameter(name = "minConnectionInterval") Integer minConnectionInterval,
             @RpcParameter(name = "maxConnectionInterval") Integer maxConnectionInterval,
-            @RpcParameter(name = "slaveLatency") Integer slaveLatency,
+            @RpcParameter(name = "peripheralLatency") Integer peripheralLatency,
             @RpcParameter(name = "supervisionTimeout") Integer supervisionTimeout,
             @RpcParameter(name = "minConnectionEventLen") Integer minConnectionEventLen,
             @RpcParameter(name = "maxConnectionEventLen") Integer maxConnectionEventLen)
@@ -1758,7 +1758,7 @@
         boolean result = false;
         if (mBluetoothGattList.get(index) != null) {
             result = mBluetoothGattList.get(index).requestLeConnectionUpdate(
-                minConnectionInterval, maxConnectionInterval, slaveLatency, supervisionTimeout,
+                minConnectionInterval, maxConnectionInterval, peripheralLatency, supervisionTimeout,
                 minConnectionEventLen, maxConnectionEventLen);
         } else {
             throw new Exception("Invalid index input:" + index);
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/net/IpSecManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/net/IpSecManagerFacade.java
index d0a443f..a856a1b 100644
--- a/Common/src/com/googlecode/android_scripting/facade/net/IpSecManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/net/IpSecManagerFacade.java
@@ -18,6 +18,7 @@
 
 import android.app.Service;
 import android.content.Context;
+import android.net.InetAddresses;
 import android.net.IpSecAlgorithm;
 import android.net.IpSecManager;
 import android.net.IpSecManager.ResourceUnavailableException;
@@ -26,7 +27,6 @@
 import android.net.IpSecManager.UdpEncapsulationSocket;
 import android.net.IpSecTransform;
 import android.net.IpSecTransform.Builder;
-import android.net.NetworkUtils;
 
 import com.google.common.io.BaseEncoding;
 import com.googlecode.android_scripting.Log;
@@ -315,7 +315,7 @@
             String addr,
             String udpEncapSockId) {
         IpSecTransform transform = null;
-        InetAddress inetAddr = NetworkUtils.numericToInetAddress(addr);
+        InetAddress inetAddr = InetAddresses.parseNumericAddress(addr);
         UdpEncapsulationSocket udpEncapSocket = sUdpEncapHashMap.get(udpEncapSockId);
         SecurityParameterIndex spi = sSpiHashMap.get(spiId);
         if (spi == null) {
@@ -385,7 +385,7 @@
     public String ipSecAllocateSecurityParameterIndex(
             @RpcParameter(name = "addr") String addr,
             @RpcParameter(name = "requestedSpi") @RpcOptional Integer requestedSpi) {
-        InetAddress inetAddr = NetworkUtils.numericToInetAddress(addr);
+        InetAddress inetAddr = InetAddresses.parseNumericAddress(addr);
         SecurityParameterIndex spi = null;
         if (requestedSpi == null) {
             spi = allocateSpi(inetAddr);
diff --git a/Common/src/com/googlecode/android_scripting/facade/net/SocketFacade.java b/Common/src/com/googlecode/android_scripting/facade/net/SocketFacade.java
index b57f1fb..4bcd60c 100644
--- a/Common/src/com/googlecode/android_scripting/facade/net/SocketFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/net/SocketFacade.java
@@ -16,7 +16,7 @@
 
 package com.googlecode.android_scripting.facade.net;
 
-import android.net.NetworkUtils;
+import android.net.InetAddresses;
 import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -157,8 +157,8 @@
             String local,
             Integer localPort) {
         try {
-            InetAddress remoteAddr = NetworkUtils.numericToInetAddress(remote);
-            InetAddress localAddr = NetworkUtils.numericToInetAddress(local);
+            InetAddress remoteAddr = InetAddresses.parseNumericAddress(remote);
+            InetAddress localAddr = InetAddresses.parseNumericAddress(local);
             Socket socket = new Socket(remoteAddr, remotePort.intValue(), localAddr,
                     localPort.intValue());
             socket.setSoLinger(true, 0);
@@ -209,7 +209,7 @@
     @Rpc(description = "Open TCP server socket and accept connection")
     public String openTcpServerSocket(String addr, Integer port) {
         try {
-            InetAddress localAddr = NetworkUtils.numericToInetAddress(addr);
+            InetAddress localAddr = InetAddresses.parseNumericAddress(addr);
             ServerSocket serverSocket = new ServerSocket(port.intValue(), 10, localAddr);
             String id = getServerSocketId(serverSocket);
             sServerSocketHashMap.put(id, serverSocket);
@@ -402,13 +402,14 @@
 
     /**
      * Open datagram socket using java.net.DatagramSocket
+     *
      * @param addr : IP addr to use
      * @param port : port to open socket on
      * @return Hash key of {@link DatagramSocket}
      */
     @Rpc(description = "Open datagram socket")
     public String openDatagramSocket(String addr, Integer port) {
-        InetAddress localAddr = NetworkUtils.numericToInetAddress(addr);
+        InetAddress localAddr = InetAddresses.parseNumericAddress(addr);
         try {
             DatagramSocket socket = new DatagramSocket(port.intValue(), localAddr);
             socket.setSoTimeout(SOCK_TIMEOUT);
@@ -458,7 +459,7 @@
             Integer port) {
         byte[] buf = message.getBytes();
         try {
-            InetAddress remoteAddr = NetworkUtils.numericToInetAddress(addr);
+            InetAddress remoteAddr = InetAddresses.parseNumericAddress(addr);
             DatagramSocket socket = sDatagramSocketHashMap.get(datagramSocketId);
             DatagramPacket pkt = new DatagramPacket(buf, buf.length, remoteAddr, port.intValue());
             socket.send(pkt);
@@ -505,7 +506,7 @@
     public String openSocket(Integer domain, Integer type, String addr, Integer port) {
         try {
             FileDescriptor fd = Os.socket(domain, type, 0);
-            InetAddress localAddr = NetworkUtils.numericToInetAddress(addr);
+            InetAddress localAddr = InetAddresses.parseNumericAddress(addr);
             Os.bind(fd, localAddr, port.intValue());
             String id = getFileDescriptorId(fd);
             sFileDescriptorHashMap.put(id, fd);
@@ -545,7 +546,7 @@
     public Boolean sendDataOverSocket(
             String remoteAddr, Integer remotePort, String message, String id) {
         FileDescriptor fd = sFileDescriptorHashMap.get(id);
-        InetAddress remote = NetworkUtils.numericToInetAddress(remoteAddr);
+        InetAddress remote = InetAddresses.parseNumericAddress(remoteAddr);
         try {
             byte [] data = new String(message).getBytes(StandardCharsets.UTF_8);
             int bytes = Os.sendto(fd, data, 0, data.length, 0, remote, remotePort.intValue());
@@ -604,7 +605,7 @@
     public Boolean connectSocket(String id, String addr, Integer port) {
         FileDescriptor fd = sFileDescriptorHashMap.get(id);
         try {
-            InetAddress remoteAddr = NetworkUtils.numericToInetAddress(addr);
+            InetAddress remoteAddr = InetAddresses.parseNumericAddress(addr);
             Os.connect(fd, remoteAddr, port.intValue());
             return true;
         } catch (SocketException | ErrnoException e) {
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/TelephonyConstants.java b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyConstants.java
index ecbec0d..d5e9633 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyConstants.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyConstants.java
@@ -188,6 +188,7 @@
     public static final String RAT_TD_SCDMA = "TD_SCDMA";
     public static final String RAT_GLOBAL = "GLOBAL";
     public static final String RAT_LTE_CA = "LTE_CA";
+    public static final String RAT_NR = "NR";
     public static final String RAT_UNKNOWN = "UNKNOWN";
 
     /**
@@ -228,6 +229,15 @@
     public static final String DATA_STATE_UNKNOWN = "UNKNOWN";
 
     /**
+     * Constant for Override Network Type
+     * **/
+    public static final String OVERRIDE_NETWORK_TYPE_NONE = "NONE";
+    public static final String OVERRIDE_NETWORK_TYPE_LTE_CA = "LTE_CA";
+    public static final String OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO = "LTE_ADVANCED_PRO";
+    public static final String OVERRIDE_NETWORK_TYPE_NR_NSA = "NR_NSA";
+    public static final String OVERRIDE_NETWORK_TYPE_NR_ADVANCED = "NR_MMWAVE";
+
+    /**
      * Constant for Telephony Manager Call State
      * **/
     public static final String TELEPHONY_STATE_RINGING = "RINGING";
@@ -317,6 +327,17 @@
     public static final String NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = "NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA";
     public static final String NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = "NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA";
     public static final String NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = "NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA";
+    public static final String NETWORK_MODE_NR_LTE_GSM_WCDMA = "NETWORK_MODE_NR_LTE_GSM_WCDMA";
+    public static final String NETWORK_MODE_NR_ONLY = "NETWORK_MODE_NR_ONLY";
+    public static final String NETWORK_MODE_NR_LTE = "NETWORK_MODE_NR_LTE";
+    public static final String NETWORK_MODE_NR_LTE_CDMA_EVDO = "NETWORK_MODE_NR_LTE_CDMA_EVDO";
+    public static final String NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA = "NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA";
+    public static final String NETWORK_MODE_NR_LTE_WCDMA = "NETWORK_MODE_NR_LTE_WCDMA";
+    public static final String NETWORK_MODE_NR_LTE_TDSCDMA = "NETWORK_MODE_NR_LTE_TDSCDMA";
+    public static final String NETWORK_MODE_NR_LTE_TDSCDMA_GSM = "NETWORK_MODE_NR_LTE_TDSCDMA_GSM";
+    public static final String NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA = "NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA";
+    public static final String NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA = "NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA";
+    public static final String NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = "NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA";
     public static final String NETWORK_MODE_INVALID = "INVALID";
 
     /**
@@ -373,6 +394,7 @@
     public static final String EventPreciseStateChanged = "PreciseStateChanged";
     public static final String EventDataConnectionRealTimeInfoChanged = "DataConnectionRealTimeInfoChanged";
     public static final String EventDataConnectionStateChanged = "DataConnectionStateChanged";
+    public static final String EventDisplayInfoChanged = "DisplayInfoChanged";
     public static final String EventActiveDataSubIdChanged = "ActiveDataSubIdChanged";
     public static final String EventServiceStateChanged = "ServiceStateChanged";
     public static final String EventSignalStrengthChanged = "SignalStrengthChanged";
@@ -438,6 +460,13 @@
         public static final String DATA_CONNECTION_STATE = "dataConnectionState";
     }
 
+    public static class DisplayInfoContainer {
+        public static final String TIME = "time";
+        public static final String NETWORK = "network";
+        public static final String OVERRIDE = "override";
+        public static final String SUBSCRIPTION_ID = "subscriptionId";
+    }
+
     public static class ServiceStateContainer {
         public static final String VOICE_REG_STATE = "voiceRegState";
         public static final String VOICE_NETWORK_TYPE = "voiceNetworkType";
@@ -472,7 +501,7 @@
         public static final String IS_DATA_ROAMING_FROM_REGISTRATION =
                 "isDataRoamingFromRegistration";
         public static final String IS_USING_CARRIER_AGGREGATION = "isUsingCarrierAggregation";
-        public static final String LTE_EARFCN_RSRP_BOOST = "lteEarfcnRsrpBoost";
+        public static final String LTE_EARFCN_RSRP_BOOST = "LteEarfcnRsrpBoost";
     }
 
     public static class MessageWaitingIndicatorContainer {
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyEvents.java b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyEvents.java
index 7021475..2e394fa 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyEvents.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyEvents.java
@@ -20,6 +20,7 @@
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.ServiceState;
+import android.telephony.TelephonyDisplayInfo;
 
 import com.googlecode.android_scripting.jsonrpc.JsonSerializable;
 
@@ -267,6 +268,52 @@
         }
     }
 
+    public static class DisplayInfoChangedEvent implements JsonSerializable {
+        private TelephonyDisplayInfo mDisplayInfoString;
+        private int mSubscriptionId;
+        private String mOverrideDataNetworkType;
+        private String mDataNetworkType;
+
+        DisplayInfoChangedEvent(TelephonyDisplayInfo DisplayInfoString, int subscriptionId) {
+            mDisplayInfoString = DisplayInfoString;
+            mSubscriptionId = subscriptionId;
+            mOverrideDataNetworkType = TelephonyUtils.getDisplayInfoString(
+                    DisplayInfoString.getOverrideNetworkType());
+            mDataNetworkType = TelephonyUtils.getNetworkTypeString(
+                    DisplayInfoString.getNetworkType());
+        }
+
+        public String getOverrideDataNetworkType() {
+            return mOverrideDataNetworkType;
+        }
+
+        public int getSubscriptionId() {
+            return mSubscriptionId;
+        }
+
+        public String getDataNetworkType() {
+            return mDataNetworkType;
+        }
+
+        public JSONObject toJSON() throws JSONException {
+            JSONObject displayInfoState = new JSONObject();
+
+            displayInfoState.put(
+                    TelephonyConstants.DisplayInfoContainer.OVERRIDE,
+                    mOverrideDataNetworkType);
+
+            displayInfoState.put(
+                    TelephonyConstants.DisplayInfoContainer.NETWORK,
+                    mDataNetworkType);
+
+            displayInfoState.put(
+                    TelephonyConstants.DisplayInfoContainer.SUBSCRIPTION_ID,
+                    mSubscriptionId);
+
+            return displayInfoState;
+        }
+    }
+
     public static class PhysicalChannelConfigChangedEvent implements JsonSerializable {
         private final List<PhysicalChannelConfig> mConfigs;
 
@@ -284,7 +331,7 @@
                 JSONObject cfg  = new JSONObject();
                 cfg.put(
                         TelephonyConstants.PhysicalChannelConfigContainer.CELL_BANDWIDTH_DOWNLINK,
-                        c.getCellBandwidthDownlink());
+                        c.getCellBandwidthDownlinkKhz());
                 cfg.put(
                         TelephonyConstants.PhysicalChannelConfigContainer.CONNECTION_STATUS,
                         c.getConnectionStatus());
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 a338402..e60e5f1 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyManagerFacade.java
@@ -55,6 +55,8 @@
 import com.googlecode.android_scripting.facade.telephony.TelephonyStateListeners
                                                    .DataConnectionStateChangeListener;
 import com.googlecode.android_scripting.facade.telephony.TelephonyStateListeners
+                                                   .DisplayInfoStateChangeListener;
+import com.googlecode.android_scripting.facade.telephony.TelephonyStateListeners
                                                    .ServiceStateChangeListener;
 import com.googlecode.android_scripting.facade.telephony.TelephonyStateListeners
                                                    .SignalStrengthChangeListener;
@@ -303,6 +305,28 @@
         return true;
     }
 
+    @Rpc(description = "Starts tracking display info change" +
+                       "for default subscription ID.")
+    public Boolean telephonyStartTrackingDisplayInfoChange() {
+        return telephonyStartTrackingDisplayInfoChangeForSubscription(
+                              SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+    }
+
+    @Rpc(description = "Starts tracking display info change" +
+                       "for specified subscription ID.")
+    public Boolean telephonyStartTrackingDisplayInfoChangeForSubscription(
+                @RpcParameter(name = "subId") Integer subId) {
+        StateChangeListener listener = getStateChangeListenerForSubscription(subId, true);
+        if(listener == null) {
+            Log.e("Invalid subscription ID");
+            return false;
+        }
+        mTelephonyManager.createForSubscriptionId(subId).listen(
+            listener.mDisplayInfoStateChangeListener,
+            PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED);
+        return true;
+    }
+
     @Rpc(description = "Turn on/off precise listening on fore/background or" +
                        " ringing calls for default voice subscription ID.")
     public Boolean telephonyAdjustPreciseCallStateListenLevel(
@@ -381,6 +405,28 @@
         return true;
     }
 
+    @Rpc(description = "Stops tracking display info change " +
+                       "for default subscription ID.")
+    public Boolean telephonyStopTrackingDisplayInfoChange() {
+        return telephonyStopTrackingDisplayInfoChangeForSubscription(
+                SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+    }
+
+    @Rpc(description = "Stops tracking display info change " +
+                       "for specified subscription ID.")
+    public Boolean telephonyStopTrackingDisplayInfoChangeForSubscription(
+                   @RpcParameter(name = "subId") Integer subId) {
+        StateChangeListener listener = getStateChangeListenerForSubscription(subId, false);
+        if(listener == null) {
+            Log.e("Invalid subscription ID");
+            return false;
+        }
+        mTelephonyManager.createForSubscriptionId(subId).listen(
+            listener.mDisplayInfoStateChangeListener,
+            PhoneStateListener.LISTEN_NONE);
+        return true;
+    }
+
     @Rpc(description = "Stops tracking call state change " +
             "for default voice subscription ID.")
     public Boolean telephonyStopTrackingCallStateChange() {
@@ -756,7 +802,12 @@
     public boolean telephonySetAlwaysAllowMmsData(
             @RpcParameter(name = "subId") Integer subId,
             @RpcParameter(name = "alwaysAllow") Boolean alwaysAllow) {
-        return mTelephonyManager.createForSubscriptionId(subId).setAlwaysAllowMmsData(alwaysAllow);
+        boolean wasAlwaysAllow = mTelephonyManager.isMobileDataPolicyEnabled(
+                TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED);
+        mTelephonyManager.createForSubscriptionId(subId)
+                .setMobileDataPolicyEnabled(
+                        TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, alwaysAllow);
+        return wasAlwaysAllow == alwaysAllow;
     }
 
     /**
@@ -1506,6 +1557,7 @@
         public CellInfoChangeListener mCellInfoChangeListener;
         public DataConnectionStateChangeListener mDataConnectionStateChangeListener;
         public ActiveDataSubIdChangeListener mActiveDataSubIdChangeListener;
+        public DisplayInfoStateChangeListener mDisplayInfoStateChangeListener;
         public DataConnectionRealTimeInfoChangeListener mDataConnectionRTInfoChangeListener;
         public VoiceMailStateChangeListener mVoiceMailStateChangeListener;
 
@@ -1520,6 +1572,9 @@
             mActiveDataSubIdChangeListener =
                 new ActiveDataSubIdChangeListener(
                         mEventFacade, mTelephonyManager, subId, mService.getMainLooper());
+            mDisplayInfoStateChangeListener =
+                new DisplayInfoStateChangeListener(
+                        mEventFacade, mTelephonyManager, subId, mService.getMainLooper());
             mCallStateChangeListener =
                 new CallStateChangeListener(mEventFacade, subId, mService.getMainLooper());
             mCellInfoChangeListener =
@@ -1545,6 +1600,9 @@
                     mActiveDataSubIdChangeListener,
                     PhoneStateListener.LISTEN_NONE);
             mTelephonyManager.listen(
+                    mDisplayInfoStateChangeListener,
+                    PhoneStateListener.LISTEN_NONE);
+            mTelephonyManager.listen(
                     mCellInfoChangeListener,
                     PhoneStateListener.LISTEN_NONE);
             mTelephonyManager.listen(
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyStateListeners.java b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyStateListeners.java
index e06af17..b13b66c 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyStateListeners.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyStateListeners.java
@@ -24,6 +24,7 @@
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 
 import com.googlecode.android_scripting.facade.EventFacade;
@@ -217,6 +218,45 @@
         }
     }
 
+    public static class DisplayInfoStateChangeListener extends PhoneStateListener {
+
+        private final EventFacade mEventFacade;
+        private final TelephonyManager mTelephonyManager;
+        public static final int sListeningStates =
+                PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED;
+        public int subscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+        public DisplayInfoStateChangeListener(EventFacade ef, TelephonyManager tm) {
+            super();
+            mEventFacade = ef;
+            mTelephonyManager = tm;
+            subscriptionId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+        }
+
+        public DisplayInfoStateChangeListener(EventFacade ef, TelephonyManager tm, int subId) {
+            super();
+            mEventFacade = ef;
+            mTelephonyManager = tm;
+            subscriptionId = subId;
+        }
+
+        public DisplayInfoStateChangeListener(
+                EventFacade ef, TelephonyManager tm, int subId, Looper looper) {
+            super(looper);
+            mEventFacade = ef;
+            mTelephonyManager = tm;
+            subscriptionId = subId;
+        }
+
+        @Override
+        public void onDisplayInfoChanged(TelephonyDisplayInfo mTelephonyDisplayInfo) {
+            mEventFacade.postEvent(
+                TelephonyConstants.EventDisplayInfoChanged,
+                    new TelephonyEvents.DisplayInfoChangedEvent(
+                        mTelephonyDisplayInfo, subscriptionId));
+        }
+    }
+
     public static class ServiceStateChangeListener extends PhoneStateListener {
 
         private final EventFacade mEventFacade;
diff --git a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyUtils.java b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyUtils.java
index 3b0b436..7af51cb 100644
--- a/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyUtils.java
+++ b/Common/src/com/googlecode/android_scripting/facade/telephony/TelephonyUtils.java
@@ -15,16 +15,20 @@
  */
 
 package com.googlecode.android_scripting.facade.telephony;
-import com.android.ims.ImsConfig;
-import com.android.internal.telephony.RILConstants;
-import com.googlecode.android_scripting.Log;
+
 import android.telecom.TelecomManager;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.PreciseCallState;
 import android.telephony.ServiceState;
+import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
 import android.telephony.VoLteServiceState;
 
+import com.android.ims.ImsConfig;
+import com.android.internal.telephony.RILConstants;
+
+import com.googlecode.android_scripting.Log;
+
 /**
  * Telephony utility functions
  */
@@ -189,6 +193,23 @@
         return TelephonyConstants.DATA_STATE_UNKNOWN;
     }
 
+    public static String getDisplayInfoString(int state) {
+        switch (state) {
+            case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE:
+                return TelephonyConstants.OVERRIDE_NETWORK_TYPE_NONE;
+            case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA:
+                return TelephonyConstants.OVERRIDE_NETWORK_TYPE_LTE_CA;
+            case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO:
+                return TelephonyConstants.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO;
+            case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA:
+                return TelephonyConstants.OVERRIDE_NETWORK_TYPE_NR_NSA;
+            case TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED:
+                return TelephonyConstants.OVERRIDE_NETWORK_TYPE_NR_ADVANCED;
+        }
+        Log.d("getDisplayInfoStateString error. int: " + state);
+        return TelephonyConstants.OVERRIDE_NETWORK_TYPE_NONE;
+    }
+
     public static int getNetworkModeIntfromString(String networkMode) {
         switch (networkMode) {
             case TelephonyConstants.NETWORK_MODE_WCDMA_PREF:
@@ -237,6 +258,28 @@
                 return RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
             case TelephonyConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
                 return RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA:
+                return RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+            case TelephonyConstants.NETWORK_MODE_NR_ONLY:
+                return RILConstants.NETWORK_MODE_NR_ONLY;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE:
+                return RILConstants.NETWORK_MODE_NR_LTE;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO:
+                return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA:
+                return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE_WCDMA:
+                return RILConstants.NETWORK_MODE_NR_LTE_WCDMA;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA:
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM:
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA:
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA:
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+            case TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+                return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
         }
         Log.d("getNetworkModeIntfromString error. String: " + networkMode);
         return RILConstants.RIL_ERRNO_INVALID_RESPONSE;
@@ -290,6 +333,28 @@
                 return TelephonyConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
             case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
                 return TelephonyConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+            case RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+            case RILConstants.NETWORK_MODE_NR_ONLY:
+                return TelephonyConstants.NETWORK_MODE_NR_ONLY;
+            case RILConstants.NETWORK_MODE_NR_LTE:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE;
+            case RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+            case RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+            case RILConstants.NETWORK_MODE_NR_LTE_WCDMA:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE_WCDMA;
+            case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+            case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+            case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+            case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+            case RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
+                return TelephonyConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
         }
         Log.d("getNetworkModeStringfromInt error. Int: " + networkMode);
         return TelephonyConstants.NETWORK_MODE_INVALID;
@@ -335,6 +400,8 @@
                 return TelephonyConstants.RAT_IDEN;
             case TelephonyManager.NETWORK_TYPE_LTE_CA:
                 return TelephonyConstants.RAT_LTE_CA;
+            case TelephonyManager.NETWORK_TYPE_NR:
+                return TelephonyConstants.RAT_NR;
         }
         return TelephonyConstants.RAT_UNKNOWN;
     }
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 97b6b0e..dbbe99c 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 = "Return true if WiFi is enabled.")
@@ -1413,6 +1601,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)
      */
@@ -1431,6 +1630,16 @@
         makeLock(WifiManager.WIFI_MODE_SCAN_ONLY);
     }
 
+    @Rpc(description = "Acquires a high performance Wifi lock.")
+    public void wifiLockAcquireFullHighPerf() {
+        makeLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF);
+    }
+
+    @Rpc(description = "Acquires a low latency Wifi lock.")
+    public void wifiLockAcquireFullLowLatency() {
+        makeLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY);
+    }
+
     @Rpc(description = "Releases a previously acquired Wifi lock.")
     public void wifiLockRelease() {
         if (mLock != null) {
@@ -1454,6 +1663,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) {
@@ -1524,10 +1757,45 @@
             for (int j = 0; j < blockedList.length(); j++) {
                 blockedClientList.add(MacAddress.fromString(blockedList.getString(j)));
             }
-
         }
+
         configBuilder.setAllowedClientList(allowedClientList);
         configBuilder.setBlockedClientList(blockedClientList);
+
+        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();
     }
 
@@ -1684,6 +1952,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)
@@ -1699,91 +1977,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.
      *
@@ -1874,7 +2067,6 @@
 
     @Override
     public void shutdown() {
-        wifiReleaseNetworkAll();
         wifiLockRelease();
         if (mTrackingWifiStateChange == true) {
             wifiStopTrackingStateChange();
@@ -1974,6 +2166,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;
+        }
     }
 
     /**
@@ -2017,6 +2239,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
      *
      */
@@ -2043,4 +2283,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 a06adbc..24b44cd 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;
@@ -83,7 +86,6 @@
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.ModemActivityInfo;
-import android.telephony.ModemActivityInfo.TransmitPower;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -94,12 +96,14 @@
 import android.util.Base64;
 import android.util.DisplayMetrics;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import com.android.internal.net.LegacyVpnInfo;
 
 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;
@@ -124,6 +128,8 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 public class JsonBuilder {
 
@@ -238,6 +244,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);
         }
@@ -924,10 +933,52 @@
 
     private static Object buildNetwork(Network data) throws JSONException {
         JSONObject nw = new JSONObject();
-        nw.put("netId", data.netId);
+        nw.put("netId", data.getNetId());
         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();
@@ -1056,6 +1107,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();
@@ -1083,6 +1150,24 @@
         config.put("ClientControlByUserEnabled", data.isClientControlByUserEnabled());
         config.put("AllowedClientList", build(data.getAllowedClientList()));
         config.put("BlockedClientList", build(data.getBlockedClientList()));
+        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;
     }
 
@@ -1221,6 +1306,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;
     }
 
@@ -1230,6 +1338,9 @@
         Log.d("build softAp info.");
         info.put("frequency", data.getFrequency());
         info.put("bandwidth", data.getBandwidth());
+        info.put("wifiStandard", data.getWifiStandard());
+        info.put("autoShutdownTimeoutMillis", data.getAutoShutdownTimeoutMillis());
+        info.put("bssid", data.getBssid());
         return info;
     }
 
@@ -1410,16 +1521,14 @@
             throws JSONException {
         JSONObject info = new JSONObject();
 
-        info.put("Timestamp", modemInfo.getTimestamp());
+        info.put("Timestamp", modemInfo.getTimestampMillis());
         info.put("SleepTimeMs", modemInfo.getSleepTimeMillis());
         info.put("IdleTimeMs", modemInfo.getIdleTimeMillis());
-        // convert from int[] to List<Integer> for proper JSON translation
-        List<TransmitPower> txPowerIno = modemInfo.getTransmitPowerInfo();
-        List<Integer> tmp = new ArrayList<Integer>(txPowerIno.size());
-        for (TransmitPower val : txPowerIno) {
-            tmp.add(val.getTimeInMillis());
-        }
-        info.put("TxTimeMs", build(tmp));
+        List<Long> txPowerDurations = IntStream.range(0, 5)
+                .mapToLong(modemInfo::getTransmitDurationMillisAtPowerLevel)
+                .boxed()
+                .collect(Collectors.toList());
+        info.put("TxTimeMs", build(txPowerDurations));
         info.put("RxTimeMs", modemInfo.getReceiveTimeMillis());
         return info;
     }
@@ -1538,7 +1647,7 @@
         info.put(TelephonyConstants.ServiceStateContainer.IS_USING_CARRIER_AGGREGATION,
                 ss.isUsingCarrierAggregation());
         info.put(TelephonyConstants.ServiceStateContainer.LTE_EARFCN_RSRP_BOOST,
-                ss.getLteEarfcnRsrpBoost());
+                ss.getArfcnRsrpBoost());
         return info;
     }
 
diff --git a/Common/src/com/googlecode/android_scripting/rpc/MethodDescriptor.java b/Common/src/com/googlecode/android_scripting/rpc/MethodDescriptor.java
index f1a8634..1416574 100644
--- a/Common/src/com/googlecode/android_scripting/rpc/MethodDescriptor.java
+++ b/Common/src/com/googlecode/android_scripting/rpc/MethodDescriptor.java
@@ -26,10 +26,6 @@
 import com.googlecode.android_scripting.jsonrpc.RpcReceiverManager;
 import com.googlecode.android_scripting.util.VisibleForTesting;
 
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
@@ -41,6 +37,10 @@
 import java.util.List;
 import java.util.Map;
 
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
 /**
  * An adapter that wraps {@code Method}.
  */
diff --git a/InterpreterForAndroid/Android.bp b/InterpreterForAndroid/Android.bp
index 452b65d..1a963b8 100644
--- a/InterpreterForAndroid/Android.bp
+++ b/InterpreterForAndroid/Android.bp
@@ -14,6 +14,15 @@
 //  limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_sl4a_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_sl4a_license"],
+}
+
 java_library {
     name: "sl4a.InterpreterForAndroid",
     owner: "google",
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..6d8601b
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: RESTRICTED
+}
diff --git a/ScriptingLayer/Android.bp b/ScriptingLayer/Android.bp
index cf65c2e..c296755 100644
--- a/ScriptingLayer/Android.bp
+++ b/ScriptingLayer/Android.bp
@@ -14,6 +14,15 @@
 //  limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_sl4a_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_sl4a_license"],
+}
+
 java_library {
     name: "sl4a.ScriptingLayer",
     owner: "google",
diff --git a/ScriptingLayerForAndroid/Android.bp b/ScriptingLayerForAndroid/Android.bp
index bfe27c9..5c5972b 100644
--- a/ScriptingLayerForAndroid/Android.bp
+++ b/ScriptingLayerForAndroid/Android.bp
@@ -14,6 +14,16 @@
 //  limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_sl4a_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-GPL-2.0
+    default_applicable_licenses: ["external_sl4a_license"],
+}
+
 android_test_helper_app {
     name: "sl4a",
 
@@ -56,6 +66,9 @@
     },
 
     jni_libs: ["libcom_googlecode_android_scripting_Exec"],
+
+    min_sdk_version: "30",
+    target_sdk_version: "30",
 }
 
 java_import {
diff --git a/ScriptingLayerForAndroid/AndroidManifest.xml b/ScriptingLayerForAndroid/AndroidManifest.xml
index 11db787..441342c 100644
--- a/ScriptingLayerForAndroid/AndroidManifest.xml
+++ b/ScriptingLayerForAndroid/AndroidManifest.xml
@@ -117,14 +117,14 @@
     <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
     <uses-permission android:name="com.android.certinstaller.INSTALL_AS_USER" />
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
-    <uses-permission android:name="com.android.permission.WHITELIST_BLUETOOTH_DEVICE" />
+    <uses-permission android:name="com.android.permission.ALLOWLIST_BLUETOOTH_DEVICE" />
     <application
         android:icon="@drawable/sl4a_logo_48"
         android:label="@string/application_title"
         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" />
@@ -134,7 +134,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" />
@@ -145,31 +145,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>
@@ -178,7 +183,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" />
@@ -190,7 +196,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>
@@ -202,14 +209,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" />
@@ -231,7 +240,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/jni/Android.bp b/ScriptingLayerForAndroid/jni/Android.bp
index cb5e991..d7f6d7a 100644
--- a/ScriptingLayerForAndroid/jni/Android.bp
+++ b/ScriptingLayerForAndroid/jni/Android.bp
@@ -14,6 +14,15 @@
 //  limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_sl4a_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_sl4a_license"],
+}
+
 cc_library_shared {
     name: "libcom_googlecode_android_scripting_Exec",
 
@@ -25,6 +34,8 @@
         "-Wno-unused-parameter",
     ],
 
+    header_libs: ["jni_headers"],
+
     shared_libs: ["liblog"],
 
     sdk_version: "23",
diff --git a/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/ScriptingLayerService.java b/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/ScriptingLayerService.java
index eab396d..9d5b4cf 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);
diff --git a/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/TriggerService.java b/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/TriggerService.java
index b9115df..bf6f235 100644
--- a/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/TriggerService.java
+++ b/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/service/TriggerService.java
@@ -127,7 +127,8 @@
            .setWhen(System.currentTimeMillis())
            .setContentTitle("SL4A Trigger Service")
            .setContentText("Tap to view triggers")
-           .setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent, 0));
+           .setContentIntent(PendingIntent.getActivity(this, 0, notificationIntent,
+                   PendingIntent.FLAG_IMMUTABLE));
     Notification notification = builder.build();
     notification.flags = Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
     return notification;
diff --git a/Utils/Android.bp b/Utils/Android.bp
index d48d317..51b1325 100644
--- a/Utils/Android.bp
+++ b/Utils/Android.bp
@@ -14,6 +14,15 @@
 //  limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_sl4a_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_sl4a_license"],
+}
+
 java_library {
     name: "sl4a.Utils",
     owner: "google",
diff --git a/Utils/src/com/googlecode/android_scripting/Log.java b/Utils/src/com/googlecode/android_scripting/Log.java
index b3ad6c3..775d67e 100644
--- a/Utils/src/com/googlecode/android_scripting/Log.java
+++ b/Utils/src/com/googlecode/android_scripting/Log.java
@@ -74,7 +74,8 @@
            .setWhen(0)
            .setContentTitle(contentTitle)
            .setContentText(message)
-           .setContentIntent(PendingIntent.getService(context, 0, null, 0));
+           .setContentIntent(PendingIntent.getService(context, 0, null,
+                   PendingIntent.FLAG_IMMUTABLE));
     Notification note = builder.build();
     note.contentView.getLayoutId();
     notificationManager.notify(NotificationIdFactory.create(), note);